diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index 9df4c932c68..321f92ddba4 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -47,6 +47,9 @@ rsync html_/ html/ -a --copy-links # Build the release image without build artifacts. docker_build --target sagemath --tag "$DOCKER_IMAGE_CLI" . +# Tag the sagemath:$DOCKER_TAG image that CI has just built as +# sagemath:$COMMIT_HASH so we can refer to it uniquely later. +docker tag "$DOCKER_IMAGE_CLI" "$DOCKER_IMAGE_BINDER" # Display the layers of this image docker history "$DOCKER_IMAGE_CLI" # Build the developer image with the build artifacts intact. diff --git a/.ci/push-dockerhub.sh b/.ci/push-dockerhub.sh index a685ae8dd6e..5f0b5b5eab5 100755 --- a/.ci/push-dockerhub.sh +++ b/.ci/push-dockerhub.sh @@ -26,4 +26,8 @@ if [ -z "$DOCKER_USER" -o -z "$SECRET_DOCKER_PASS" ]; then else cat "$SECRET_DOCKER_PASS" | docker login -u $DOCKER_USER --password-stdin docker push ${DOCKER_NAMESPACE:-sagemath}/$1:$DOCKER_TAG + + # For historical reasons, we also provide a -py3 tag. It's identical to the non-py3 tag. + docker tag ${DOCKER_NAMESPACE:-sagemath}/$1:$DOCKER_TAG ${DOCKER_NAMESPACE:-sagemath}/$1:$DOCKER_TAG-py3 + docker push ${DOCKER_NAMESPACE:-sagemath}/$1:$DOCKER_TAG-py3 fi diff --git a/.ci/update-env.sh b/.ci/update-env.sh index c82345de348..fd1900b1a04 100755 --- a/.ci/update-env.sh +++ b/.ci/update-env.sh @@ -45,6 +45,8 @@ fi export DOCKER_IMAGE_CLI=${DOCKER_NAMESPACE:-sagemath}/sagemath:$DOCKER_TAG export DOCKER_IMAGE_DEV=${DOCKER_NAMESPACE:-sagemath}/sagemath-dev:$DOCKER_TAG +export DOCKER_IMAGE_BINDER="${DOCKER_NAMESPACE:-sagemath}/sagemath:${CI_COMMIT_SHA}" + # Seed the build cache with this image (set to source-clean to build from # scratch.) export ARTIFACT_BASE=${ARTIFACT_BASE:-$DEFAULT_ARTIFACT_BASE} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..fb91e2c6ec0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Force LF normalization +* text=auto eol=lf +# except for Windows batch files +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf \ No newline at end of file diff --git a/.github/workflows/ci-cygwin-minimal.yml b/.github/workflows/ci-cygwin-minimal.yml new file mode 100644 index 00000000000..21b798552a0 --- /dev/null +++ b/.github/workflows/ci-cygwin-minimal.yml @@ -0,0 +1,950 @@ +name: CI cygwin-minimal + +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,!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: + +############################################## stage-i ########################################## + + cygwin-stage-i-a: + env: + STAGE: i-a + # builds openblas + TARGETS: iml gsl + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-i-b: + env: + STAGE: i-b + TARGETS: cython setuptools_scm kiwisolver dateutil cycler pyparsing nose certifi pkgconfig pplpy + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-ii ########################################## + + cygwin-stage-ii-a: + env: + STAGE: ii-a + PREVIOUS_STAGES: i-* + TARGETS: cvxopt rpy2 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] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-b: + env: + STAGE: ii-b + PREVIOUS_STAGES: i-* + TARGETS: singular maxima gap pari gfan palp flintqs ratpoints arb ecm givaro + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-c: + env: + STAGE: ii-c + PREVIOUS_STAGES: i-* + TARGETS: cypari eclib fplll linbox giac + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-d: + env: + STAGE: ii-d + PREVIOUS_STAGES: i-* + TARGETS: ipython ipywidgets notebook thebe + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-e: + env: + STAGE: ii-e + PREVIOUS_STAGES: i-* + TARGETS: threejs tachyon pillow jmol m4rie sage_brial sympy lrcalc lcalc symmetrica cliquer libbraiding planarity rw elliptic_curves combinatorial_designs zn_poly sympow + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-iii ########################################## + + cygwin-stage-iii: + 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 }} + + needs: [cygwin-stage-ii-a, cygwin-stage-ii-b, cygwin-stage-ii-c, cygwin-stage-ii-d, cygwin-stage-ii-e] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-iv ########################################## + + cygwin-stage-iv-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] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-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] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-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] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-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] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() diff --git a/.github/workflows/ci-cygwin-standard.yml b/.github/workflows/ci-cygwin-standard.yml new file mode 100644 index 00000000000..02ae9cdf41a --- /dev/null +++ b/.github/workflows/ci-cygwin-standard.yml @@ -0,0 +1,950 @@ +name: CI cygwin-standard + +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,!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: + +############################################## stage-i ########################################## + + cygwin-stage-i-a: + env: + STAGE: i-a + # builds openblas + TARGETS: iml gsl + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [standard] + 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-i-b: + env: + STAGE: i-b + TARGETS: cython setuptools_scm kiwisolver dateutil cycler pyparsing nose certifi pkgconfig pplpy + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [standard] + 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-ii ########################################## + + cygwin-stage-ii-a: + env: + STAGE: ii-a + PREVIOUS_STAGES: i-* + TARGETS: cvxopt rpy2 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: [standard] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-b: + env: + STAGE: ii-b + PREVIOUS_STAGES: i-* + TARGETS: singular maxima gap pari gfan palp flintqs ratpoints arb ecm givaro + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [standard] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-c: + env: + STAGE: ii-c + PREVIOUS_STAGES: i-* + TARGETS: cypari eclib fplll linbox giac + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [standard] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-d: + env: + STAGE: ii-d + PREVIOUS_STAGES: i-* + TARGETS: ipython ipywidgets notebook thebe + 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: [standard] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-e: + env: + STAGE: ii-e + PREVIOUS_STAGES: i-* + TARGETS: threejs tachyon pillow jmol m4rie sage_brial sympy lrcalc lcalc symmetrica cliquer libbraiding planarity rw elliptic_curves combinatorial_designs zn_poly sympow + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [standard] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-iii ########################################## + + cygwin-stage-iii: + 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 }} + + needs: [cygwin-stage-ii-a, cygwin-stage-ii-b, cygwin-stage-ii-c, cygwin-stage-ii-d, cygwin-stage-ii-e] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [standard] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-iv ########################################## + + cygwin-stage-iv-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: [standard] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-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: [standard] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-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: [standard] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-${{ env.PREVIOUS_STAGES }}.tar' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-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: [standard] + 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 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh' + - 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 + 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 /tmp/sage-local-${{ env.STAGE }}.tar --remove-files local' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() diff --git a/.github/workflows/extract-sage-local.sh b/.github/workflows/extract-sage-local.sh new file mode 100755 index 00000000000..5c4be3afb1c --- /dev/null +++ b/.github/workflows/extract-sage-local.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# to be run from $SAGE_ROOT, with arguments sage-local-${{ env.PREVIOUS_STAGES }}.tar + +# Show all tar files +ls -l $* + +# We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. +for a in $*; do + echo Extracting $a + tar xf $a + rm -f $a +done + +# Also get rid of the stages that were not extracted +rm -f sage-local-*.tar + +# We set the installation records to the same mtime so that no rebuilds due to dependencies +# among these packages are triggered. +(cd local/var/lib/sage/installed/ && touch .dummy && touch --reference=.dummy *) + +# Show what has been built already. +ls -l local local/var/lib/sage/installed/ +df -h + +# Rebase! +src/bin/sage-rebase.sh local diff --git a/.github/workflows/scan-logs.sh b/.github/workflows/scan-logs.sh new file mode 100755 index 00000000000..9cab637e850 --- /dev/null +++ b/.github/workflows/scan-logs.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# 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 +LOGS=${1-logs} +for a in $(find "$LOGS" -type f -name "*.log"); do + if tail -100 "$a" 2>/dev/null | grep "^[A-Za-z]*Error" >/dev/null; then + echo :":"error file=$a:":" ==== ERROR IN LOG FILE $a ==== + cat "$a" + elif tail -20 "$a" 2>/dev/null | grep -E "^(Warning: Error testing|^sage .*doctest.*failed)" >/dev/null; then + echo :":"warning file=$a:":" ==== TESTSUITE FAILURE IN LOG FILE $a ==== + cat "$a" + fi +done diff --git a/.github/workflows/tox-experimental.yml b/.github/workflows/tox-experimental.yml new file mode 100644 index 00000000000..b9f09e7c03c --- /dev/null +++ b/.github/workflows/tox-experimental.yml @@ -0,0 +1,144 @@ +name: Test experimental packages with tox + +## 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 see below + +jobs: + docker: + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 6 + 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, gentoo, archlinux-latest, slackware-14.2, conda-forge, ubuntu-bionic-i386, ubuntu-eoan-i386, debian-buster-i386, centos-7-i386] + tox_packages_factor: [maximal] + targets_pattern: [0-g, h-o, p, q-z] + 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: configured with-targets with-targets-optional + TARGETS_OPTIONAL: "$( echo $(PATH=build/bin:$PATH build/bin/sage-package list :experimental: | grep -v database_stein_watkins\\$ | grep -v polytopes_db_4d | grep '^[${{ matrix.targets_pattern }}]' ) )" + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 500 + - name: fetch tags + run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - 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 docker image or 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) /bin/bash || true); fi + if [ -n "$CONTAINERS" ]; then for CONTAINER in $CONTAINERS; do for ARTIFACT in /sage/logs; do docker cp $CONTAINER:$ARTIFACT artifacts/$LOGS_ARTIFACT_NAME && HAVE_LOG=1; done; if [ -n "$HAVE_LOG" ]; then break; fi; done; fi + if: always() + - uses: actions/upload-artifact@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() + - name: Push docker images + run: | + if [ -f .tox/$TOX_ENV/Dockertags ]; then + TOKEN="${{ secrets.DOCKER_PKG_GITHUB_TOKEN }}" + if [ -z "$TOKEN" ]; then + TOKEN="${{ secrets.GITHUB_TOKEN }}" + fi + echo "$TOKEN" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin + for a in $(cat .tox/$TOX_ENV/Dockertags); do + FULL_TAG=docker.pkg.github.com/${{ github.repository }}/$a + docker tag $a $FULL_TAG + echo Pushing $FULL_TAG + docker push $FULL_TAG + done || echo "(Ignoring errors)" + fi + if: always() + + local-macos: + + runs-on: macos-latest + strategy: + fail-fast: false + max-parallel: 3 + matrix: + tox_system_factor: [homebrew-macos, homebrew-macos-python3_xcode, homebrew-macos-python3_xcode-nokegonly, homebrew-macos-python3_pythonorg, conda-forge-macos] + tox_packages_factor: [maximal] + targets_pattern: [0-g, h-o, p, q-z] + 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 }} + TARGETS_OPTIONAL: "$( echo $(PATH=build/bin:$PATH build/bin/sage-package list :experimental: | grep -v database_stein_watkins\\$ | grep -v polytopes_db_4d | grep '^[${{ matrix.targets_pattern }}]' ) )" + 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-gcc_spkg.yml b/.github/workflows/tox-gcc_spkg.yml new file mode 100644 index 00000000000..53efb6a7a6f --- /dev/null +++ b/.github/workflows/tox-gcc_spkg.yml @@ -0,0 +1,134 @@ +name: Test Linux --without-system-gcc + +## 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: sagelib-build-deps + TARGETS: build doc-html + TARGETS_OPTIONAL: ptest + +jobs: + docker: + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 5 + matrix: + tox_system_factor: [ubuntu-trusty, ubuntu-focal, fedora-27, fedora-31, debian-buster-i386] + tox_packages_factor: [minimal-gcc_spkg, standard-gcc_spkg] + 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: configured with-targets with-targets-optional + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 500 + - name: fetch tags + run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - 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 docker image or 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) /bin/bash || true); fi + if [ -n "$CONTAINERS" ]; then for CONTAINER in $CONTAINERS; do for ARTIFACT in /sage/logs; do docker cp $CONTAINER:$ARTIFACT artifacts/$LOGS_ARTIFACT_NAME && HAVE_LOG=1; done; if [ -n "$HAVE_LOG" ]; then break; fi; done; fi + if: always() + - uses: actions/upload-artifact@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() + - name: Push docker images + run: | + if [ -f .tox/$TOX_ENV/Dockertags ]; then + TOKEN="${{ secrets.DOCKER_PKG_GITHUB_TOKEN }}" + if [ -z "$TOKEN" ]; then + TOKEN="${{ secrets.GITHUB_TOKEN }}" + fi + echo "$TOKEN" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin + for a in $(cat .tox/$TOX_ENV/Dockertags); do + FULL_TAG=docker.pkg.github.com/${{ github.repository }}/$a + docker tag $a $FULL_TAG + echo Pushing $FULL_TAG + docker push $FULL_TAG + done || echo "(Ignoring errors)" + fi + if: always() + + local-ubuntu: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 1 + matrix: + tox_system_factor: [conda-forge-ubuntu] + tox_packages_factor: [minimal-gcc_spkg, standard-gcc_spkg] + 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: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install python-tox + - name: Build and test with tox + # We use a high parallelization on purpose in order to catch possible parallelization bugs in the build scripts. + # For doctesting, we use a lower parallelization to avoid timeouts. + 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-optional.yml b/.github/workflows/tox-optional.yml new file mode 100644 index 00000000000..625c6e17a11 --- /dev/null +++ b/.github/workflows/tox-optional.yml @@ -0,0 +1,148 @@ +name: Test optional packages with tox + +## 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 see below + +jobs: + docker: + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 6 + 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, gentoo, archlinux-latest, slackware-14.2, conda-forge, ubuntu-bionic-i386, ubuntu-eoan-i386, debian-buster-i386, centos-7-i386] + tox_packages_factor: [maximal] + targets_pattern: [0-g, h-o, p, q-z] + 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: configured with-targets with-targets-optional + # Test all optional packages, but do not test huge packages, + # and do not test packages that require external software + TARGETS_OPTIONAL: "$( echo $(PATH=build/bin:$PATH build/bin/sage-package list :optional: | grep -v database_stein_watkins\\$ | grep -v polytopes_db_4d | grep -v cplex | grep -v gurobi | grep '^[${{ matrix.targets_pattern }}]' ) )" + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 500 + - name: fetch tags + run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - 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 docker image or 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) /bin/bash || true); fi + if [ -n "$CONTAINERS" ]; then for CONTAINER in $CONTAINERS; do for ARTIFACT in /sage/logs; do docker cp $CONTAINER:$ARTIFACT artifacts/$LOGS_ARTIFACT_NAME && HAVE_LOG=1; done; if [ -n "$HAVE_LOG" ]; then break; fi; done; fi + if: always() + - uses: actions/upload-artifact@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() + - name: Push docker images + run: | + if [ -f .tox/$TOX_ENV/Dockertags ]; then + TOKEN="${{ secrets.DOCKER_PKG_GITHUB_TOKEN }}" + if [ -z "$TOKEN" ]; then + TOKEN="${{ secrets.GITHUB_TOKEN }}" + fi + echo "$TOKEN" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin + for a in $(cat .tox/$TOX_ENV/Dockertags); do + FULL_TAG=docker.pkg.github.com/${{ github.repository }}/$a + docker tag $a $FULL_TAG + echo Pushing $FULL_TAG + docker push $FULL_TAG + done || echo "(Ignoring errors)" + fi + if: always() + + local-macos: + + runs-on: macos-latest + strategy: + fail-fast: false + max-parallel: 3 + matrix: + tox_system_factor: [homebrew-macos, homebrew-macos-python3_xcode, homebrew-macos-python3_xcode-nokegonly, homebrew-macos-python3_pythonorg, conda-forge-macos] + tox_packages_factor: [maximal] + targets_pattern: [0-g, h-o, p, q-z] + 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 }} + # Test all optional packages, but do not test huge packages + # and do not test packages that require external software + TARGETS_OPTIONAL: "$( echo $(PATH=build/bin:$PATH build/bin/sage-package list :optional: | grep -v database_stein_watkins\\$ | grep -v polytopes_db_4d | grep -v cplex | grep -v gurobi | grep '^[${{ matrix.targets_pattern }}]' ) )" + 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 new file mode 100644 index 00000000000..d6f46bd7b16 --- /dev/null +++ b/.github/workflows/tox.yml @@ -0,0 +1,266 @@ +name: Run SAGE_ROOT/tox.ini + +## 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: sagelib-build-deps + TARGETS: build doc-html + TARGETS_OPTIONAL: ptest + +jobs: + docker: + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 20 + 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, fedora-32, centos-7, centos-8, gentoo, archlinux-latest, slackware-14.2, conda-forge, ubuntu-bionic-i386, ubuntu-eoan-i386, debian-buster-i386, centos-7-i386] + tox_packages_factor: [minimal, standard] + 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: configured with-targets with-targets-optional + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 500 + - name: fetch tags + run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - 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 docker image or 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) /bin/bash || true); fi + if [ -n "$CONTAINERS" ]; then for CONTAINER in $CONTAINERS; do for ARTIFACT in /sage/logs; do docker cp $CONTAINER:$ARTIFACT artifacts/$LOGS_ARTIFACT_NAME && HAVE_LOG=1; done; if [ -n "$HAVE_LOG" ]; then break; fi; done; fi + if: always() + - uses: actions/upload-artifact@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() + - name: Push docker images + run: | + if [ -f .tox/$TOX_ENV/Dockertags ]; then + TOKEN="${{ secrets.DOCKER_PKG_GITHUB_TOKEN }}" + if [ -z "$TOKEN" ]; then + TOKEN="${{ secrets.GITHUB_TOKEN }}" + fi + echo "$TOKEN" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin + for a in $(cat .tox/$TOX_ENV/Dockertags); do + FULL_TAG=docker.pkg.github.com/${{ github.repository }}/$a + docker tag $a $FULL_TAG + echo Pushing $FULL_TAG + docker push $FULL_TAG + done || echo "(Ignoring errors)" + fi + if: always() + + local-macos: + + runs-on: macos-latest + strategy: + fail-fast: false + max-parallel: 4 + matrix: + tox_system_factor: [homebrew-macos, homebrew-macos-python3_xcode, homebrew-macos-python3_xcode-nokegonly, homebrew-macos-python3_pythonorg, homebrew-macos-python3_xcode-gcc_spkg, conda-forge-macos] + tox_packages_factor: [minimal, standard] + 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() + + local-ubuntu: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 1 + matrix: + tox_system_factor: [conda-forge-ubuntu] + tox_packages_factor: [minimal, standard] + 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: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install python-tox + - name: Build and test with tox + # We use a high parallelization on purpose in order to catch possible parallelization bugs in the build scripts. + # For doctesting, we use a lower parallelization to avoid timeouts. + 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() + + dist: + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 500 + - name: fetch tags + run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - name: Install bootstrap prerequisites + run: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install $(sed "s/#.*//;" build/pkgs/debian-bootstrap.txt) + - name: Bootstrap with sage-update-version + # We set SAGE_ROOT and SAGE_SRC by hand + # because 'sage -sh' does not work with an unconfigured tree, + # giving: Error: SAGE_SCRIPTS_DIR is set to a bad value + run: | + git config --global user.email "nobody@example.com" + git config --global user.name "Sage GitHub CI" + SAGE_ROOT=. SAGE_SRC=./src src/bin/sage-update-version $(git describe) + - name: make dist + run: | + ./configure && make dist + - uses: actions/upload-artifact@v2 + with: + path: "dist/*.tar.gz" + name: dist + + local-macos-nohomebrew: + + needs: [dist] + + runs-on: macos-latest + strategy: + fail-fast: false + max-parallel: 4 + matrix: + tox_system_factor: [macos-nobootstrap, macos-nobootstrap-python3_pythonorg] + tox_packages_factor: [minimal] + 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 + if: "!contains(matrix.tox_system_factor, 'nobootstrap')" + - uses: actions/download-artifact@v2 + with: + path: . + name: dist + if: contains(matrix.tox_system_factor, 'nobootstrap') + - name: Unpack sage dist + run: | + tar xf sage*.tar.gz --strip-components=1 + if: contains(matrix.tox_system_factor, 'nobootstrap') + - name: Move homebrew away + run: | + (cd /usr/local && for a in bin etc include lib opt sbin share; do mv $a $a-moved; done) + - name: Install test prerequisites + run: | + /usr/bin/python3 -m pip install --user 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/update-cygwin-yml.sh b/.github/workflows/update-cygwin-yml.sh new file mode 100755 index 00000000000..bc1d195a9f4 --- /dev/null +++ b/.github/workflows/update-cygwin-yml.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +for X in minimal; do sed 's/\[standard\]/['$X']/g;s/CI cygwin-standard/CI cygwin-'$X'/g;' ci-cygwin-standard.yml > ci-cygwin-$X.yml; done diff --git a/.gitignore b/.gitignore index 77d20df76b2..59ac38b3a15 100644 --- a/.gitignore +++ b/.gitignore @@ -13,9 +13,13 @@ /config.log /config.status /configure +/conftest* /m4/sage_spkg_configures.m4 +# no longer generated, but may still be in user worktrees +/src/lib/pkgconfig + ################### # Temporary Files # ################### @@ -95,4 +99,12 @@ gitlab-build-docker.log /src/build /src/Makefile /src/bin/sage-env-config +/build/bin/sage-build-env-config + +####################### +# tox generated files # +####################### +/.tox +/prefix +worktree* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 22a4075b91e..e80a04b422b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,7 +37,7 @@ # If you want to provide your own runners, make sure to tag them as follows: # * "big" (60GB of disk space are available) to make build-from-clean pass. -image: docker:latest +image: docker:stable stages: - build @@ -77,7 +77,7 @@ before_script: # better isolation. If you expect many builds to run simultaneously on a host, # conflicting tags can cause issues with a mounted DOCKER_HOST. services: -- docker:18.05.0-ce-dind +- docker:stable-dind # Build Sage and its documentation. # The build starts from the build artifacts of DEFAULT_ARTIFACT_BASE which is @@ -100,6 +100,7 @@ build-from-latest: - .ci/build-docker.sh | tee gitlab-build-docker.log | .ci/head-tail.sh 1048576 - .ci/push-gitlab.sh sagemath-dev - .ci/push-gitlab.sh sagemath + - DOCKER_TAG=$CI_COMMIT_SHA .ci/push-gitlab.sh sagemath except: - master - develop @@ -192,43 +193,3 @@ push-dockerhub-dev: script: - . .ci/pull-gitlab.sh sagemath-dev - sh .ci/push-dockerhub.sh sagemath-dev - -.py3: - variables: - WITH_PYTHON: 3 - DEFAULT_ARTIFACT_BASE: sagemath/sagemath-dev:develop-py3 - -build-from-latest-py3: - extends: - - build-from-latest - - .py3 - -build-from-clean-py3: - extends: - - build-from-clean - - .py3 - -test-dev-py3: - extends: - - test-dev - - .py3 - -test-cli-py3: - extends: - - test-cli - - .py3 - -test-jupyter-py3: - extends: - - test-jupyter - - .py3 - -push-dockerhub-py3: - extends: - - push-dockerhub - - .py3 - -push-dockerhub-dev-py3: - extends: - - push-dockerhub-dev - - .py3 diff --git a/.homebrew-build-env b/.homebrew-build-env new file mode 100644 index 00000000000..987941c937a --- /dev/null +++ b/.homebrew-build-env @@ -0,0 +1,46 @@ +# Source this -*- shell-script -*- to set some environment variables +# that activate keg-only homebrew package installations + +HOMEBREW=`brew --prefix` || return 1 +for l in gettext; do + if [ -d "$HOMEBREW/opt/$l/bin" ]; then + PATH="$HOMEBREW/opt/$l/bin:$PATH" + fi +done +export PATH +PKG_CONFIG_PATH="$HOMEBREW/lib/pkgconfig:$PKG_CONFIG_PATH" +# libpng.pc depends on zlib.pc +for l in openblas openssl readline sqlite zlib; do + if [ -d "$HOMEBREW/opt/$l/lib/pkgconfig" ]; then + PKG_CONFIG_PATH="$HOMEBREW/opt/$l/lib/pkgconfig:$PKG_CONFIG_PATH" + fi +done +export PKG_CONFIG_PATH +# Compile-time path for libraries and includes. They are like adding +# the gcc options -L or -I, but the libraries or includes added here +# are searched after the directories specified on the command line. +[ -z "$LIBRARY_PATH" ] || LIBRARY_PATH=":${LIBRARY_PATH}" +LIBRARY_PATH="$HOMEBREW/lib$LIBRARY_PATH" +[ -z "$CPATH" ] || CPATH=":${CPATH}" +CPATH="$HOMEBREW/include$CPATH" +for l in readline ; do + if [ -d "$HOMEBREW/opt/$l/lib" ]; then + LIBRARY_PATH="$HOMEBREW/opt/$l/lib:$LIBRARY_PATH" + fi + if [ -d "$HOMEBREW/opt/$l/include" ]; then + CPATH="$HOMEBREW/opt/$l/include:$CPATH" + fi +done +for l in "gcc@9/lib/gcc/9"; do + if [ -d "$HOMEBREW/opt/$l" ]; then + LIBRARY_PATH="$HOMEBREW/opt/$l:$LIBRARY_PATH" + fi +done +export LIBRARY_PATH +export CPATH +for l in gettext; do + if [ -d "$HOMEBREW/opt/$l/share/aclocal" ]; then + ACLOCAL_PATH="$HOMEBREW/opt/$l/share/aclocal:$ACLOCAL_PATH" + fi +done +export ACLOCAL_PATH diff --git a/.lgtm.yml b/.lgtm.yml index 9617a8d128e..fefc94e7010 100644 --- a/.lgtm.yml +++ b/.lgtm.yml @@ -1,6 +1,7 @@ queries: - exclude: py/call/wrong-named-class-argument - exclude: py/call/wrong-number-class-arguments + - exclude: py/similar-function - exclude: py/unsafe-cyclic-import path_classifiers: imports_only: @@ -9,6 +10,7 @@ path_classifiers: - "**/species/library.py" - "**/categories/basic.py" - "**/combinat/ribbon.py" + - "**/combinat/family.py" - "**/interacts/geometry.py" - "**/matroids/advanced.py" - "**/matroids/named_matroids.py" diff --git a/COPYING.txt b/COPYING.txt index 9a223312b7d..c85f5b35b87 100644 --- a/COPYING.txt +++ b/COPYING.txt @@ -121,7 +121,6 @@ sage_scripts GPLv2+ sagenb GPLv3 sagetex GPLv2+ (CC BY-SA 3.0 for included documentation) scipy Modified BSD -scons MIT License setuptools Python License singular GPLv2 or GPLv3 (see below) six MIT License diff --git a/Makefile b/Makefile index 1624526e0b8..04518392a6a 100644 --- a/Makefile +++ b/Makefile @@ -33,24 +33,28 @@ sageruntime: base-toolchain +build/bin/sage-logger \ "cd build/make && ./install '$@'" logs/install.log +# CONFIG_FILES lists all files that appear in AC_CONFIG_FILES in configure.ac; +# except for build/make/Makefile-auto, which is unused by the build system +CONFIG_FILES = build/make/Makefile src/bin/sage-env-config build/bin/sage-build-env-config build/pkgs/sage_conf/src/sage_conf.py build/pkgs/sage_conf/src/setup.cfg + +# SPKG_COLLECT_FILES contains all files that influence the SAGE_SPKG_COLLECT macro +SPKG_COLLECT_FILES = build/pkgs/*/type build/pkgs/*/package-version.txt build/pkgs/*/dependencies build/pkgs/*/requirements.txt build/pkgs/*/checksums.ini build/pkgs/*/spkg-install + # If configure was run before, rerun it with the old arguments. # Otherwise, run configure with argument $PREREQ_OPTIONS. -build/make/Makefile: configure build/make/deps build/make/Makefile.in build/pkgs/*/* +build/make/Makefile: configure $(SPKG_COLLECT_FILES) $(CONFIG_FILES:%=%.in) rm -f config.log mkdir -p logs/pkgs ln -s logs/pkgs/config.log config.log @if [ -x config.status ]; then \ ./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; ) + echo >&2 '****************************************************************************'; \ + echo >&2 'error: Sage source tree is unconfigured. Please run "./configure" first.'; \ + echo >&2 'note: Type "./configure --help" to see the available configuration options.'; \ + echo >&2 '****************************************************************************'; \ + exit 1; \ + fi # This is used to monitor progress towards Python 3 and prevent # regressions. Should be removed after the full switch to python3. @@ -77,6 +81,8 @@ dist: build/make/Makefile ssl: all ./sage -i pyopenssl +# Deleting src/lib is to get rid of src/lib/pkgconfig +# that was forgotten to clean in #29082. misc-clean: @echo "Deleting build artifacts generated by autoconf, automake, make ..." rm -rf logs @@ -85,7 +91,7 @@ misc-clean: rm -f aclocal.m4 config.log confcache rm -rf autom4te.cache rm -f build/make/Makefile build/make/Makefile-auto - rm -f src/lib/pkgconfig/* + rm -rf src/lib bdist-clean: clean $(MAKE) misc-clean @@ -102,6 +108,9 @@ distclean: build-clean # source tarball bootstrap-clean: rm -rf config configure build/make/Makefile-auto.in + rm -f src/doc/en/installation/*.txt + rm -rf src/doc/en/reference/spkg/*.rst + rm -f src/doc/en/reference/repl/*.txt # Remove absolutely everything which isn't part of the git repo maintainer-clean: distclean bootstrap-clean @@ -109,7 +118,9 @@ maintainer-clean: distclean bootstrap-clean # Remove everything that is not necessary to run Sage and pass all its # doctests. -micro_release: sagelib-clean misc-clean +micro_release: + $(MAKE) sagelib-clean + $(MAKE) misc-clean @echo "Stripping binaries ..." LC_ALL=C find local/lib local/bin -type f -exec strip '{}' ';' 2>&1 | grep -v "File format not recognized" | grep -v "File truncated" || true @echo "Removing sphinx artifacts..." @@ -193,7 +204,7 @@ ptestoptional: all ptestoptionallong: all $(PTESTALL) --long --logfile=logs/ptestoptionallong.log -configure: configure.ac src/bin/sage-version.sh m4/*.m4 build/pkgs/*/spkg-configure.m4 +configure: bootstrap src/doc/bootstrap configure.ac src/bin/sage-version.sh m4/*.m4 build/pkgs/*/spkg-configure.m4 build/pkgs/*/type build/pkgs/*.txt build/pkgs/*/distros/*.txt ./bootstrap -d install: all @@ -209,7 +220,7 @@ list: @$(MAKE) --silent build/make/Makefile >&2 @$(MAKE) --silent -f build/make/Makefile SAGE_SPKG_INST=local $@ -.PHONY: default build install micro_release \ +.PHONY: default build dist install micro_release \ misc-clean bdist-clean distclean bootstrap-clean maintainer-clean \ test check testoptional testall testlong testoptionallong testallong \ ptest ptestoptional ptestall ptestlong ptestoptionallong ptestallong \ diff --git a/README.md b/README.md index e25634ebf9b..62d99fb397e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ > "Creating a Viable Open Source Alternative to > Magma, Maple, Mathematica, and MATLAB" -> Copyright (C) 2005-2019 The Sage Development Team +> Copyright (C) 2005-2020 The Sage Development Team https://www.sagemath.org @@ -24,6 +24,8 @@ you extracted the binary archive and type: ./sage +(Note that the first run will take more time, as Sage needs to get itself ready.) + If you downloaded the [sources](https://www.sagemath.org/download-source.html), please read below on how to build Sage and work around common issues. @@ -31,260 +33,311 @@ If you have questions or encounter problems, please do not hesitate to email the [sage-support mailing list](https://groups.google.com/group/sage-support) or ask on [ask.sagemath.org](https://ask.sagemath.org). -Contributing to Sage --------------------- - -If you'd like to contribute to Sage, be sure to read the -[Developer's Guide](https://doc.sagemath.org/html/en/developer/index.html). - Supported Platforms ------------------- -Sage fully supports several Linux distributions, recent versions of -Mac OS X, Windows (using virtualization), as well as a number of -Solaris and OpenSolaris releases. +Sage fully supports all major Linux distributions, recent versions of +macOS, and Windows (using Cygwin, Windows Subsystem for Linux, or +using virtualization). -Ports are in progress to some other, less common platforms. The list of -supported platforms and their current statuses are given in [our wiki](https://wiki.sagemath.org/SupportedPlatforms). +We highly appreciate contributions to Sage that fix portability bugs +and help port Sage to new platforms; let us know at the [sage-devel +mailing list](https://groups.google.com/group/sage-devel). -If you are interested in helping port Sage to a new platform, please let -us know at the [sage-devel mailing list](https://groups.google.com/group/sage-devel). +Docker Images +------------- -Quick Instructions to Build from Source ---------------------------------------- +You can also have a look at our Docker images to run Sage. +To use these images [install Docker](https://www.docker.com/community-edition#/download) +and follow the instructions on [our Docker Hub page](https://hub.docker.com/r/sagemath/sagemath/). -The following steps briefly outline the process of building Sage from -source. More detailed instructions, including how to build faster on -multicore machines, are contained later in this README and in the -[Installation Guide](https://doc.sagemath.org/html/en/installation). +[Windows] Preparing the Platform +-------------------------------- -1. Make sure your system has an SSL library and its development -files installed +The 64-bit version of Cygwin, also known as Cygwin64, is the current +target for Sage support on Windows. - Like Python, on which it is based, Sage uses the OpenSSL library - for added performance if made available by the operating system. It - has been shown that Sage can be successfully built against other - SSL libraries, with some of its features disabled. +1. Download [cygwin64](https://cygwin.com/install.html) (do not get + the 32-bit version; it is not supported by Sage). +2. Run the `setup-x86_64.exe` graphical installer. Pick the default + options in most cases. At the package selection screen, use the + search bar to find and select at least the following packages: + `bzip2`, `coreutils`, `curl`, `gawk`, `gzip`, `tar`, `wget`, `git`. -1. Make sure you have the dependencies and 5 GB of free disk space +3. Start the Cygwin terminal and ensure you get a working bash prompt. - * __All Linux versions:__ gcc, make, m4, perl, ranlib, git, and tar (a - matching set of gcc, gfortran and g++ will avoid the compilation - of Sage-specific compilers). It should also be possible to use clang/clang++, - however this is less well-tested. +4. Make sure the path of your Cygwin home directory does not contain + space characters. - * __Fedora or RedHat systems:__ the perl-ExtUtils-MakeMaker package. - (install these using your package manager) + By default, your username in Cygwin is the same as your username in + Windows. This might contain spaces and other traditionally + non-UNIX-friendly characters, e.g., if it is your full name. You + can check this as follows: - * __OS X:__ - * Make sure you have installed the most recent version - of Xcode which you can install for free from the App Store. - * You also need to install the "command line tools". When - using OS X Mavericks, after installing Xcode, run - `xcode-select --install` from a terminal window: - Then click "Install" in the pop-up window. - When using OS X Mountain Lion or earlier, you need to install the - command line tools from Xcode: run Xcode; then from the File - menu, choose "Preferences", then the "Downloads" tab, and then - "Install" the Command Line Tools. You might also have Homebrew or - a similar "Apple's missing package manager" system installed, with - and libraries such gfortran, gmp, etc installed. (However, this - is still experimental as of May 2019). + $ whoami + Erik M. Bray - * __Other platforms:__ See detailed instructions below. + This means your default home directory on Cygwin contains this + username verbatim; in the above example, `/home/Erik M. Bray`. It + will save some potential trouble if you change your Cygwin home + directory to something not containing any non-alphanumeric + characters, for example, `/home/embray`. The easiest way to do + this is to first create the home directory you want to use instead, + then create an `/etc/passwd` file specifying that directory as your + home, as follows: -1. It might be desirable, it terms of faster building and better portability, - to install, as system packages, an ever increasing [list of Sage packages](https://trac.sagemath.org/ticket/27330) - which otherwise might have to be built. The following is a list of Sage packages - "replaceable" by system's packages as of Sage release 8.8: - `bzip2`, `curl`, `cmake`, `gcc/clang`, `gf2x`, `gfortran` (usually part of `gcc` installation), - `git`, `gmp`, `libffi`, `patch`, `pcre`, `perl_term_readline_gnu`, `xz/lzma`, `yasm`, `zeromq`, `zlib`. - Details and names of system packages containing these are system-dependent. E.g. on Debian - `bzip2` lives in `libbz2-dev`. More details on this are in Installation manual. + $ whocanibe=embray + $ mkdir /home/$whocanibe + $ mkpasswd.exe -l -u "$(whoami)" | sed -r 's,/home/[^:]+,/home/'$whocanibe, > /etc/passwd -1. Extract the tarball + After this, close all Cygwin terminals (ensure nothing in + `C:\cygwin64` is running), then start a new Cygwin terminal and + your home directory should have moved. - tar zxvf sage-*.tar.gz + There are [other ways to do + this](https://stackoverflow.com/questions/1494658/how-can-i-change-my-cygwin-home-folder-after-installation), + but the above seems to be the simplest that's still supported. -1. cd into the Sage directory and type make +5. Install the package manager `apt-cyg`: - cd sage-*/ - make + $ curl -OL https://rawgit.com/transcode-open/apt-cyg/master/apt-cyg + $ install apt-cyg /usr/local/bin + $ rm -f apt-cyg - That's it! Everything is automatic and non-interactive. The build - should work fine on all fully supported platforms. If it does not, we - want to know! +An alternative to Cygwin is to use [Windows Subsystem for +Linux](https://docs.microsoft.com/en-us/windows/wsl/faq), which allows +you to install a standard Linux distribution such as Ubuntu within +your Windows. Then all instructions for installation in Linux apply. -Environment Variables ---------------------- +As another alternative, you can also run Linux on Windows using Docker +(see above) or other virtualization solutions such as the [Sage +virtual appliance](https://wiki.sagemath.org/SageAppliance). -There are a lot of environment variables which control the install -process of Sage described in more detail in the -[Installation Guide](https://doc.sagemath.org/html/en/installation/source.html#environment-variables). +[macOS] Preparing the Platform +------------------------------ -Implementation --------------- +Make sure you have installed the most current version of Xcode +supported on your version of macOS. If you don't, go to +https://developer.apple.com/, sign up, and download the free Xcode +package. -Sage has significant components written in the following languages: -C/C++, Python, Cython, Lisp, Fortran, and a bit of Perl. Lisp (ECL), Python, and Cython -are built as part of Sage. +You also need to install the "command line tools": After installing +Xcode, run `xcode-select --install` from a terminal window; then click +"Install" in the pop-up window. (When using Mountain Lion or earlier, +you need to install the command line tools from Xcode: run Xcode; then +from the File menu, choose "Preferences", then the "Downloads" tab, +and then "Install" the Command Line Tools.) -Docker Images -------------- +Optionally, you can consider installing Homebrew ("the missing package +manager for macOS") from https://brew.sh/, which can provide libraries +such gfortran, gmp, etc. -You can also have a look at our Docker images to run Sage. -To use these images [install Docker](https://www.docker.com/community-edition#/download) -and follow the instructions on [our Docker Hub page](https://hub.docker.com/r/sagemath/sagemath/). +Instructions to Build from Source +--------------------------------- + +Like many other software packages, Sage is built from source using +`./configure`, followed by `make`. However, we strongly recommend to +read the following step-by-step instructions for building Sage. + +The instructions cover all of Linux, macOS, and Cygwin. + +More detailed instructions are contained in the [Installation +Guide](https://doc.sagemath.org/html/en/installation). + +1. Decide on the source/build directory (`SAGE_ROOT`): + + - For example, you could use `SAGE_ROOT=~/sage/sage-x.y`, which we + will use as the running example below, where `x.y` is the + current Sage version. + + - You need at least 6 GB of free disk space. + + - The path name must contain **no spaces**. + + - After starting the build, you cannot move the source/build + directory without breaking things. + + - [Cygwin] Avoid building in home directories of Windows domain + users or in paths with capital letters. + +2. Download/unpack the sources. + + - After downloading the source tarball `sage-x.y.tar.gz` into + `~/sage/`: + + $ cd ~/sage/ + $ tar zxvf sage-x.y.tar.gz + + This will create the subdirectory `sage-x.y`. + + - [Git] Alternatively, clone the Sage git repository: + + $ git clone -c core.symlinks=true --branch master git://trac.sagemath.org/sage.git -More Detailed Instructions to Build from Source ------------------------------------------------ + This will create the subdirectory `sage`. -1. Make sure you have about 5 GB of free disk space. + - [Windows] The Sage source tree contains symbolic links, and the + build will not work if Windows line endings rather than UNIX + line endings are used. -1. Install build dependencies + Therefore it is crucial that you unpack the source tree from the + Cygwin (or WSL) `bash` using the Cygwin (or WSL) `tar` utility + and not using other Windows tools (including mingw). Likewise, + when using `git`, it is recommended (but not necessary) to use the Cygwin (or WSL) + version of `git`. - * __Linux:__ See quick instructions above. +3. `cd` into the source/build directory: - * __OS X:__ (a.k.a __MacOS__) Make sure you have a recent Xcode version. - If you don't, go to https://developer.apple.com/, - sign up, and download the free Xcode package. Usually, Xcode's command line - tools suffice to build Sage, although several times new releases of Xcode broke this. - Only OS X >= 10.4 is supported, and (as of May 2019) we only test Sage on OS X >= 10.6. + $ cd sage*/ - * __Solaris and OpenSolaris:__ Building Sage on these platforms is more - tricky than on Linux or OS X. For details on how to build Sage on - these platforms, see [our wiki](https://wiki.sagemath.org/solaris) (outdated as of May 2019). +4. Optionally, decide on the installation prefix (`SAGE_LOCAL`): - * __Windows:__ [Download and install VirtualBox](https://www.virtualbox.org/wiki/Downloads), - and then download the [Sage virtual appliance](https://wiki.sagemath.org/SageAppliance). + - Traditionally, and by default, Sage is installed into the + subdirectory hierarchy rooted at `SAGE_ROOT/local`. - * __NOTE:__ On some operating systems, it might be necessary to install - gas/as, gld/ld, gnm/nm. On most platforms, these are automatically - installed when you install the programs listed above. + - This can be changed using `./configure --prefix=SAGE_LOCAL`, + where `SAGE_LOCAL` is the desired installation prefix, which + must be writable by the user. (See the installation manual for + options if you want to install into shared locations such as + `/usr/local/`. Do not attempt to build Sage as root.) -1. Extract the Sage source tarball into a directory, making sure - there are no spaces in the path to the resulting directory. +5. [Git] If you cloned the Sage repository using `git`, bootstrap the + source tree using: - Note that moving the directory after Sage has been built will - require to build Sage again. + $ make configure -1. Change to the Sage directory using `cd`. +6. [Linux, Cygwin] Install the required minimal build prerequisites. -1. Optional: set some environment variables to customize the build. + * Compilers: `gcc`, `gfortran`, `g++` (a matching set of these + three will avoid the compilation of Sage-specific compilers - + unless they are too old). See the Installation Manual for a + discussion of suitable compilers. - For example, the `MAKE` environment variable controls whether to run - several jobs in parallel, while the `SAGE_CHECK` environment variable - controls whether to perform more tests during the installation. For - an in-depth discussion of environment variables for building Sage, see - [the installation guide](https://doc.sagemath.org/html/en/installation/source.html#environment-variables). + * Build tools: GNU `make`, GNU `m4`, `perl` (including + ``ExtUtils::MakeMaker``), `ranlib`, `git`, `tar`, `bc` - On a machine with 4 processors, say, typing `export MAKE="make -j4"` - will configure the build script to perform a parallel compilation of - Sage using 4 jobs. You might even consider `-j5` or `-j6`, as - building with more jobs than CPU cores can speed things up further. - You might in addition pass a `-l` [load flag](https://www.gnu.org/software/make/manual/make.html#Options-Summary) - to `make`: this sets a load limit, so for example if you execute - `export MAKE="make -j4 -l5.5"` then "make" won't start more than one - job at a time if the system load average is above 5.5, see - the [make documentation](https://www.gnu.org/software/make/manual/make.html#Parallel). + * Any version of `python` (full installation including `urllib`), + but ideally version 3.7.x, which will avoid having to build Sage's + own copy of Python 3. - If you want to run the test suite for each individual Sage package - as it gets installed, type `export SAGE_CHECK="yes"`. This will run - each test suite, raising an error if any failure occurs. Python's - test suite has been disabled by default, because it causes failures - on most systems. To enable the Python test suite, set the environment - variable `SAGE_CHECK_PACKAGES` to `python`. + We have collected lists of system packages that provide these build + prerequisites. See [build/pkgs/arch.txt](build/pkgs/arch.txt), + [cygwin.txt](build/pkgs/cygwin.txt), + [debian.txt](build/pkgs/debian.txt) (also for Ubuntu, Linux Mint, + etc.), [fedora.txt](build/pkgs/fedora.txt) (also for Red Hat, + CentOS), and [slackware.txt](build/pkgs/slackware.txt). -1. To start the build, type `make`. +7. Optional, but highly recommended: Make sure your system has an SSL + library and its development files installed. - Note: to build a Python2-based Sage, instead of typing `make`, type + Like Python, on which it is based, Sage uses the OpenSSL library + for added performance if made available by the operating system. It + has been shown that Sage can be successfully built against other + SSL libraries, with some of its features disabled. + +8. Optional: It is recommended that you have both LaTeX and the + ImageMagick tools (e.g. the "convert" command) installed since some + plotting functionality benefits from it. + +9. Optionally, review the configuration options, which includes + many optional packages: + + ./configure --help + +10. Optional, but highly recommended: Set some environment variables to + customize the build. - make configure - ./configure --with-python=2 - make + For example, the `MAKE` environment variable controls whether to + run several jobs in parallel. On a machine with 4 processors, say, + typing `export MAKE="make -j4"` will configure the build script to + perform a parallel compilation of Sage using 4 jobs. On some + powerful machines, you might even consider `-j16`, as building with + more jobs than CPU cores can speed things up further. - This will build Sage based on Python 2 rather than based on Python 3, - which is the default since sage 9.0. + To reduce the terminal output during the build, type `export V=0`. + (`V` stands for "verbosity".) -1. Wait about 20 minutes to 14 days, depending on your computer (it took - about 2 weeks to build Sage on the T-Mobile G1 Android cell phone). + For an in-depth discussion of more environment variables for + building Sage, see [the installation + guide](https://doc.sagemath.org/html/en/installation/source.html#environment-variables). -1. Type `./sage` to try it out. +11. Type `./configure`, followed by any options that you wish to use. + For example, to build Sage with `gf2x` package supplied by Sage, + use `./configure --with-system-gf2x=no`. -1. Optional: Type `make ptestlong` to test all examples in the documentation + At the end of a successful `./configure` run, you may see messages + recommending to install extra system packages using your package + manager. + + For a large [list of Sage + packages](https://trac.sagemath.org/ticket/27330), Sage is able to + detect whether an installed system package is suitable for use with + Sage; in that case, Sage will not build another copy from source. + + Sometimes, the messages will recommend to install packages that are + already installed on your system. See the earlier configure + messages or the file `config.log` for explanation. Also, the + messages may recommend to install packages that are actually not + available; only the most recent releases of your distribution will + have all of these recommended packages. + +12. Optional: If you choose to install the additional system packages, + a re-run of `./configure` will test whether the versions installed + are usable for Sage; if they are, this will reduce the compilation + time and disk space needed by Sage. The usage of packages may be + adjusted by `./configure` parameters (check again the output of + `./configure --help`). + +13. Type `make`. That's it! Everything is automatic and + non-interactive; but it will take a few hours (on a recent + computer). + + The build should work fine on all fully supported platforms. If it + does not, we want to know! + +14. Type `./sage` to try it out. + +15. Optional: Type `make ptestlong` to test all examples in the documentation (over 200,000 lines of input!) -- this takes from 10 minutes to several hours. Don't get too disturbed if there are 2 to 3 failures, but always feel free to email the section of `logs/ptestlong.log` that contains errors to the [sage-support mailing list](https://groups.google.com/group/sage-support). If there are numerous failures, there was a serious problem with your build. - Note: if you built for Python 3, you can instead run `make ptest-python3`. - -1. The HTML version of the [documentation](https://doc.sagemath.org/html/en/index.html) +16. The HTML version of the [documentation](https://doc.sagemath.org/html/en/index.html) is built during the compilation process of Sage and resides in the directory `local/share/doc/sage/html/`. -1. Optional: If you want to build the PDF version of the documentation, +17. Optional: If you want to build the PDF version of the documentation, run `make doc-pdf` (this requires LaTeX to be installed). -1. Optional: You might install optional packages of interest to you: type +18. Optional: You might install optional packages of interest to you: type `./sage --optional` to get a list. -1. Optional: It is recommended that you have both LaTeX and the - ImageMagick tools (e.g. the "convert" command) installed since some - plotting functionality benefits from it. - -1. Optional: Read this if you are intending to run a Sage notebook - server for multiple users. For security (i.e., to run - `notebook(secure=True)`) you want to access the server using the - HTTPS protocol. First, install OpenSSL and the OpenSSL development - headers on your system if they are not already installed. Then - install pyOpenSSL by building Sage and then typing - `./sage -i pyopenssl`. - Note that this command requires internet access. Alternatively, - `make ssl` builds Sage and installs pyOpenSSL. - Troubleshooting --------------- If you have problems building Sage, check the Sage Installation Guide, -and also note the following. Each separate component of Sage is -contained in an spkg; these are stored in `build/pkgs/`. As each one -is built, a build log is stored in `logs/pkgs/`, so you can browse these -to find error messages. If an spkg fails to build, the whole build -process will stop soon after, so check the most recent log files -first, or run - - grep -li "^Error" logs/pkgs/* - -from the top-level Sage directory to find log files with error -messages in them. Send (a small part of) the relevant log file to the -[sage-devel mailing list](https://groups.google.com/group/sage-devel), -making sure to include at least some of the error messages; probably -someone there will have some helpful suggestions. - -Supported Compilers -------------------- +as well as the version-specific Sage Installation FAQ in the [Sage Release +Tour](https://wiki.sagemath.org/ReleaseTours) corresponding to the +version that you are installing. + +Please do not hesitate to ask for help in the [SageMath forum +](https://ask.sagemath.org/questions/) or the [sage-support mailing +list](https://groups.google.com/forum/#!forum/sage-support). The +[Troubleshooting section in the Sage Installation Guide]() provides +instructions on what information to provide so that we can provide +help more effectively. + +Contributing to Sage +-------------------- + +If you'd like to contribute to Sage, we strongly recommend that you read the +[Developer's Guide](https://doc.sagemath.org/html/en/developer/index.html). -Sage includes a GCC (_GNU Compiler Collection_) package. However, -it almost always better to use C, C++ and Fortran compilers -already available on the system. To force using specific compilers, -set environment variables `CC`, `CXX`, and `FC` (for C, C++, and Fortran compilers, -respectively) to the desired values, -and run `./configure`. E.g. `CC=clang CXX=clang++ FC=gfortran ./configure` -will configure Sage to be built with Clang C/C++ compilers and Fortran -compiler gfortran. - -It is determined automatically whether Sage's GCC package, or just its part containing -Fortran compiler `gfortran` needs to be installed. This can be overwritten -by running `./configure` with option `--without-system-gcc`. - -There are some known problems with old assemblers, in particular when -building the ECM package. You should ensure that your assembler -understands all instructions for your processor. On Linux, this means -you need a recent version of binutils; on OS X you need a recent version -of Xcode. +Sage has significant components written in the following languages: +C/C++, Python, Cython, Common Lisp, Fortran, and a bit of Perl. Directory Layout ---------------- @@ -361,13 +414,7 @@ through to another Makefile under `build/make/Makefile`. The latter `build/make/Makefile` *is* generated by an autoconf-generated `configure` script, using the template in `build/make/Makefile.in`. This includes rules for building the Sage library itself (`make sagelib`), and for -building and installing each of Sage's dependencies (e.g. `make python2`). - -Although it's possible to manually run Sage's `configure` script if one wants -to provide some customizations (e.g. it is possible to select which BLAS -implementation to use), the top-level `Makefile` will run `configure` for you, -in order to build `build/make/Makefile` since it's a prerequisite for most of -Sage's make targets. +building and installing each of Sage's dependencies (e.g. `make gf2x`). The `configure` script itself, if it is not already built, can be generated by running the `bootstrap` script (the latter requires _GNU autotools_ being installed). @@ -377,12 +424,13 @@ To summarize, running a command like `make python3` at the top-level of the source tree goes something like this: 1. `make python3` -1. run `./bootstrap` if `configure` does not exist -1. run `./configure` if `build/make/Makefile` does not exist -1. `cd` into `build/make` and run the `install` script--this is little more +2. run `./bootstrap` if `configure` needs updating +3. run `./configure` with any previously configured options if `build/make/Makefile` + needs updating +4. `cd` into `build/make` and run the `install` script--this is little more than a front-end to running `make -f build/make/Makefile python3`, which sets some necessary environment variables and logs some information -1. `build/make/Makefile` contains the actual rule for building `python3`; this +5. `build/make/Makefile` contains the actual rule for building `python3`; this includes building all of `python3`'s dependencies first (and their dependencies, recursively); the actual package installation is performed with the `sage-spkg` program @@ -391,12 +439,14 @@ source tree goes something like this: Relocation ---------- -It used to be possible to move the `sage-x.y.z/` directory anywhere you -want, however, this is no longer supported. -If you copy the sage script or make a symbolic link to it, you +It is not supported to move the `SAGE_ROOT` or `SAGE_LOCAL` directory +after starting the build. If you do move the directories, you will +have to rebuilt Sage again from scratch. + +If you copy the `sage` script or make a symbolic link to it, you should modify the script to reflect this (as instructed at the top of -the script). It is important that the path to Sage does not have any spaces -and non-ASCII characters in it. +the script). It is important that the path to Sage does not have any +spaces and non-ASCII characters in it. For a system-wide installation, you have to build Sage as a "normal" user and then as root you can change permissions. Afterwards, you need to start up @@ -404,9 +454,6 @@ Sage as root at least once prior to using the system-wide Sage as a normal user. See the [Installation Guide](https://doc.sagemath.org/html/en/installation/source.html#installation-in-a-multiuser-environment) for further information. -If you find anything that doesn't work correctly after you moved the -directory, please email the [sage-support mailing list](https://groups.google.com/group/sage-support). - Redistribution -------------- @@ -415,15 +462,15 @@ install. You can make changes to documentation, source, etc., and very easily package the complete results up for redistribution just like we do. -1. To make your own source tarball of Sage, type: +1. To make a binary distribution with your currently installed packages, + visit [sagemath/binary-pkg](https://github.com/sagemath/binary-pkg). + +2. (**Obsolete, probably broken**) To make your own source tarball of Sage, type: sage --sdist The result is placed in the directory `dist/`. -2. To make a binary distribution with your currently installed packages, - visit [sagemath/binary-pkg](https://github.com/sagemath/binary-pkg). - Changes to Included Software ---------------------------- diff --git a/VERSION.txt b/VERSION.txt index 534e8c95371..d017b100fbb 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.1.beta4, Release Date: 2020-02-13 +SageMath version 9.2.beta8, Release Date: 2020-08-10 diff --git a/bootstrap b/bootstrap index 12b8eb474b6..e90d04e0c47 100755 --- a/bootstrap +++ b/bootstrap @@ -75,23 +75,29 @@ install_config_rpath() { bootstrap () { rm -f m4/sage_spkg_configures.m4 spkg_configures="" - for filename in $(find build/pkgs -type f -name spkg-configure.m4 | sort); do - pkgname="$(echo $filename | cut -d/ -f3)" - echo "m4_sinclude([$filename])" >> m4/sage_spkg_configures.m4 - spkg_configures="$spkg_configures -SAGE_SPKG_CONFIGURE_$(echo ${pkgname} | tr '[a-z]' '[A-Z]')" - done for filename in $(find build/pkgs -type f -name type); do pkgtype="$(cat $filename)" pkgname="$(echo $filename | cut -d/ -f3)" case "$pkgtype" in optional|experimental) - spkg_configures="$spkg_configures -SAGE_SPKG_ENABLE([$pkgname], [$pkgtype])" ;; + # Trac #29629: Temporary solution for Sage 9.1: Do not provide + # --enable-SPKG options for installing pip packages + if [ ! -f "$(dirname $filename)/requirements.txt" ]; then + spkg_configures="$spkg_configures +SAGE_SPKG_ENABLE([$pkgname], [$pkgtype])" + fi + ;; esac done + for filename in $(find build/pkgs -type f -name spkg-configure.m4 | sort); do + pkgname="$(echo $filename | cut -d/ -f3)" + echo "m4_sinclude([$filename])" >> m4/sage_spkg_configures.m4 + spkg_configures="$spkg_configures +SAGE_SPKG_CONFIGURE_$(echo ${pkgname} | tr '[a-z]' '[A-Z]')" + done echo "$spkg_configures" >> m4/sage_spkg_configures.m4 + SAGE_ROOT="$SAGE_ROOT" src/doc/bootstrap && \ install_config_rpath && \ aclocal -I m4 && \ automake --add-missing --copy build/make/Makefile-auto && \ @@ -141,7 +147,7 @@ save () { set -e # Check that config.guess is sufficiently recent - if ! grep '^timestamp=.*201[5-9]' config/config.guess >/dev/null; then + if ! grep '^timestamp=.*20\(1[5-9]\|2[0-9]\)' config/config.guess >/dev/null; then echo >&2 "Error: config.guess is outdated:" grep >&2 '^timestamp=' config/config.guess echo >&2 "You should update the 'gnuconfig' or 'automake' package and try again" @@ -154,7 +160,7 @@ save () { # Create configure tarball echo "Creating $NEWCONFBALL..." mkdir -p upstream - tar zcf "$NEWCONFBALL" configure config/* build/make/Makefile-auto.in + tar zcf "$NEWCONFBALL" configure config/* build/make/Makefile-auto.in src/doc/en/installation/*.txt src/doc/en/reference/spkg/*.rst src/doc/en/reference/repl/*.txt # Update version echo "$NEWCONFVERSION" >$PKG/package-version.txt @@ -197,9 +203,9 @@ fi $MAKE bootstrap-clean 2>/dev/null mkdir config 2>/dev/null -# Get autotools from our own package into PATH (Trac #21214). # If Sage has not been built yet, this will fail due to a missing # sage-env-config. We just ignore that error. +source src/bin/sage-env-config 2>/dev/null source src/bin/sage-env 2>/dev/null diff --git a/build/bin/sage-build-env-config.in b/build/bin/sage-build-env-config.in new file mode 100644 index 00000000000..01ba892dfd6 --- /dev/null +++ b/build/bin/sage-build-env-config.in @@ -0,0 +1,92 @@ +# -*- shell-script -*- + +########################################################################### +# +# Set some environment variables for building Sage (the distribution). +# +# NOTES: +# - You must *source* this script instead of executing. +# - Use "return" instead of "exit" to signal a failure. Since this +# file is sourced, an "exit" here will actually exit src/bin/sage, +# which is probably not intended. +# - All environment variables set here should be *exported*, otherwise +# they won't be available in child programs. +# +# If you want to set all environment variables for your shell like +# they are during the build of Sage packages, type +# +# sage --buildsh +# +########################################################################## + +# The configured CXX without special flags added that enable C++11 support +export SAGE_CXX_WITHOUT_STD="@SAGE_CXX_WITHOUT_STD@" + +# This is usually blank if the system GMP is used, or $SAGE_LOCAL otherwise +export SAGE_GMP_PREFIX="@SAGE_GMP_PREFIX@" +export SAGE_GMP_INCLUDE="@SAGE_GMP_INCLUDE@" +if [ -n "$SAGE_GMP_PREFIX" ]; then + # Many packages that depend on GMP accept a --with-gmp= flag to + # their ./configure scripts. When using the system's GMP this is not + # generally necessary, but when using the GMP package installed in + # SAGE_LOCAL it is useful to pass it. We define this variable to + # pass to these packages' ./configure scripts. When using the system + # GMP its value is just blank (for many of these packages passing + # --with-gmp without an argument is actually a bug) + export SAGE_CONFIGURE_GMP="--with-gmp=$SAGE_GMP_PREFIX" +fi + +# The MPFR case is very close to the GMP case above +# This is usually blank if the system MPFR is used, or $SAGE_LOCAL otherwise +export SAGE_MPFR_PREFIX="@SAGE_MPFR_PREFIX@" +if [ -n "$SAGE_MPFR_PREFIX" ]; then + # Some packages that depend on MPFR accept a --with-mpfr= flag to + # their ./configure scripts. Thus we deal with this just as with GMP above. + export SAGE_CONFIGURE_MPFR="--with-mpfr=$SAGE_MPFR_PREFIX" +fi + +# The MPC case is very close to the MPFR case above +# This is usually blank if the system MPC is used, or $SAGE_LOCAL otherwise +export SAGE_MPC_PREFIX="@SAGE_MPC_PREFIX@" +if [ -n "$SAGE_MPC_PREFIX" ]; then + # Some packages that depend on MPC accept a --with-mpc= flag to + # their ./configure scripts. Thus we deal with this just as with GMP above. + export SAGE_CONFIGURE_MPC="--with-mpc=$SAGE_MPC_PREFIX" +fi + +# Location of system crti.o, in case we build our own gcc +export SAGE_CRTI_DIR="@SAGE_CRTI_DIR@" + +# This is usually blank if the system NTL is used, or $SAGE_LOCAL otherwise +export SAGE_NTL_PREFIX="@SAGE_NTL_PREFIX@" +if [ -n "$SAGE_NTL_PREFIX" ]; then + # Many packages that depend on NTL accept a --with-ntl= flag to + # their ./configure scripts. When using the system's NTL this is not + # generally necessary, but when using the NTL package installed in + # SAGE_LOCAL it is useful to pass it. + export SAGE_CONFIGURE_NTL="--with-ntl=$SAGE_NTL_PREFIX" +fi + +# The FLINT case is very close to the MPFR case above +# This is usually blank if the system FLINT is used, or $SAGE_LOCAL otherwise +export SAGE_FLINT_PREFIX="@SAGE_FLINT_PREFIX@" +if [ -n "$SAGE_FLINT_PREFIX" ]; then + # Some packages that depend on FLINT accept a --with-flint= flag to + # their ./configure scripts. Thus we deal with this just as with GMP above. + export SAGE_CONFIGURE_FLINT="--with-flint=$SAGE_FLINT_PREFIX" +fi + +# This is usually blank if the system PARI is used, or $SAGE_LOCAL otherwise +export SAGE_PARI_PREFIX="@SAGE_PARI_PREFIX@" +if [ -n "$SAGE_PARI_PREFIX" ]; then + # Some packages that depend on PARI accept a --with-pari= flag to + # their ./configure scripts. Thus we deal with this just as with GMP above. + export SAGE_CONFIGURE_PARI="--with-pari=$SAGE_PARI_PREFIX" +fi +export SAGE_PARI_CFG="@SAGE_PARI_CFG@" + +export SAGE_GLPK_PREFIX="@SAGE_GLPK_PREFIX@" +export SAGE_FREETYPE_PREFIX="@SAGE_FREETYPE_PREFIX@" +export SAGE_SUITESPARSE_PREFIX="@SAGE_SUITESPARSE_PREFIX@" + +export SAGE_CONFIGURE_FFLAS_FFPACK="@SAGE_CONFIGURE_FFLAS_FFPACK@" diff --git a/src/bin/sage-clone-source b/build/bin/sage-clone-source similarity index 98% rename from src/bin/sage-clone-source rename to build/bin/sage-clone-source index bb826ca920f..6720ea7391d 100755 --- a/src/bin/sage-clone-source +++ b/build/bin/sage-clone-source @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh ######################################################################## # Helper script for sdist diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers index d24a4fd08de..a4a8c88a29c 100644 --- a/build/bin/sage-dist-helpers +++ b/build/bin/sage-dist-helpers @@ -140,7 +140,7 @@ sdh_check_vars() { while [ -n "$1" ]; do [ -n "$(eval "echo "\${${1}+isset}"")" ] || sdh_die << _EOF_ ${1} undefined ... exiting -Maybe run 'sage --sh'? +Maybe run 'sage --buildsh'? _EOF_ shift done @@ -184,6 +184,11 @@ sdh_make() { } +sdh_make_check() { + echo "Checking $PKG_NAME" + ${MAKE:-make} check "$@" || sdh_die "Failures checking $PKG_NAME" +} + sdh_make_install() { echo "Installing $PKG_NAME" if [ -n "$SAGE_DESTDIR" ]; then diff --git a/build/bin/sage-get-system-packages b/build/bin/sage-get-system-packages new file mode 100755 index 00000000000..47e08301052 --- /dev/null +++ b/build/bin/sage-get-system-packages @@ -0,0 +1,19 @@ +#!/bin/sh +SYSTEM=$1 +if [ -z "$SYSTEM" ]; then + echo >&2 "usage: $0 {debian|arch|conda|...} SPKGS..." + exit 1 +fi +shift +SPKGS="$*" +# +if [ -z "$SAGE_ROOT" ]; then + SAGE_ROOT=`pwd` +fi +STRIP_COMMENTS="sed s/#.*//;" +for PKG_BASE in $SPKGS; do + SYSTEM_PACKAGES_FILE=$SAGE_ROOT/build/pkgs/$PKG_BASE/distros/$SYSTEM.txt + if [ -f $SYSTEM_PACKAGES_FILE ]; then + echo $(${STRIP_COMMENTS} $SYSTEM_PACKAGES_FILE) + fi +done diff --git a/build/bin/sage-guess-package-system b/build/bin/sage-guess-package-system new file mode 100755 index 00000000000..32a97330744 --- /dev/null +++ b/build/bin/sage-guess-package-system @@ -0,0 +1,37 @@ +#!/bin/sh +# +# First test for user-installable package systems, then system package systems +if conda --version > /dev/null 2>&1; then + if [ -z "$CONDA_DEFAULT_ENV" ]; then + echo >&2 -n "(ignoring conda because no environment is active) " + else + echo conda + exit + fi +fi +if brew --version > /dev/null 2>&1; then + echo homebrew +elif emerge --version > /dev/null 2>&1; then + echo gentoo +elif apt-get --version > /dev/null 2>&1; then + echo debian +elif yum --version > /dev/null 2>&1; then + echo fedora +elif pacman --version > /dev/null 2>&1; then + echo arch +elif slackpkg --version > /dev/null 2>&1; then + echo slackware +elif zypper --version > /dev/null 2>&1; then + echo opensuse +elif apk --version > /dev/null 2>&1; then + echo alpine +else + case `uname -s` in + CYGWIN*) + echo cygwin + ;; + *) + echo unknown + ;; + esac +fi diff --git a/build/bin/sage-logger b/build/bin/sage-logger index d920ca72a33..14ab1297022 100755 --- a/build/bin/sage-logger +++ b/build/bin/sage-logger @@ -32,6 +32,7 @@ fi cmd="$1" logfile="$2" +export SAGE_LOGFILE="$logfile" logname="$(basename $logfile .log)" logdir=`dirname "$logfile"` @@ -77,7 +78,9 @@ if [ -n "$SAGE_SILENT_BUILD" -a ${use_prefix} = true ]; then ( exec>> $logfile 2>&1 ; eval "$cmd" ) status=$? if [[ $status != 0 ]]; then - echo " [$logname] error installing, exit status $status. Log file: $logfile" + echo " [$logname] error installing, exit status $status. End of log file:" + tail -n 40 "$logfile" | sed "/Please email sage-devel/,$ d;s;^; [$logname] ;" >&2 + echo " [$logname] Full log file: $logfile" else echo " [$logname] successfully installed." fi diff --git a/build/bin/sage-pip-install b/build/bin/sage-pip-install index e69eec28ae1..2ff9bd43d3e 100755 --- a/build/bin/sage-pip-install +++ b/build/bin/sage-pip-install @@ -38,11 +38,13 @@ fi # Note: We need to take care to specify the full path to Sage's Python here # to emphasize that this command hould use it, and not the system Python; # see https://trac.sagemath.org/ticket/18438 +# But now we delegate this to sage-python23. +PYTHON=sage-python23 + +# The PIP variable is only used to determine the name of the lock file. if [ "$SAGE_PYTHON3" = yes ]; then - PYTHON="$SAGE_LOCAL/bin/python3" PIP=pip3 else - PYTHON="$SAGE_LOCAL/bin/python2" PIP=pip2 fi diff --git a/build/bin/sage-print-system-package-command b/build/bin/sage-print-system-package-command new file mode 100755 index 00000000000..ccbb4986f17 --- /dev/null +++ b/build/bin/sage-print-system-package-command @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# +system=$1 +shift +IF_VERBOSE=: +SUDO= +PROMPT= +while : +do + case "$1" in + --verbose) + IF_VERBOSE= + ;; + --sudo) + # Whether to print sudo for package managers that need sudo for non-root users + SUDO="sudo " + ;; + --prompt) + PROMPT=' $ ' + ;; + -*) + echo >&2 "$0: unknown option $2" + exit 1 + ;; + *) + 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 +system_packages="$*" +shopt -s extglob +case $system:$command in + 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 "${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" + ;; + gentoo*:install) + [ -n "$system_packages" ] && echo "${PROMPT}${SUDO}emerge $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-python23 b/build/bin/sage-python23 index 25abc6b1034..83a5668def4 100755 --- a/build/bin/sage-python23 +++ b/build/bin/sage-python23 @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # Run the Python interpreter that we are currently building Sage with diff --git a/src/bin/sage-sdist b/build/bin/sage-sdist similarity index 100% rename from src/bin/sage-sdist rename to build/bin/sage-sdist diff --git a/build/bin/sage-site b/build/bin/sage-site new file mode 100755 index 00000000000..44d8e3acff9 --- /dev/null +++ b/build/bin/sage-site @@ -0,0 +1,169 @@ +#!/usr/bin/env bash +# Handle options of the src/bin/sage script that pertain to SAGE_ROOT or sage-the-distribution. + +usage() { + echo + #### 1.......................26..................................................78 + #### |.....................--.|...................................................| + echo "Sage-the-distribution options:" + echo " --optional -- list all optional packages that can be installed" + echo " --experimental -- list all experimental packages that can be installed" + echo " --info [packages] -- print the SPKG.txt or SPKG.rst of the given packages," + echo " and some additional information." + echo " -i [packages] -- install the given Sage packages" + echo " --upgrade [version] -- download, build and install the given version. Here," + echo " 'version' is a git branch or tag name. Useful values" + echo " are 'master' (the current development version, this" + echo " is the default) or a version number like '5.13'." +} + +usage_advanced() { + echo + #### 1.......................26..................................................78 + #### |.....................--.|...................................................| + echo "Building the Sage library:" + echo + echo " -b -- build Sage library -- do this if you have" + echo " modified any source code files in SAGE_ROOT/src/sage/" + echo " -ba -- same as -b, but rebuild *all* Cython" + echo " code." + echo " -br -- build and run Sage" + echo + echo " -bt [...] -- build Sage and test; same options as -t" + echo " -btp [...] -- build Sage and test in parallel; same options as -tp" + echo " -btnew [...] -- build Sage and test modified files, as in -t --new" + echo + echo " -bn [...], --build-and-notebook [...]" + echo " -- build the Sage library (as by running \"sage -b\")" + echo " and then start the notebook" + echo + echo "Package handling:" + echo + echo " --package [args] -- call the package manager with given arguments." + echo " Run without arguments for help." + echo " --experimental -- list all experimental packages that can be installed" + echo " -i [opts] [pkgs] -- install the given Sage packages. Options:" + echo " -c -- run the packages' test suites," + echo " overriding the settings of" + echo " SAGE_CHECK and SAGE_CHECK_PACKAGES" + echo " -d -- only download, do not install packages" + echo " -f -- force build: install the packages even" + echo " if they are already installed" + echo " -s -- do not delete the temporary build directories" + echo " after a successful build" + echo " -y -- reply yes to prompts about experimental" + echo " and old-style packages; warning: there" + echo " is no guarantee that these packages will" + echo " build correctly; use at your own risk" + echo " -n -- reply no to prompts about experimental" + echo " and old-style packages" + echo " -f [opts] [pkgs] -- shortcut for -i -f: force build of the given Sage" + echo " packages" + echo " -p [opts] [packages]-- install the given Sage packages, without dependency" + echo " checking. Options are the same as for the -i command." + echo " --location -- if needed, fix paths to make Sage relocatable" + echo " --optional -- list all optional packages that can be installed" + echo " --standard -- list all standard packages that can be installed" + echo " --installed -- list all installed packages" + echo + echo "Upgrading:" + echo + echo " --upgrade [version] -- download, build and install the given version. Here," + echo " 'version' is a git branch or tag name. Useful values" + echo " are 'master' (the current development version, this" + echo " is the default) or a version number like '5.13'." + + echo + #### 1.......................26..................................................78 + #### |.....................--.|...................................................| + echo "Making Sage distributions:" + echo + echo " --sdist -- build a source distribution of Sage" + echo + echo "Building the documentation:" + echo + echo " --docbuild [lang/] -- Build the Sage documentation" + echo + echo "Other developer tools:" + echo + echo " --root -- print the Sage root directory" + echo " --git-branch -- print the current git branch" + echo " --buildsh [...] -- run a shell with Sage environment variables" + echo " as they are set while building Sage and its packages" + echo + #### 1.......................26..................................................78 + #### |.....................--.|...................................................| +} + +if [ "$1" = '-h' -o "$1" = '-?' -o "$1" = '-help' -o "$1" = '--help' ]; then + usage + exit 0 +fi +if [ "$1" = "-advanced" -o "$1" = "--advanced" ]; then + usage_advanced + exit 0 +fi + +##################################################################### +# Package handling +##################################################################### + +if [ "$1" = '-package' -o "$1" = "--package" ]; then + shift + exec sage-package $@ +fi + +if [ "$1" = '-optional' -o "$1" = "--optional" ]; then + shift + exec sage-list-packages optional $@ +fi + +if [ "$1" = '-experimental' -o "$1" = "--experimental" ]; then + shift + exec sage-list-packages experimental $@ +fi + +if [ "$1" = '-standard' -o "$1" = "--standard" ]; then + shift + exec sage-list-packages standard $@ +fi + +if [ "$1" = '-installed' -o "$1" = "--installed" ]; then + shift + exec sage-list-packages all --installed-only $@ +fi + +if [ "$1" = '-p' ]; then + echo "Error: Installing old-style SPKGs is no longer supported." + exit 1 +fi + +if [ "$1" = '-info' -o "$1" = '--info' ]; then + shift + for PKG in "$@" + do + sage-spkg --info "$PKG" || exit $? + done + exit 0 +fi + +##################################################################### +# Building the documentation. +##################################################################### + +if [ "$1" = "-docbuild" -o "$1" = "--docbuild" ]; then + # Redirect stdin from /dev/null. This helps with running TeX which + # tends to ask interactive questions if something goes wrong. These + # cause the build to hang. If stdin is /dev/null, TeX just aborts. + shift + export LANG=C # to ensure it is possible to scrape out non-EN locale warnings + exec sage-python -m sage_setup.docbuild "$@" ... +# sage {-i|-p} ... # # Options can be: # -s: do not delete temporary build directory # -k: do not uninstall existing installation of this package before # installing; instead simply overwrite existing files. # -c: after installing, run the test suite for the spkg. This should -# override the settings of SAGE_CHECK and SAGE_CHECK_PACKAGES. +# override the settings of SAGE_CHECK. +# Exit with an error if the test suite fails. +# -w: after installing, run the test suite for the spkg. This should +# override the settings of SAGE_CHECK. +# Print a warning if the test suite fails. # -d: only download the package # -y: automatically reply "y" for all prompts regarding # experimental and old-style packages @@ -64,21 +68,24 @@ #***************************************************************************** # Avoid surprises with character ranges [a-z] in regular expressions -export LC_ALL=C +# See Trac #15791; some locales can produce different results for +# character ranges (use C.UTF-8 to ensure UTF-8 default encoding in Python) +export LC_ALL=C.UTF-8 usage() { cat < +Usage: sage {-i|-p} -If is a URL, download and install it. If it is a file -name, install it. Otherwise, search Sage's list of packages (see -'sage --package list') for a matching package, and if a match is -found, install it. +Search Sage's list of packages (see 'sage --package list') for a +matching package, and if a match is found, install it. Options: -s: do not delete the temporary build directory - -c: after installing, run the test suite for the package + -c: after installing, run the test suite for the package; + exit with an error on test suite failures + -w: after installing, run the test suite for the package; + print a warning on test suite failures -d: only download the package -y: automatically reply "y" for all prompts regarding experimental and old-style packages; warning: there @@ -86,6 +93,8 @@ Options: use at your own risk" -n: automatically reply "n" for all prompts regarding experimental and old-style packages + -o: allow fetching the package from its upstream URL + when it is not available from the Sage mirrors (yet) EOF } @@ -112,7 +121,7 @@ If you want to try to fix the problem yourself, *don't* just cd to `pwd` and type '$2' or whatever is appropriate. Instead, the following commands setup all environment variables correctly and load a subshell for you to debug the error: - (cd '`pwd`' && '$SAGE_ROOT/sage' --sh) + (cd '`pwd`' && '$SAGE_ROOT/sage' --buildsh) When you are done debugging, you can type "exit" to leave the subshell. MESSAGE fi @@ -157,6 +166,7 @@ fi # The following sets environment variables for building packages. # Since this is sourced, it returns a non-zero value on errors rather # than exiting. Using dot suggested by W. Cheung. +. sage-env-config . sage-env @@ -218,10 +228,13 @@ while true; do -s) export SAGE_KEEP_BUILT_SPKGS=yes;; -c|--check) - SAGE_CHECK_PACKAGES=x # nonempty, so not set to default later export SAGE_CHECK=yes;; + -w|--check-warning-only) + export SAGE_CHECK=warn;; -k|--keep-existing) KEEP_EXISTING=yes;; + -o|--allow-upstream) + SAGE_DOWNLOAD_FILE_OPTIONS+=" --allow-upstream";; -*) echo >&2 "Error: unknown option '$1'" exit 2;; @@ -235,37 +248,16 @@ done # Figure out the package filename, download it if needed. ################################################################## # One should be able to install a package using -# sage -i where can be any of the -# following values: -# -# 1a. /path/to/-x.y.z.spkg, i.e. the package is found somewhere -# in your file system and you're giving an absolute path. -# 1b. relative/path/to/-x.y.z.spkg, the same with a relative -# path. -# 2a. -x.y.z, i.e. the name of the package plus the package's -# version numbers. -# 2b. -x.y.z.spkg, i.e. the name of the package in addition to -# the version numbers and the ".spkg" extension. -# 3. , i.e. the name of the package without a version number. -# 4. /-x.y.z.spkg, i.e. the full URL where the package -# is hosted. Any local packages matching are ignored. -# -# In cases 2a, 2b and 3 we first look locally inside spkg/* for a -# matching package. Otherwise, we try to download it. In all cases, -# we reduce to case 1a. -# -# See #7544 and #12602. -# +# sage -i PKG_SRC="$1" # Does PKG_SRC contain a slash? if echo "$PKG_SRC" | grep / >/dev/null; then - PKG_HAS_PATH=yes + echo >&2 "Error: Installing old-style SPKGs is no longer supported" + exit 1 fi -# PKG_NAME is the last path component without .spkg -# This already reduces case 2b to case 2a. -PKG_NAME=`basename "$PKG_SRC" | sed 's/\.spkg$//'` -PKG_BASE=`echo "$PKG_NAME" | sed 's/-.*//'` +PKG_NAME="$PKG_SRC" +PKG_BASE=`echo "$PKG_NAME" | sed 's/-.*//'` # strip version number # USE_LOCAL_SCRIPTS is a flag that if non-empty will cause # this script to try to install the package using local metadata @@ -273,39 +265,31 @@ PKG_BASE=`echo "$PKG_NAME" | sed 's/-.*//'` # the value of this flag is set in the next codeblock USE_LOCAL_SCRIPTS= -if [ -f "$PKG_SRC" ]; then - # PKG_SRC is a file. If it is given by a relative path, prepend `pwd` - # (reduce case 1b to 1a) - if ! echo "$PKG_SRC" | grep '^/' >/dev/null; then - PKG_SRC="`pwd`/$PKG_SRC" - fi -elif [ -z "$PKG_HAS_PATH" ]; then - # If PKG_SRC is not an existing file and doesn't contain a slash, - # we are in case 2a or 3. If version in 2a matches the version in - # build/pkgs or we are in case 3 use the local scripts, otherwise - # we try to find a package in upstream - PKG_VER="${PKG_NAME#${PKG_BASE}}" - PKG_VER="${PKG_VER#-}" - PKG_SCRIPTS="$SAGE_ROOT/build/pkgs/$PKG_BASE" - LOCAL_PKG_VER=`cat $PKG_SCRIPTS/package-version.txt 2>/dev/null` - if [ -n "$LOCAL_PKG_VER" ] && [ -z "$PKG_VER" -o "$PKG_VER" = "$LOCAL_PKG_VER" ]; then - PKG_VER="$LOCAL_PKG_VER" - if [ -z "$PKG_VER" ]; then - PKG_NAME="${PKG_BASE}" - else - PKG_NAME="${PKG_BASE}-${PKG_VER}" - fi - USE_LOCAL_SCRIPTS=yes - PKG_BASE_VER=`echo $PKG_VER | sed 's/\.p[0-9][0-9]*$//'` - PKG_NAME_UPSTREAM=`lookup_param tarball "$PKG_SCRIPTS/checksums.ini" | sed "s/VERSION/$PKG_BASE_VER/"` - echo "Found local metadata for $PKG_NAME" - - # Warning for experimental packages - if [ x`cat "$PKG_SCRIPTS/type"` = x"experimental" -a $INFO = 0 ]; then - if [ $YES != 1 ]; then - # We use /dev/tty here because our output may be redirected - # to a logfile, or line-buffered. - write_to_tty </dev/null` +PKG_VER="$LOCAL_PKG_VER" +if [ -z "$PKG_VER" ]; then + PKG_NAME="${PKG_BASE}" +else + PKG_NAME="${PKG_BASE}-${PKG_VER}" +fi +USE_LOCAL_SCRIPTS=yes +PKG_BASE_VER=`echo $PKG_VER | sed 's/\.p[0-9][0-9]*$//'` +PKG_NAME_UPSTREAM=`lookup_param tarball "$PKG_SCRIPTS/checksums.ini" | sed "s/VERSION/$PKG_BASE_VER/"` +echo "Found local metadata for $PKG_NAME" + +# Warning for experimental packages +if [ x`cat "$PKG_SCRIPTS/type"` = x"experimental" -a $INFO = 0 ]; then + if [ $YES != 1 ]; then + # We use /dev/tty here because our output may be redirected + # to a logfile, or line-buffered. + write_to_tty < /dev/tty 2>&1 - else - answer=n - fi - case "$answer" in - n*|N*) exit 1;; - esac - # Confirm the user's input. (This gives important - # feedback to the user when output is redirected to a logfile.) - echo > /dev/tty "OK, installing $PKG_NAME now..." - fi + if [ $? -ne 0 ]; then + echo "Terminal not available for prompting. Use 'sage -i -y $PKG_BASE'" + echo "to install experimental packages in non-interactive mode." + YES=-1 fi - - else - cd "$SAGE_DISTFILES" - for spkg in `ls -1t ${PKG_NAME}.spkg ${PKG_NAME}-*.spkg 2>/dev/null`; do - if [ -f "$spkg" ]; then - # Found a good package - echo "Found package $PKG_NAME in $SAGE_DISTFILES/$spkg" - PKG_SRC="`pwd`/$spkg" - PKG_NAME=`basename "$spkg" | sed 's/\.spkg$//'` - break - fi - done + if [ $YES != -1 ]; then + read -p "Are you sure you want to continue [Y/n]? " answer < /dev/tty > /dev/tty 2>&1 + else + answer=n + fi + case "$answer" in + n*|N*) exit 1;; + esac + # Confirm the user's input. (This gives important + # feedback to the user when output is redirected to a logfile.) + echo > /dev/tty "OK, installing $PKG_NAME now..." fi fi if [ $INFO -ne 0 -a "$USE_LOCAL_SCRIPTS" = yes ]; then - cat "$PKG_SCRIPTS/SPKG.txt" - if [ -r "$PKG_SCRIPTS/type" ] ; then - echo - echo "== Type ==" - echo - cat "$PKG_SCRIPTS/type" - echo - fi - echo "== Equivalent System Packages ==" - echo - PKG_DISTROS="$PKG_SCRIPTS"/distros - for system_package_file in "$PKG_DISTROS"/debian*.txt "$PKG_DISTROS"/fedora*.txt "$PKG_DISTROS"/*conda*.txt "$PKG_DISTROS"/homebrew*.txt "$PKG_DISTROS"/arch*.txt ; do - if [ -f "$system_package_file" ]; then - system=$(basename "$system_package_file" .txt) - system_packages="$(echo $(sed 's/#.*//;' $system_package_file))" - case $system in - debian) - # Generic - echo "Debian/Ubuntu:" - echo " sudo apt-get install $system_packages" - ;; - debian*|ubuntu*) - # Specific distribution - echo "$system:" - echo " sudo apt-get install $system_packages" - ;; - fedora) - # Generic - echo "Fedora/Redhat/CentOS:" - echo " sudo yum install $system_packages" - ;; - fedora*|redhat*|centos*) - # Specific distribution - echo "$system:" - echo " sudo yum install $system_packages" - ;; - arch*) - echo "$system:" - echo " sudo pacman -S $system_packages" - ;; - *conda*) - echo "$system:" - echo " conda install $system_packages" - ;; - homebrew*) - echo "$system:" - echo " brew install $system_packages" - ;; - *) - echo "$system:" - echo " install the following packages: $system_packages" - ;; - esac - fi - done - if [ -z "$system" ]; then - echo "(none known)" - else - echo - if [ -f "$PKG_SCRIPTS"/spkg-configure.m4 ]; then - echo "If the system package is installed, ./configure will check whether it can be used." - else - echo "However, these system packages will not be used for building Sage" - echo "because spkg-configure.m4 has not been written for this package;" - echo "see https://trac.sagemath.org/ticket/27330" - fi - fi - echo - exit 0 + exec sage-spkg-info $PKG_BASE fi # If we haven't found the package yet, we must download it if [ ! -f "$PKG_SRC" ]; then if [ -n "$PKG_NAME_UPSTREAM" ]; then # This is the new-style package way of downloading the tarball - if ! sage-download-file "$PKG_NAME_UPSTREAM"; then + if ! sage-download-file $SAGE_DOWNLOAD_FILE_OPTIONS "$PKG_NAME_UPSTREAM"; then error_msg "Error downloading $PKG_NAME_UPSTREAM" exit 1 fi PKG_SRC="$SAGE_DISTFILES/$PKG_NAME_UPSTREAM" else - # Handle all the legacy cruft. This branch can be deleted once - # we get rid of old-style spkgs - if [ $YES = -1 ]; then - # User provided -n option, so don't even try to download the package" - echo "Old-style packages disabled by use of '-n' option" - exit 1 - fi - if [ $INFO -eq 0 ]; then - echo "Attempting to download package $PKG_NAME" - else - echo "Attempting to get on-line info for package $PKG_NAME" - fi - - # Reduce everything to case 4: full URL. - if [ -n "$PKG_HAS_PATH" ]; then - PKG_URL="$PKG_SRC" - else - # Handle cases 2a and 3, where the package name is something - # like "foo" or "foo-1.2.3". - MIRROR=$(sage-download-file --print-fastest-mirror)/spkg - if [ $? -ne 0 ]; then - error_msg "Error downloading list of packages" - exit 1 - fi - for repo in optional experimental huge; do - # Download the list of packages. - echo ">>> Checking online list of $repo packages." - # File inside DOT_SAGE should be writable - repolist="${DOT_SAGE}/${repo}.list" - sage-download-file --quiet "$MIRROR/$repo/list" $repolist - if [ $? -ne 0 ]; then - rm -f $repolist - error_msg "Error downloading $MIRROR/$repo/list" - exit 1 - fi - - # The contrived sed commands print out either ${PKG_NAME} if - # it appears as a complete line or some string starting with - # ${PKG_NAME}- whichever occurs first. - # Tested with GNU sed, BSD sed (on OS X) and Solaris sed. - pkg=`sed -n -f <( echo "/^${PKG_NAME}\$/{p;q;}" && echo "/^${PKG_NAME}-/{p;q;}" ) $repolist` - rm -f $repolist - if [ -n "$pkg" ]; then - echo ">>> Found $pkg" - PKG_NAME=$pkg - - # If INFO is set, try downloading only the .txt file - if [ $INFO -eq 1 ]; then - PKG_URL="$MIRROR/$repo/$pkg.txt" - sage-download-file --quiet "$PKG_URL" && exit 0 - # If the download failed (for whatever reason), - # fall through and use the .spkg file. - else - if [ $YES != 1 ]; then - # Warn and ask the user if downloading an - # experimental package. - # Add a deprecation note for other packages, - # since old-style packages are deprecated. - if [ $repo = experimental ]; then - write_to_tty < /dev/tty 2>&1 - else - answer=n - fi - case "$answer" in - n*|N*) exit 1;; - esac - else - # Deprecated since Sage 6.9, Trac #19158 - write_to_tty < /dev/tty 2>&1 - elif [ $YES = -1 ]; then - answer=n - else - answer=y - fi - case "$answer" in - n*|N*) exit 1;; - esac - fi - # Confirm the user's input. (This gives important - # feedback to the user when output is redirected to a logfile.) - echo > /dev/tty "OK, installing $PKG_NAME now..." - fi - fi - PKG_URL="$MIRROR/$repo/$pkg.spkg" - break - fi - done - - if [ -z "$PKG_URL" ]; then - echo >&2 "Error: could not find a package matching $PKG_NAME" - echo >&2 " Try 'sage --package list' to see the available packages" - echo >&2 " $(sage-package apropos $PKG_NAME)" - exit 1 - fi - fi - - # Trac #5852: check write permissions - mkdir -p "$SAGE_DISTFILES" - if [ ! -w "$SAGE_DISTFILES" ]; then - error_msg "Error: no write access to packages directory $SAGE_PACKAGES" - exit 1 - fi - cd "$SAGE_DISTFILES" || exit $? - - # Download to a temporary file (such that we don't end up with a - # corrupted .spkg file). - PKG_TMP="${PKG_URL##*/}.tmp" - echo ">>> Trying to download $PKG_URL" - sage-download-file "$PKG_URL" "$PKG_TMP" - if [ $? -ne 0 ]; then - # Delete failed download - rm -f "$PKG_TMP" - error_msg "Error downloading $PKG_URL" - exit 1 - fi - - PKG_SRC="`pwd`/${PKG_URL##*/}" - mv -f "$PKG_TMP" "$PKG_SRC" + echo >&2 "Error: Installing old-style SPKGs is no longer supported" + exit 1 fi fi @@ -688,21 +444,8 @@ if [ "$USE_LOCAL_SCRIPTS" = yes ]; then exit 1 fi else - # Old-style package (deprecated) - echo "Extracting package $PKG_SRC" - ls -l "$PKG_SRC" - - sage-uncompress-spkg "$PKG_SRC" - if [ $? -ne 0 ]; then - error_msg "Error: failed to extract $PKG_SRC" - exit 1 - fi - - cd "$PKG_NAME" - if [ $? -ne 0 ]; then - error_msg "Error: after extracting, the directory '$PKG_NAME' does not exist" - exit 1 - fi + echo >&2 "Error: Installing old-style SPKGs is no longer supported." + exit 1 fi echo "Finished extraction" @@ -727,7 +470,7 @@ write_script_wrapper() { trap "echo >&2 Error: Unexpected error writing wrapper script for $script; exit \$_" ERR - if head -1 "$script" | grep '^#!.*$' >/dev/null; then + if head -1 "$script.in" | grep '^#!.*$' >/dev/null; then echo >&2 "Error: ${script##*/} should not contain a shebang line; it will be prepended automatically." exit 1 fi @@ -746,7 +489,7 @@ export PKG_NAME="$PKG_NAME" export PKG_BASE="$PKG_BASE" export PKG_VER="$PKG_VER" -for lib in "\$SAGE_ROOT/build/bin/sage-dist-helpers" "\$SAGE_SRC/bin/sage-env" ; do +for lib in "\$SAGE_ROOT/build/bin/sage-dist-helpers" "\$SAGE_SRC/bin/sage-env-config" "\$SAGE_SRC/bin/sage-env" "\$SAGE_ROOT/build/bin/sage-build-env-config"; do source "\$lib" if [ \$? -ne 0 ]; then echo >&2 "Error: failed to source \$lib" @@ -769,7 +512,7 @@ fi __EOF__ - cat "$script" >> "$tmpscript" + cat "$script.in" >> "$tmpscript" mv "$tmpscript" "$script" chmod +x "$script" @@ -793,21 +536,8 @@ for script in $WRAPPED_SCRIPTS; do script="spkg-$script" - if [ -f "$script" ]; then + if [ -f "$script.in" ]; then if [ "$USE_LOCAL_SCRIPTS" = "yes" ]; then - if [ -x "$script" ]; then - msg="$script should not be marked executable in the build/pkgs directory" - if [ "$UNAME" = "CYGWIN" ]; then - # On Cygwin we can't necessarily rely on file permissions - # being sane, so just issue a warning; on other platforms - # this should be enforced as an error - echo >&2 "WARNING: $msg" - else - error_msg "$msg" - exit 1 - fi - fi - write_script_wrapper "$(pwd)/$script" "$script_dir" else if [ ! -x "$script" ]; then @@ -836,25 +566,6 @@ if [ ! -f spkg-install ]; then chmod +x spkg-install fi - -# SAGE_CHECK_PACKAGES: if this contains "!pkg", skip tests for "pkg", -# so set SAGE_CHECK=no. If this contains "pkg", run tests, so set -# SAGE_CHECK=yes. Check this now and export SAGE_CHECK so that the -# package's spkg-install script can use this variable. -# -# Since Python's self-tests seem to fail on all platforms, we disable -# its test suite by default. -if [ -z "$SAGE_CHECK_PACKAGES" ]; then - SAGE_CHECK_PACKAGES='!python2,!python3' -fi -# Allow spaces, commas, or colons as separator (the documentation suggests commas). -if echo ",$SAGE_CHECK_PACKAGES," | grep -i "[ ,:]\!$PKG_BASE[ ,:]" > /dev/null ; then - export SAGE_CHECK=no -elif echo ",$SAGE_CHECK_PACKAGES," | grep -i "[ ,:]$PKG_BASE[ ,:]" > /dev/null ; then - export SAGE_CHECK=yes -fi - - echo "****************************************************" echo "Host system:" uname -a @@ -912,6 +623,9 @@ if [ "$KEEP_EXISTING" != "yes" ]; then sage-spkg-uninstall "$PKG_BASE" fi +# To work around #26996: Create lib and set a symlink so that writes into lib64/ end up in lib/ +(mkdir -p "$SAGE_DESTDIR_LOCAL/lib" && cd "$SAGE_DESTDIR_LOCAL" && ln -sf lib lib64) + # Run the pre-install script, if any if [ -f spkg-preinst ]; then echo "Running pre-install script for $PKG_NAME." @@ -943,6 +657,9 @@ else fi fi +# To work around #26996: Remove the symlink set, or we get "cp: cannot overwrite directory" +rm -f "$SAGE_DESTDIR_LOCAL/lib64" + # All spkgs should eventually support this, but fall back on old behavior in # case DESTDIR=$SAGE_DESTDIR installation was not used echo "Copying package files from temporary location $SAGE_DESTDIR to $SAGE_LOCAL" @@ -1032,15 +749,22 @@ fi # spkg-check scripts were written before use of DESTDIR installs, and so # fail in many cases. This might be good to change later. -if [ "$SAGE_CHECK" = "yes" ]; then +if [ "$SAGE_CHECK" = "yes" -o "$SAGE_CHECK" = "warn" ]; then if [ -f spkg-check ]; then echo "Running the test suite for $PKG_NAME..." time ./spkg-check if [ $? -ne 0 ]; then - error_msg "Error testing package $PKG_NAME" "make check" - exit 1 + TEST_SUITE_RESULT="failed" + if [ "$SAGE_CHECK" = "warn" ]; then + error_msg "Warning: Failures testing package $PKG_NAME (ignored)" "make check" + else + error_msg "Error testing package $PKG_NAME" "make check" + exit 1 + fi + else + TEST_SUITE_RESULT="passed" + echo "Passed the test suite for $PKG_NAME." fi - TEST_SUITE_RESULT="passed" else echo "Package $PKG_NAME has no test suite." TEST_SUITE_RESULT="not available" @@ -1100,4 +824,4 @@ fi touch "$SAGE_LOCAL/lib/sage-force-relocate.txt" -echo "Finished installing $PKG_NAME.spkg" +echo "Finished installing $PKG_NAME" diff --git a/build/bin/sage-spkg-info b/build/bin/sage-spkg-info new file mode 100755 index 00000000000..9575df7f20e --- /dev/null +++ b/build/bin/sage-spkg-info @@ -0,0 +1,59 @@ +#!/bin/sh +# +# sage-spkg-info SPKG +# Format information about a Sage package +# +# Assumes SAGE_ROOT is set +PKG_BASE=$1 +PKG_SCRIPTS="$SAGE_ROOT/build/pkgs/$PKG_BASE" +for ext in rst txt; do + SPKG_FILE="$PKG_SCRIPTS/SPKG.$ext" + if [ -f "$SPKG_FILE" ]; then + cat "$SPKG_FILE" + break + fi +done +if [ -r "$PKG_SCRIPTS/type" ] ; then + echo + echo "== Type ==" + echo + cat "$PKG_SCRIPTS/type" + echo +fi +echo "== Equivalent System Packages ==" +echo +PKG_DISTROS="$PKG_SCRIPTS"/distros +for system_package_file in "$PKG_DISTROS"/*.txt; do + if [ -f "$system_package_file" ]; then + system=$(basename "$system_package_file" .txt) + system_packages="$(echo $(sed 's/#.*//;' $system_package_file))" + case $system in + debian) + # Generic + echo "Debian/Ubuntu:" + ;; + fedora) + # Generic + echo "Fedora/Redhat/CentOS:" + ;; + *) + echo "$system:" + ;; + esac + echo -n " " + sage-print-system-package-command $system --prompt --sudo install $system_packages + fi +done +if [ -z "$system" ]; then + echo "(none known)" +else + echo + if [ -f "$PKG_SCRIPTS"/spkg-configure.m4 ]; then + echo "If the system package is installed, ./configure will check whether it can be used." + else + echo "However, these system packages will not be used for building Sage" + echo "because spkg-configure.m4 has not been written for this package;" + echo "see https://trac.sagemath.org/ticket/27330" + fi +fi +echo diff --git a/src/bin/sage-starts b/build/bin/sage-starts similarity index 98% rename from src/bin/sage-starts rename to build/bin/sage-starts index 64d9b29f0de..1adcdccaa06 100755 --- a/src/bin/sage-starts +++ b/build/bin/sage-starts @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # Run this test from SAGE_ROOT. # If SAGE_ROOT is not defined (this will be the case when called from diff --git a/build/bin/sage-system-python b/build/bin/sage-system-python index e3d80eb4136..a72b1527df5 100755 --- a/build/bin/sage-system-python +++ b/build/bin/sage-system-python @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # Run the system python. # @@ -13,12 +13,29 @@ if [ -z "$SAGE_ORIG_PATH" ]; then SAGE_ORIG_PATH="$PATH" fi -PYTHON="$(PATH="$SAGE_ORIG_PATH" command -v python)" +# In particular, it is invoked by "bootstrap -d" for sage-download-file, +# i.e., before a configure run, and by "sage-spkg", also for sage-download-file. +# So it needs to find a python that has the urllib module. +# For example, on Debian buster, the python3-minimal package does NOT provide it. +# +# See https://trac.sagemath.org/ticket/29090 -# fallback for systems where only the python3 command is available -# see https://trac.sagemath.org/ticket/26953 -if [ -z "$PYTHON" ]; then - PYTHON="$(PATH="$SAGE_ORIG_PATH" command -v python3)" -fi +# Trac #29890: Our first choice is "python", not "python3". This is to avoid +# a defect of sage_bootstrap on macOS regarding SSL URLs. + +# Trac #30177: Also check for hashlib.sha1 to guard against broken python2 +# from old homebrew installations. Also check whether the current directory +# is accessible by this python; this is to guard on Cygwin against Pythons +# installed somewhere else in Windows. -exec "$PYTHON" "$@" +PYTHONS="python python3 python3.8 python3.7 python2.7 python3.6 python2" +for PY in $PYTHONS; do + PYTHON="$(PATH="$SAGE_ORIG_PATH" command -v $PY)" + if [ -n "$PYTHON" ]; then + if "$PYTHON" -c "import urllib; from hashlib import sha1; from os import listdir; listdir(\"$(pwd)\");" 2>/dev/null; then + exec "$PYTHON" "$@" + fi + fi +done +echo >&2 "$0: error: none of $PYTHONS is a suitable Python with urllib" +exit 1 diff --git a/build/bin/sage-venv b/build/bin/sage-venv new file mode 100755 index 00000000000..9ee9fc794be --- /dev/null +++ b/build/bin/sage-venv @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# (Actually, we invoke this script with a specific python3 determined by configure.) + +# Adapted from examples in https://docs.python.org/3/library/venv.html + +import venv +import sys +import argparse + +parser = argparse.ArgumentParser(prog=__name__, + description='Creates a virtual Python ' + 'environment in a target directory.') +parser.add_argument('env_dir', metavar='ENV_DIR', + help='A directory in which to create the' + 'virtual environment.') + +parser.add_argument('--system-site-packages', default=False, + action='store_true', dest='system_site', + help='Give the virtual environment access to the ' + 'system site-packages dir.') + +parser.add_argument('--clear', default=False, action='store_true', + dest='clear', help='Delete the contents of the ' + 'virtual environment ' + 'directory if it already ' + 'exists, before virtual ' + 'environment creation.') +parser.add_argument('--upgrade', default=False, action='store_true', + dest='upgrade', help='Upgrade the virtual ' + 'environment directory to ' + 'use this version of ' + 'Python, assuming Python ' + 'has been upgraded ' + 'in-place.') + +options = parser.parse_args() +if options.upgrade and options.clear: + raise ValueError('you cannot supply --upgrade and --clear together.') + +if sys.platform == 'cygwin': + # default for Cygwin; see https://trac.sagemath.org/ticket/30149 + use_symlinks = False +else: + # default for posix + # On macOS, definitely need symlinks=True (which matches what we test in build/pkgs/spkg-configure.m4) + # or it may fail with 'dyld: Library not loaded: @executable_path/../Python3' on macOS. + use_symlinks = True + +b = venv.EnvBuilder(system_site_packages=options.system_site, + clear=options.clear, + upgrade=options.upgrade, + symlinks=use_symlinks) +c = b.ensure_directories(options.env_dir) +b.setup_python(c) +b.create_configuration(c) +# We do not call setup_scripts, which would install the venv 'activate'/'deactivate' scripts. + diff --git a/src/bin/testcc.sh b/build/bin/testcc.sh similarity index 100% rename from src/bin/testcc.sh rename to build/bin/testcc.sh diff --git a/src/bin/testcflags.sh b/build/bin/testcflags.sh similarity index 100% rename from src/bin/testcflags.sh rename to build/bin/testcflags.sh diff --git a/src/bin/testcxx.sh b/build/bin/testcxx.sh similarity index 100% rename from src/bin/testcxx.sh rename to build/bin/testcxx.sh diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index c9e97efdbb1..3100565f8fa 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -41,8 +41,14 @@ case $SYSTEM in debian*|ubuntu*) cat <) variables. For example, takes: # -# deps_cysignals = python2 cython pari | pip +# deps_cysignals = python3 cython pari | pip # # to: # -# $(inst_python2) $(inst_cython) $(inst_pari) | $(inst_pip) +# $(inst_python3) $(inst_cython) $(inst_pari) | $(inst_pip) # # If some value in the dependencies list is not a package name (e.g. it is # the name of some arbitrary file, or it is the '|' symbol) then it is just @@ -156,25 +382,35 @@ pkg_deps = \ # ============================= normal packages ============================== # Generate build rules for 'normal' packages; this template is used to generate -# three rules in the form: +# rules in the form: # # $(INST)/-: -# +$(AM_V_at)sage-logger -p '$(SAGE_SPKG) -' '$(SAGE_LOGS)/-.log' +# $(MAKE) $(1)-no-deps # # : $(INST)/- # +# -build-deps: +# +# -no-deps: +# +$(AM_V_at)sage-logger -p '$(SAGE_SPKG) -' '$(SAGE_LOGS)/-.log' +# # -clean: # sage-spkg-uninstall '$(SAGE_LOCAL)' # -# For example, for python2 this will expand to: +# So -build-deps installs just the dependencies, while +# -no-deps tries to install the package without its +# dependencies. This is currently used in SAGE_SRC/bin/sage when +# running 'sage -b' to build the Sage library. # -# $(INST)/python2-2.7.14: $(inst_zlib) $(inst_readline) $(inst_sqlite) $(inst_libpng) $(inst_bzip2) -# +$(AM_V_at)sage-logger -p '$(SAGE_SPKG) python2-2.7.14' '$(SAGE_LOGS)/python2-2.7.14.log' +# For example, for python3 this will expand to: # -# python2: $(INST)/python2-2.7.14 +# $(INST)/python3-3.7.3: $(inst_zlib) $(inst_readline) $(inst_sqlite) $(inst_libpng) $(inst_bzip2) $(inst_xz) $(inst_libffi) +# +$(AM_V_at)sage-logger -p '$(SAGE_SPKG) python3-3.7.3' '$(SAGE_LOGS)/python3-3.7.3.log' # -# python2-clean: -# sage-spkg-uninstall python2 '$(SAGE_LOCAL)' +# python3: $(INST)/python3-3.7.3 +# +# python3-clean: +# sage-spkg-uninstall python3 '$(SAGE_LOCAL)' # # Note: In these rules the $(INST)/- target is used # explicitly, rather than expanding the $(inst_) variable, since @@ -191,19 +427,32 @@ pkg_deps = \ # $(1): package name # $(2): package version # $(3): package dependencies -define NORMAL_PACKAGE_templ + +ifeq "$(origin SAGE_CHECK)" "undefined" +SAGE_CHECK := no +endif + +define NORMAL_PACKAGE_templ ########################################## +SAGE_CHECK_$(1) := $(SAGE_CHECK) + +$(1)-build-deps: $(3) + $$(INST)/$(1)-$(2): $(3) - +$(AM_V_at)sage-logger -p '$$(SAGE_SPKG) \ - $(if $(filter $(1),$(TOOLCHAIN_DEPS)),--keep-existing) \ - $(1)-$(2)' '$$(SAGE_LOGS)/$(1)-$(2).log' + $(MAKE) $(1)-no-deps $(1): $$(INST)/$(1)-$(2) +$(1)-no-deps: + +$(AM_V_at)sage-logger -p 'SAGE_CHECK=$$(SAGE_CHECK_$(1)) $$(SAGE_SPKG) $$(SAGE_SPKG_OPTIONS) \ + $(if $(filter $(1),$(TOOLCHAIN_DEPS)),--keep-existing) \ + $(1)-$(2)' '$$(SAGE_LOGS)/$(1)-$(2).log' + $(1)-clean: sage-spkg-uninstall $(if $(filter $(1),$(TOOLCHAIN_DEPS)),--keep-files) \ $(1) '$(SAGE_LOCAL)' -endef +.PHONY: $(1) $(1)-clean $(1)-build-deps $(1)-no-deps +endef ################################################################# $(foreach pkgname, $(NORMAL_PACKAGES),\ $(eval $(call NORMAL_PACKAGE_templ,$(pkgname),$(vers_$(pkgname)),\ @@ -216,26 +465,64 @@ $(foreach pkgname, $(NORMAL_PACKAGES),\ $(call pkg_deps,$(pkgname))))) endif +# Parsing the SAGE_CHECK_PACKAGES variable: +# - if this contains "!pkg", set SAGE_CHECK_pkg=no. +# - if this contains "?pkg", set SAGE_CHECK_pkg=warn. +# - if this contains "pkg", set SAGE_CHECK_pkg=yes. +# +# We check this now and export SAGE_CHECK_pkg for +# dependencies and the Makefile rules. +# +# Since Python's self-tests seem to fail on all platforms, we disable +# its test suite by default. +# However, if SAGE_CHECK=warn, we do not do that. +SAGE_CHECK_PACKAGES_DEFAULT_yes := !python3 +SAGE_CHECK_PACKAGES_DEFAULT_warn := +SAGE_CHECK_PACKAGES_DEFAULT_no := +comma := , +ifeq "$(origin SAGE_CHECK_PACKAGES)" "undefined" +SAGE_CHECK_PACKAGES := $(SAGE_CHECK_PACKAGES_DEFAULT$(SAGE_CHECK)) +endif +SAGE_CHECK_PACKAGES_sep := $(subst $(comma), ,$(SAGE_CHECK_PACKAGES)) +SAGE_CHECK_PACKAGES_sep := $(subst :, ,$(SAGE_CHECK_PACKAGES_sep)) +define SET_SAGE_CHECK +$(eval SAGE_CHECK_$(1) := $(2)) +endef +$(foreach clause, $(SAGE_CHECK_PACKAGES_sep), \ + $(if $(findstring !,$(clause)), \ + $(eval $(call SET_SAGE_CHECK,$(subst !,,$(clause)),no)), \ + $(if $(findstring ?,$(clause)), \ + $(eval $(call SET_SAGE_CHECK,$(subst ?,,$(clause)),warn)), \ + $(eval $(call SET_SAGE_CHECK,$(clause),yes))))) +debug-check: + @echo $(foreach pkgname, $(NORMAL_PACKAGES), SAGE_CHECK_$(pkgname) = $(SAGE_CHECK_$(pkgname))) + # ================================ pip packages =============================== # Generate build rules for 'pip' packages; this template is used to generate # two rules in the form: # # : -# $(AM_V_at)sage-logger -p 'sage --pip install ' '$(SAGE_LOGS)/.log' +# $(AM_V_at)sage-logger -p 'sage --pip install ...' '$(SAGE_LOGS)/.log' # # -clean: -# -sage --pip uninstall -y +# -sage --pip uninstall -y ... # Positional arguments: # $(1): package name # $(2): package dependencies define PIP_PACKAGE_templ +$(1)-build-deps: $(2) + $(1): $(2) - $(AM_V_at)sage-logger -p 'sage --pip install $(1)' '$$(SAGE_LOGS)/$(1).log' + $(MAKE) $(1)-no-deps + +$(1)-no-deps: + $(AM_V_at)sage-logger -p 'sage --pip install -r "$$(SAGE_ROOT)/build/pkgs/$(1)/requirements.txt"' '$$(SAGE_LOGS)/$(1).log' $(1)-clean: - -sage --pip uninstall -y $(1) + -sage --pip uninstall -y -r '$$(SAGE_ROOT)/build/pkgs/$(1)/requirements.txt' +.PHONY: $(1) $(1)-clean $(1)-build-deps $(1)-no-deps endef $(foreach pkgname,$(PIP_PACKAGES),\ @@ -249,43 +536,64 @@ endif # ============================= script packages ============================== # Generate build rules for 'script' packages; this template is used to generate -# two rules in the form: +# three rules in the form: # -# : +# $(INST)/-: # $(AM_V_at)cd '$SAGE_ROOT' && \\ -# source '$SAGE_ROOT/src/bin/sage-env' && \\ +# . '$SAGE_ROOT/src/bin/sage-env-config' && \\ +# . '$SAGE_ROOT/src/bin/sage-env' && \\ # sage-logger -p '$SAGE_ROOT/build/pkgs//spkg-install' '$(SAGE_LOGS)/.log' # +# : $(INST)/- +# # -clean: # -$(AM_V_at)cd '$SAGE_ROOT' && \\ -# source '$SAGE_ROOT/src/bin/sage-env' && \\ +# . '$SAGE_ROOT/src/bin/sage-env-config' && \\ +# . '$SAGE_ROOT/src/bin/sage-env' && \\ # '$SAGE_ROOT/build/pkgs/$PKG_NAME/spkg-uninstall' # Positional arguments: # $(1): package name -# $(2): package dependencies +# $(2): package version +# $(3): package dependencies define SCRIPT_PACKAGE_templ -$(1): $(2) - $(AM_V_at)cd '$$(SAGE_ROOT)' && \ - source '$$(SAGE_ROOT)/src/bin/sage-env' && \ - sage-logger -p '$$(SAGE_ROOT)/build/pkgs/$(1)/spkg-install' '$$(SAGE_LOGS)/$(1).log' +$(1)-build-deps: $(3) -$(1)-clean: - -$(AM_V_at)cd '$$(SAGE_ROOT)' && \ - source '$$(SAGE_ROOT)/src/bin/sage-env' && \ +$$(INST)/$(1)-$(2): $(3) + $(MAKE) $(1)-no-deps + +$(1): $$(INST)/$(1)-$(2) + +$(1)-no-deps: + $(AM_V_at)cd '$$(SAGE_ROOT)/build/pkgs/$(1)' && \ + . '$$(SAGE_ROOT)/src/bin/sage-env-config' && \ + . '$$(SAGE_ROOT)/src/bin/sage-env' && \ + . '$$(SAGE_ROOT)/build/bin/sage-build-env-config' && \ + sage-logger -p '$$(SAGE_ROOT)/build/pkgs/$(1)/spkg-install' '$$(SAGE_LOGS)/$(1)-$(2).log' + touch "$$(INST)/$(1)-$(2)" + +$(1)-uninstall: + -$(AM_V_at)cd '$$(SAGE_ROOT)/build/pkgs/$(1)' && \ + . '$$(SAGE_ROOT)/src/bin/sage-env-config' && \ + . '$$(SAGE_ROOT)/src/bin/sage-env' && \ + . '$$(SAGE_ROOT)/build/bin/sage-build-env-config' && \ '$$(SAGE_ROOT)/build/pkgs/$(1)/spkg-uninstall' + -rm -f "$$(INST)/$(1)-$(2)" +.PHONY: $(1) $(1)-uninstall $(1)-build-deps $(1)-no-deps $(1)-clean endef $(foreach pkgname,$(SCRIPT_PACKAGES),\ - $(eval $(call SCRIPT_PACKAGE_templ,$(pkgname),$(call pkg_deps,$(pkgname))))) + $(eval $(call SCRIPT_PACKAGE_templ,$(pkgname),$(vers_$(pkgname)),$(call pkg_deps,$(pkgname))))) ifdef DEBUG_RULES $(info # Rules for script packages) $(foreach pkgname,$(SCRIPT_PACKAGES),\ - $(info $(call SCRIPT_PACKAGE_templ,$(pkgname),$(call pkg_deps,$(pkgname))))) + $(info $(call SCRIPT_PACKAGE_templ,$(pkgname),$(vers_$(pkgname)),$(call pkg_deps,$(pkgname))))) endif +# sagelib depends on this so that its install script is always executed +FORCE: # Use this target to list common targets of this Makefile (in particular for # installation with `sage -i`. @@ -295,7 +603,4 @@ list: done -.PHONY: $(NORMAL_PACKAGES) $(addsuffix -clean,$(NORMAL_PACKAGES)) \ - $(PIP_PACKAGES) $(addsuffix -clean,$(PIP_PACKAGES)) \ - $(SCRIPT_PACKAGES) $(addsuffix -clean,$(SCRIPT_PACKAGES)) \ - list +.PHONY: list diff --git a/build/make/deps b/build/make/deps deleted file mode 100644 index 77b526cefff..00000000000 --- a/build/make/deps +++ /dev/null @@ -1,288 +0,0 @@ -# -*- Makefile -*- ########################################################### -# vim: noexpandtab filetype=make -# This file ($SAGE_ROOT/build/make/deps) will be copied into -# $SAGE_ROOT/build/make/Makefile by config.status -############################################################################### - -# Silent rules -# https://www.gnu.org/software/automake/manual/html_node/Automake-Silent-Rules.html -ifeq ($(V), 0) -AM_V_at = @ -else -AM_V_at = -endif - -# We need to be able to override this to support ./sage -i -c PKG -SAGE_SPKG = sage-spkg - -# List of targets that can be run using `sage -i` or `sage -f` -# These should generally have an associated -clean target for `sage -f` to -# work correctly -SAGE_I_TARGETS = sagelib doc - -STARTED = $(SAGE_LOCAL)/etc/sage-started.txt - - -# Tell make not to look for files with these names: -.PHONY: all all-sage all-toolchain all-build all-sageruntime \ - all-start build-start base toolchain toolchain-deps base-toolchain \ - sagelib \ - doc doc-html doc-html-jsmath doc-html-mathjax doc-pdf \ - doc-clean doc-src-clean doc-output-clean \ - clean sagelib-clean build-clean _clean-broken-gcc - -# Build everything and start Sage. -# Note that we put the "doc" target first in the rule below because -# the doc build takes the most time and should be started as soon as -# possible. -all-start: toolchain-deps - $(MAKE) doc all-sage - $(MAKE) '$(STARTED)' - -# Build everything except the documentation -all-build: toolchain-deps - $(MAKE) all-sage - - -# The 2 preliminary build phases: base and toolchain. -base-toolchain: _clean-broken-gcc base - $(MAKE) toolchain - -# All targets except for the base packages -all-sage: \ - sagelib \ - $(STANDARD_PACKAGE_INSTS) \ - $(OPTIONAL_INSTALLED_PACKAGE_INSTS) \ - $(OPTIONAL_CLEANED_PACKAGES_CLEANS) \ - $(EXTCODE) \ - $(SCRIPTS) - -# Download all packages which should be inside an sdist tarball (the -B -# option to make forces all targets to be built unconditionally) -download-for-sdist: - env SAGE_INSTALL_FETCH_ONLY=yes $(MAKE) -B SAGERUNTIME= \ - $(SDIST_PACKAGES) - -# TOOLCHAIN consists of dependencies determined by configure. -# These are built after the "base" target but before anything else. -toolchain: $(foreach pkgname,$(TOOLCHAIN),$(inst_$(pkgname))) $(PCFILES) - -# Build all packages that GCC links against serially, otherwise this -# leads to race conditions where some library which is used by GCC gets -# reinstalled. Since system GCCs might use Sage's libraries, we do this -# unconditionally. We still use the dependency checking from $(MAKE), -# so this will not trigger useless rebuilds. -# See #14168 and #14232. -# -# Note: This list consists of only the *runtime* dependencies of the toolchain. -TOOLCHAIN_DEPS = zlib $(MP_LIBRARY) mpfr mpc -TOOLCHAIN_DEP_INSTS = \ - $(foreach pkgname,$(TOOLCHAIN_DEPS),$(inst_$(pkgname))) - -toolchain-deps: - for target in $(TOOLCHAIN_DEP_INSTS); do \ - $(MAKE) $$target; \ - done - -all-toolchain: base-toolchain - $(MAKE) toolchain-deps - -# Everything needed to start up Sage using "./sage". Of course, not -# every part of Sage will work. It does not include Maxima for example. -SAGERUNTIME = sagelib $(SCRIPTS) $(inst_ipython) $(inst_pexpect) \ - $(inst_psutil) $(inst_future) - -all-sageruntime: toolchain-deps - $(MAKE) $(SAGERUNTIME) - - -# Start Sage at least once to check that it works -# (i.e. when we just installed Sage for the first time). -build-start: all-build - $(MAKE) '$(STARTED)' - -# We make this depend on all standard packages because running -# sage-starts runs sage-location, which should be run after installing -# any package. -$(STARTED): $(STANDARD_PACKAGE_INSTS) - $(AM_V_at)"$(SAGE_LOCAL)/bin/sage-starts" - - -############################################################################### -# Building the base system -# -# This consists of packages which are required for the Sage build system. -############################################################################### -base: $(inst_patch) $(inst_pkgconf) - -############################################################################### -# Building normal packages -############################################################################### - -# List all *build-time* dependencies of the Sage library. These are, -# on the one hand, programs needed for the build/install process of the -# Sage library (e.g. CYTHON, JINJA2), and on the other hand all -# dependencies for Cython files (e.g. PARI, NTL, MP_LIBRARY). -sagelib: \ - $(inst_arb) \ - $(inst_boost_cropped) \ - $(inst_$(BLAS)) \ - $(inst_brial) \ - $(inst_cliquer) \ - $(inst_cypari) \ - $(inst_cysignals) \ - $(inst_cython) \ - $(inst_ecl) \ - $(inst_eclib) \ - $(inst_ecm) \ - $(inst_flint) \ - $(inst_libgd) \ - $(inst_gap) \ - $(inst_givaro) \ - $(inst_glpk) \ - $(inst_gmpy2) \ - $(inst_gsl) \ - $(inst_iml) \ - $(inst_jinja2) \ - $(inst_jupyter_core) \ - $(inst_lcalc) \ - $(inst_lrcalc) \ - $(inst_libbraiding) \ - $(inst_libhomfly) \ - $(inst_libpng) \ - $(inst_linbox) \ - $(inst_m4ri) \ - $(inst_m4rie) \ - $(inst_mpc) \ - $(inst_mpfi) \ - $(inst_mpfr) \ - $(inst_$(MP_LIBRARY)) \ - $(inst_ntl) \ - $(inst_numpy) \ - $(inst_pari) \ - $(inst_pip) \ - $(inst_pkgconfig) \ - $(inst_planarity) \ - $(inst_ppl) \ - $(inst_pplpy) \ - $(inst_pycygwin) \ - $(inst_pynac) \ - $(inst_$(PYTHON)) \ - $(inst_ratpoints) \ - $(inst_readline) \ - $(inst_rw) \ - $(inst_sage_conf) \ - $(inst_singular) \ - $(inst_six) \ - $(inst_symmetrica) \ - $(inst_zn_poly) \ - $(EXTCODE) \ - $(PCFILES) - $(AM_V_at)if [ -z "$$SAGE_INSTALL_FETCH_ONLY" ]; then \ - cd $(SAGE_SRC) && source bin/sage-env && \ - sage-logger -p 'time $(MAKE) sage' '$(SAGE_LOGS)/sagelib-$(SAGE_VERSION).log'; \ - fi - - -############################################################################### -# Building scripts and extcode -############################################################################### - -# Don't just use "install" since we don't want to change permissions -$(SAGE_LOCAL)/bin/%: $(SAGE_SRC)/bin/% - $(AM_V_at)cp $< $@ - -# Don't just use "install -D" since we don't want to change permissions. -# cp won't correctly setup the SAGE_EXTCODE directory structure -# (unlike install), so we need a mkdir here such that cp can copy into -# an existing folder. -$(SAGE_EXTCODE)/%: $(SAGE_SRC)/ext/% - @mkdir -p "$(@D)" - $(AM_V_at)cp $< $@ - -# Install sage-specific generated .pc files -$(SAGE_PKGCONFIG)/%.pc: $(SAGE_SRC)/lib/pkgconfig/%.pc - @mkdir -p "$(@D)" - $(AM_V_at)cp -P $< $@ - - -############################################################################### -# Building the documentation -############################################################################### - -# You can choose to have the built HTML version of the documentation link to -# the PDF version. To do so, you need to build both the HTML and PDF versions. -# To have the HTML version link to the PDF version, do -# -# $ ./sage --docbuild all html -# $ ./sage --docbuild all pdf -# -# For more information on the docbuild utility, do -# -# $ ./sage --docbuild -H - -# Building the documentation has many dependencies, because all -# documented modules are imported and because we use matplotlib to -# produce plots. -DOC_DEPENDENCIES = sagelib $(inst_sphinx) \ - | $(SAGERUNTIME) $(inst_maxima) $(inst_networkx) $(inst_scipy) $(inst_sympy) \ - $(inst_matplotlib) $(inst_pillow) $(inst_mathjax) $(inst_mpmath) \ - $(inst_ipykernel) $(inst_jupyter_client) $(inst_conway_polynomials) \ - $(inst_tachyon) $(inst_jmol) $(inst_thebe) $(inst_ipywidgets) $(inst_typing) - -doc: doc-html - -doc-html: $(DOC_DEPENDENCIES) - $(AM_V_at)cd ../.. && sage-logger -p './sage --docbuild --no-pdf-links all html $(SAGE_DOCBUILD_OPTS)' logs/dochtml.log - -# 'doc-html-no-plot': build docs without building the graphics coming -# from the '.. plot' directive, in case you want to save a few -# megabytes of disk space. 'doc-clean' is a prerequisite because the -# presence of graphics is cached in src/doc/output. -doc-html-no-plot: doc-clean $(DOC_DEPENDENCIES) - $(AM_V_at)cd ../.. && sage-logger -p './sage --docbuild --no-pdf-links --no-plot all html $(SAGE_DOCBUILD_OPTS)' logs/dochtml.log - -doc-html-mathjax: $(DOC_DEPENDENCIES) - $(AM_V_at)cd ../.. && sage-logger -p './sage --docbuild --no-pdf-links all html -j $(SAGE_DOCBUILD_OPTS)' logs/dochtml.log - -# Keep target 'doc-html-jsmath' for backwards compatibility. -doc-html-jsmath: doc-html-mathjax - -doc-pdf: $(DOC_DEPENDENCIES) - $(AM_V_at)cd ../.. && sage-logger -p './sage --docbuild all pdf $(SAGE_DOCBUILD_OPTS)' logs/docpdf.log - -doc-clean: doc-src-clean doc-output-clean - -doc-src-clean: - cd "$(SAGE_SRC)/doc" && $(MAKE) clean - -doc-output-clean: - rm -rf "$(SAGE_SHARE)/doc/sage" - - -############################################################################### -# Cleaning up -############################################################################### - -clean: - @echo "Deleting package build directories..." - rm -rf "$(SAGE_LOCAL)/var/tmp/sage/build" - -sagelib-clean: - cd "$(SAGE_SRC)" && $(MAKE) clean - -build-clean: clean doc-clean sagelib-clean - -# Special target for cleaning up a broken GCC install detected by configure -# This should check for the .clean-broken-gcc stamp, and if found clean -# everything up along with the stamp file itself. This target is then run -# as a prerequisite to installing any other packages. -_clean-broken-gcc: - @if [ -f "$(SAGE_ROOT)/build/make/.clean-broken-gcc" ]; then \ - rm -f "$(SAGE_LOCAL)/bin/gcc"; \ - rm -f "$(SAGE_LOCAL)/gcc-"*; \ - rm -f "$(SAGE_LOCAL)/bin/g++"; \ - rm -f "$(SAGE_SPKG_INST)/gcc-"*; \ - rm -f "$(SAGE_ROOT)/build/make/.clean-broken-gcc"; \ - echo "Cleaned up old broken GCC install"; \ - fi diff --git a/build/make/install b/build/make/install index f67e640a6b9..0d18185295f 100755 --- a/build/make/install +++ b/build/make/install @@ -16,12 +16,10 @@ if [ $? -ne 0 ]; then fi export SAGE_SHARE="$SAGE_LOCAL/share" -export SAGE_EXTCODE="$SAGE_SHARE/sage/ext" export SAGE_PKGCONFIG="$SAGE_LOCAL/lib/pkgconfig" export SAGE_LOGS="$SAGE_ROOT/logs/pkgs" export SAGE_SPKG_INST="$SAGE_LOCAL/var/lib/sage/installed" -. "$SAGE_SRC"/bin/sage-version.sh -export SAGE_VERSION +export SAGE_SPKG_SCRIPTS="$SAGE_LOCAL/var/lib/sage/scripts" if [ -z "${SAGE_ORIG_PATH_SET}" ]; then SAGE_ORIG_PATH=$PATH && export SAGE_ORIG_PATH @@ -59,57 +57,55 @@ env | sort printf '\E[m' echo "***********************************************" -############################################################################### -# NOW do the actual build: -############################################################################### -time $MAKE "$@" -if [ $? -ne 0 ]; then - cat >&2 </dev/null`; do + for f in `ls -tr $1 2>/dev/null`; do # Look for recent error message in log file. # Note that "tail -n 20 ..." doesn't work on Solaris. - if tail -20 "$f" 2>/dev/null | grep "^Error" &>/dev/null; then + if tail -$2 $f 2>/dev/null | grep "$3" &>/dev/null; then base_f=`basename $f .log` # stat(1) is not portable between Linux and macOS, so we extract it from ls -lf timestamp=`ls -l $f | awk -F' ' '{print $6, $7, $8}'` 2> /dev/null cat >&2 <&2 <&2 </dev/null`; do - # Look for recent error message in log file. - # Note that "tail -n 20 ..." doesn't work on Solaris. - if tail -100 "$f" 2>/dev/null | grep "^Error" &>/dev/null; then - base_f=`basename $f .log` - # stat(1) is not portable between Linux and macOS, so we extract it from ls -lf - timestamp=`ls -l $f | awk -F' ' '{print $6, $7, $8}'` 2> /dev/null - cat >&2 <&2 <&2 + look_for_errors "$SAGE_LOGS/../doc*.log" 100 "^Error" documentation >&2 + cat >&2 <&2 <&2 + fi + fi # Build succeeded. diff --git a/build/pkgs/4ti2/SPKG.rst b/build/pkgs/4ti2/SPKG.rst new file mode 100644 index 00000000000..c847e765542 --- /dev/null +++ b/build/pkgs/4ti2/SPKG.rst @@ -0,0 +1,25 @@ +4ti2 +==== + +Description +----------- + +A software package for algebraic, geometric and combinatorial problems +on linear spaces. Available at www.4ti2.de. + +License +------- + +4ti2 is released under a GPL v2 license. + + +Upstream Contact +---------------- + +- Raymond Hemmecke, TU Munich, Germany +- Matthias Köppe, UC Davis, CA, USA + +Dependencies +------------ + +GLPK, GMP. diff --git a/build/pkgs/4ti2/SPKG.txt b/build/pkgs/4ti2/SPKG.txt deleted file mode 100644 index 9a791bd4ce2..00000000000 --- a/build/pkgs/4ti2/SPKG.txt +++ /dev/null @@ -1,36 +0,0 @@ -= 4ti2 = - -== Description == - -A software package for algebraic, geometric and combinatorial problems on linear spaces. Available at www.4ti2.de. - -== License == -4ti2 is released under a GPL v2 license. - -== Upstream Contact == - -Raymond Hemmecke, TU Munich, Germany -Matthias Köppe, UC Davis, CA, USA - -== Dependencies == - -GLPK, GMP. - -=== 4ti2-1.6.5 (Matthias Köppe, May 19, 2015) === - * update to 4ti2 version 1.6.5 - * add spkg-check script - -=== 4ti2-1.6.3 (Dima Pasechnik, May 1, 2015) === - * update to 4ti2 version 1.6.3 and to new spkg style - -=== 4ti2-1.6.2 (Dima Pasechnik, Jul 4, 2014) === - * update to 4ti2 version 1.6.2 - -=== 4ti2-1.6 (Dima Pasechnik, Sept 7, 2013) === - * update to 4ti2 version 1.6 - * hg: added stuff to track - * updated upstream contacts - -=== 4ti2.p0 (Marshall Hampton, July 31th, 2009) === - * Created a first attempt. - diff --git a/build/pkgs/4ti2/spkg-check b/build/pkgs/4ti2/spkg-check deleted file mode 100644 index 06a0b122190..00000000000 --- a/build/pkgs/4ti2/spkg-check +++ /dev/null @@ -1,19 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -# We don't have to set up any environment variables here since the -# Makefiles already have them from 'configure'. - -echo "Now running 4ti2's test suite..." -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error: The 4ti2 test suite failed." - exit 1 -fi - -echo "The 4ti2 test suite passed successfully." diff --git a/build/pkgs/4ti2/spkg-check.in b/build/pkgs/4ti2/spkg-check.in new file mode 100644 index 00000000000..4727de00ffe --- /dev/null +++ b/build/pkgs/4ti2/spkg-check.in @@ -0,0 +1,6 @@ +cd src + +# We don't have to set up any environment variables here since the +# Makefiles already have them from 'configure'. + +$MAKE check diff --git a/build/pkgs/4ti2/spkg-install b/build/pkgs/4ti2/spkg-install.in similarity index 100% rename from build/pkgs/4ti2/spkg-install rename to build/pkgs/4ti2/spkg-install.in diff --git a/build/pkgs/alabaster/SPKG.rst b/build/pkgs/alabaster/SPKG.rst new file mode 100644 index 00000000000..63abb0eed3c --- /dev/null +++ b/build/pkgs/alabaster/SPKG.rst @@ -0,0 +1,15 @@ +alabaster +========= + +Description +----------- + +Alabaster is a visually (c)lean, responsive, configurable theme for the +Sphinx documentation system. It is Python 2+3 compatible. + +It began as a third-party theme, and is still maintained separately, but +as of Sphinx 1.3, Alabaster is an install-time dependency of Sphinx and +is selected as the default theme. + +Live examples of this theme can be seen on paramiko.org, fabfile.org and +pyinvoke.org. diff --git a/build/pkgs/alabaster/SPKG.txt b/build/pkgs/alabaster/SPKG.txt deleted file mode 100644 index b6f8231441d..00000000000 --- a/build/pkgs/alabaster/SPKG.txt +++ /dev/null @@ -1,13 +0,0 @@ -= alabaster = - -== Description == - -Alabaster is a visually (c)lean, responsive, configurable theme for the Sphinx -documentation system. It is Python 2+3 compatible. - -It began as a third-party theme, and is still maintained separately, but as of -Sphinx 1.3, Alabaster is an install-time dependency of Sphinx and is selected -as the default theme. - -Live examples of this theme can be seen on paramiko.org, fabfile.org and -pyinvoke.org. diff --git a/build/pkgs/alabaster/dependencies b/build/pkgs/alabaster/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/alabaster/dependencies +++ b/build/pkgs/alabaster/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/alabaster/spkg-install b/build/pkgs/alabaster/spkg-install.in similarity index 100% rename from build/pkgs/alabaster/spkg-install rename to build/pkgs/alabaster/spkg-install.in diff --git a/build/pkgs/appnope/SPKG.rst b/build/pkgs/appnope/SPKG.rst new file mode 100644 index 00000000000..bd3e10e2962 --- /dev/null +++ b/build/pkgs/appnope/SPKG.rst @@ -0,0 +1,7 @@ +appnope +======= + +Description +----------- + +Disable App Nap on OS X 10.9 diff --git a/build/pkgs/appnope/SPKG.txt b/build/pkgs/appnope/SPKG.txt deleted file mode 100644 index eafadb7faad..00000000000 --- a/build/pkgs/appnope/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= appnope = - -== Description == - -Disable App Nap on OS X 10.9 diff --git a/build/pkgs/appnope/dependencies b/build/pkgs/appnope/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/appnope/dependencies +++ b/build/pkgs/appnope/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/appnope/spkg-install b/build/pkgs/appnope/spkg-install.in similarity index 100% rename from build/pkgs/appnope/spkg-install rename to build/pkgs/appnope/spkg-install.in diff --git a/build/pkgs/arb/SPKG.rst b/build/pkgs/arb/SPKG.rst new file mode 100644 index 00000000000..c0eea2854e7 --- /dev/null +++ b/build/pkgs/arb/SPKG.rst @@ -0,0 +1,34 @@ +arb +=== + +Description +----------- + +Arb is a C library for arbitrary-precision floating-point ball +arithmetic, developed by Fredrik Johansson +(fredrik.johansson@gmail.com). It supports efficient high-precision +computation with polynomials, power series, matrices and special +functions over the real and complex numbers, with automatic, rigorous +error control. + +License +------- + +GNU General Public License v2+ + + +Upstream Contact +---------------- + +- Fredrik Johansson: fredrik.johansson@gmail.com + +Dependencies +------------ + +- FLINT +- MPIR or GMP +- MPFR + + +Special Update/Build Instructions +--------------------------------- diff --git a/build/pkgs/arb/SPKG.txt b/build/pkgs/arb/SPKG.txt deleted file mode 100644 index 20a790f549c..00000000000 --- a/build/pkgs/arb/SPKG.txt +++ /dev/null @@ -1,29 +0,0 @@ -= arb = - -== Description == - -Arb is a C library for arbitrary-precision floating-point ball -arithmetic, developed by Fredrik Johansson -(fredrik.johansson@gmail.com). It supports efficient high-precision -computation with polynomials, power series, matrices and special -functions over the real and complex numbers, with automatic, rigorous -error control. - -== License == - -GNU General Public License v2+ - - -== Upstream Contact == - -* Fredrik Johansson: fredrik.johansson@gmail.com - - -== Dependencies == - -* FLINT -* MPIR or GMP -* MPFR - -== Special Update/Build Instructions == - diff --git a/build/pkgs/arb/distros/fedora.txt b/build/pkgs/arb/distros/fedora.txt new file mode 100644 index 00000000000..76794404627 --- /dev/null +++ b/build/pkgs/arb/distros/fedora.txt @@ -0,0 +1 @@ +arb arb-devel diff --git a/build/pkgs/arb/distros/gentoo.txt b/build/pkgs/arb/distros/gentoo.txt new file mode 100644 index 00000000000..58e3d4f8008 --- /dev/null +++ b/build/pkgs/arb/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/arb diff --git a/build/pkgs/arb/distros/homebrew.txt b/build/pkgs/arb/distros/homebrew.txt new file mode 100644 index 00000000000..9a7a5bca0a1 --- /dev/null +++ b/build/pkgs/arb/distros/homebrew.txt @@ -0,0 +1,3 @@ +## This package depends on ntl, the homebrew package of which we cannot use +## because it is built with NTL_THREADS. See https://trac.sagemath.org/ticket/29339 +# sagemath/science/arb diff --git a/build/pkgs/arb/patches/silence-stderr-exponent-too-large.patch b/build/pkgs/arb/patches/silence-stderr-exponent-too-large.patch new file mode 100644 index 00000000000..dbc608955c4 --- /dev/null +++ b/build/pkgs/arb/patches/silence-stderr-exponent-too-large.patch @@ -0,0 +1,37 @@ +Do not write stuff to stderr when signalling errors + +See https://trac.sagemath.org/ticket/28817 + + +diff --git a/arf/get_mpfr.c b/arf/get_mpfr.c +index 34012af..9ccb87b 100644 +--- a/arf/get_mpfr.c ++++ b/arf/get_mpfr.c +@@ -30,7 +30,6 @@ arf_get_mpfr(mpfr_t x, const arf_t y, mpfr_rnd_t rnd) + } + else if (COEFF_IS_MPZ(*ARF_EXPREF(y))) + { +- flint_printf("exception: exponent too large to convert to mpfr"); + flint_abort(); + r = 0; /* dummy return because flint_abort() is not declared noreturn */ + } +diff --git a/fmpr/get_mpfr.c b/fmpr/get_mpfr.c +index 63b6682..e2dda3b 100644 +--- a/fmpr/get_mpfr.c ++++ b/fmpr/get_mpfr.c +@@ -25,7 +25,6 @@ fmpr_get_mpfr(mpfr_t x, const fmpr_t y, mpfr_rnd_t rnd) + } + else if (COEFF_IS_MPZ(*fmpr_expref(y))) + { +- flint_printf("exception: exponent too large to convert to mpfr"); + flint_abort(); + r = 0; /* dummy return because flint_abort() is not declared noreturn */ + } +@@ -42,7 +41,6 @@ fmpr_get_mpfr(mpfr_t x, const fmpr_t y, mpfr_rnd_t rnd) + + if (!mpfr_regular_p(x)) + { +- flint_printf("exception: exponent too large to convert to mpfr"); + flint_abort(); + } + } diff --git a/build/pkgs/arb/spkg-check b/build/pkgs/arb/spkg-check.in similarity index 100% rename from build/pkgs/arb/spkg-check rename to build/pkgs/arb/spkg-check.in diff --git a/build/pkgs/arb/spkg-install b/build/pkgs/arb/spkg-install deleted file mode 100644 index 3695bf521eb..00000000000 --- a/build/pkgs/arb/spkg-install +++ /dev/null @@ -1,12 +0,0 @@ -cd src - -# The git head of arb now honors LDFLAGS; The following workaround can -# be removed in arb >= 2.8 when it is released -export EXTRA_SHARED_FLAGS=$LDFLAGS - -./configure --disable-static --prefix="$SAGE_LOCAL" $SAGE_CONFIGURE_GMP \ - $SAGE_CONFIGURE_MPFR $SAGE_CONFIGURE_FLINT || \ - sdh_die "Error configuring arb." - -sdh_make verbose -sdh_make_install diff --git a/build/pkgs/arb/spkg-install.in b/build/pkgs/arb/spkg-install.in new file mode 100644 index 00000000000..4aa200a2b91 --- /dev/null +++ b/build/pkgs/arb/spkg-install.in @@ -0,0 +1,21 @@ +cd src + +# The git head of arb now honors LDFLAGS; The following workaround can +# be removed in arb >= 2.8 when it is released +export EXTRA_SHARED_FLAGS=$LDFLAGS + +# Trac #29607: We must always supply --with-gmp, --with-mpfr, +# --with-flint because otherwise ARB's configure script uses +# /usr/local, which is always wrong. +# This is why we do not use $SAGE_CONFIGURE_GMP etc. here. +# The value $SAGE_LOCAL is always a safe choice even if the library +# is coming from the system and is found using what is in +# LIBRARY_PATH or LDFLAGS etc. +./configure --disable-static --prefix="$SAGE_LOCAL" \ + --with-gmp="$SAGE_LOCAL" \ + --with-mpfr="$SAGE_LOCAL" \ + --with-flint="$SAGE_LOCAL" || \ + sdh_die "Error configuring arb." + +sdh_make verbose +sdh_make_install diff --git a/build/pkgs/arch.txt b/build/pkgs/arch.txt index 138726e849d..9b9bf09e7e2 100644 --- a/build/pkgs/arch.txt +++ b/build/pkgs/arch.txt @@ -6,3 +6,5 @@ python # system python for bootstrapping the build tar bc gcc +# Needed for 4ti2: +which diff --git a/build/pkgs/atlas/SPKG.rst b/build/pkgs/atlas/SPKG.rst new file mode 100644 index 00000000000..b2c086c83ba --- /dev/null +++ b/build/pkgs/atlas/SPKG.rst @@ -0,0 +1,106 @@ +ATLAS +===== + +Description +----------- + +This spkg builds ATLAS for Sage. + +License +------- + +3-clause BSD + + +Upstream Contact +---------------- + +- Atlas devel mailing list. +- Clint Whaley has frequently answered questions from the Sage project + +Dependencies +------------ + +- Python + + +Special Update/Build Instructions +--------------------------------- + +- src/lapack-x.y.z.tgz: The netlib lapack tarball. If you update this, + make sure you also update the LAPACK_TARBALL variable in + spkg-install. + +- src/ATLAS-lib: We are using a dummy autotools/libtools project + to repack the static ATLAS libraries into shared libraries. + +- src/ARCHS: We ship some archdef tarballs to speed ATLAS build. +- spkg-install: If you update atlas to a new version make sure that the + ATLAS_OSTYPE, ATLAS_MACHTYPE, and ATLAS_ISAEXT variables in + spkg-install remain in sync with atlas' CONFIG/include/atlconf.h + +- The package is never installed on OS X, unless you set + SAGE_ATLAS_ARCH. + +Patches +~~~~~~~ + +- patches/detect.patch: Fix Itanium2 support on modern + RHEL 5 and SLES 10 systems, work around -m64 issue on Itanium2, + and correctly detect number and speed of CPUs on a bunch of systems. + +- patches/arm_hard_floats.patch: make sure soft floats are not enforced + on ARM. +- patches/Makefile.patch: fix clean target. +- patches/do_not_force_mutex.patch: always use assembly over mutex + since the mutex version fails to build a shared library. See #15045 + for details. + +- patches/glibc_scanf_workaround.patch: Workaround for the scanf bug + in glibc-2.18 that breaks the atlas auto-tuning system. + +Configuration +~~~~~~~~~~~~~ + +The package can be configured via three environment variables: + +- SAGE_ATLAS_LIB=path + + If this environment variable is set, the libraries libatlas, + libcblas, liblapack, and libf77blas from the direcory "path" are + used and ATLAS is not compiled from source. The libraries can be + either static (endin in .a) or shared libraries (ending in .so or + .dylib). + +- SAGE_ATLAS_ARCH=arch[,isaext1][,isaext2]...[,isaextN] + + The given architectural default and instruction set extensions are + used instead of the empirical tuning. Available architectures are + + POWER3, POWER4, POWER5, PPCG4, PPCG5, POWER6, POWER7, IBMz9, + IBMz10, IBMz196, x86x87, x86SSE1, x86SSE2, x86SSE3, P5, P5MMX, + PPRO, PII, PIII, PM, CoreSolo, CoreDuo, Core2Solo, Core2, Corei1, + Corei2, Atom, P4, P4E, Efficeon, K7, HAMMER, AMD64K10h, AMDDOZER, + UNKNOWNx86, IA64Itan, IA64Itan2, USI, USII, USIII, USIV, UST1, UST2, + UnknownUS, MIPSR1xK, MIPSICE9, ARMv6, ARMv7 + + and instruction set extensions are + + VSX, AltiVec, AVXMAC, AVXFMA4, AVX, SSE3, SSE2, SSE1, 3DNow, NEON + + In addition, you can also set + +- SAGE_ATLAS_ARCH=fast picks defaults for a modern (2-3 year old) + CPU of your processor line, and + +- SAGE_ATLAS_ARCH=base picks defaults that should work for a ~10 + year old CPU. + + For example, + + SAGE_ATLAS_ARCH=Corei2,AVX,SSE3,SSE2,SSE1 + + would be appropriate for a Core i7 CPU. + +- If SAGE_ATLAS_SAVE_ARCHDEF = is given, then a new archdef + file is created and saved to the given path. diff --git a/build/pkgs/atlas/SPKG.txt b/build/pkgs/atlas/SPKG.txt deleted file mode 100644 index eb015aa4ec4..00000000000 --- a/build/pkgs/atlas/SPKG.txt +++ /dev/null @@ -1,85 +0,0 @@ -= ATLAS = - -== Description == - -This spkg builds ATLAS for Sage. - -== License == - -3-clause BSD - -== Upstream Contact == - - * Atlas devel mailing list. - * Clint Whaley has frequently answered questions from the Sage project - -== Dependencies == - - * Python - -== Special Update/Build Instructions == - - * src/lapack-x.y.z.tgz: The netlib lapack tarball. If you update this, - make sure you also update the LAPACK_TARBALL variable in spkg-install. - * src/ATLAS-lib: We are using a dummy autotools/libtools project - to repack the static ATLAS libraries into shared libraries. - * src/ARCHS: We ship some archdef tarballs to speed ATLAS build. - * spkg-install: If you update atlas to a new version make sure that the - ATLAS_OSTYPE, ATLAS_MACHTYPE, and ATLAS_ISAEXT variables in - spkg-install remain in sync with atlas' CONFIG/include/atlconf.h - * The package is never installed on OS X, unless you set SAGE_ATLAS_ARCH. - -=== Patches === - * patches/detect.patch: Fix Itanium2 support on modern - RHEL 5 and SLES 10 systems, work around -m64 issue on Itanium2, - and correctly detect number and speed of CPUs on a bunch of systems. - * patches/arm_hard_floats.patch: make sure soft floats are not enforced on ARM. - * patches/Makefile.patch: fix clean target. - * patches/do_not_force_mutex.patch: always use assembly over mutex - since the mutex version fails to build a shared library. See #15045 - for details. - * patches/glibc_scanf_workaround.patch: Workaround for the scanf bug - in glibc-2.18 that breaks the atlas auto-tuning system. - -=== Configuration === -The package can be configured via three environment variables: - - * SAGE_ATLAS_LIB=path - If this environment variable is set, the libraries libatlas, - libcblas, liblapack, and libf77blas from the direcory "path" are - used and ATLAS is not compiled from source. The libraries can be - either static (endin in .a) or shared libraries (ending in .so or - .dylib). - - * SAGE_ATLAS_ARCH=arch[,isaext1][,isaext2]...[,isaextN] - The given architectural default and instruction set extensions are - used instead of the empirical tuning. Available architectures are - - POWER3, POWER4, POWER5, PPCG4, PPCG5, POWER6, POWER7, IBMz9, - IBMz10, IBMz196, x86x87, x86SSE1, x86SSE2, x86SSE3, P5, P5MMX, - PPRO, PII, PIII, PM, CoreSolo, CoreDuo, Core2Solo, Core2, Corei1, - Corei2, Atom, P4, P4E, Efficeon, K7, HAMMER, AMD64K10h, AMDDOZER, - UNKNOWNx86, IA64Itan, IA64Itan2, USI, USII, USIII, USIV, UST1, UST2, - UnknownUS, MIPSR1xK, MIPSICE9, ARMv6, ARMv7 - - and instruction set extensions are - - VSX, AltiVec, AVXMAC, AVXFMA4, AVX, SSE3, SSE2, SSE1, 3DNow, NEON - - In addition, you can also set - - - SAGE_ATLAS_ARCH=fast picks defaults for a modern (2-3 year old) - CPU of your processor line, and - - - SAGE_ATLAS_ARCH=base picks defaults that should work for a ~10 - year old CPU. - - For example, - - SAGE_ATLAS_ARCH=Corei2,AVX,SSE3,SSE2,SSE1 - - would be appropriate for a Core i7 CPU. - - * If SAGE_ATLAS_SAVE_ARCHDEF = is given, then a new archdef - file is created and saved to the given path. - diff --git a/build/pkgs/atlas/configuration.py b/build/pkgs/atlas/configuration.py index d79f597b87a..cea295d1906 100644 --- a/build/pkgs/atlas/configuration.py +++ b/build/pkgs/atlas/configuration.py @@ -40,16 +40,6 @@ # linker_GNU?, linker_Solaris?, linker_Darwin? # ld -###################################################################### -### Sanity check -###################################################################### - -if 'SAGE_LOCAL' not in os.environ: - print("SAGE_LOCAL undefined ... exiting") - print("Maybe run 'sage -sh'?") - sys.exit(1) - - ###################################################################### ### Functions ###################################################################### diff --git a/build/pkgs/atlas/spkg-check b/build/pkgs/atlas/spkg-check deleted file mode 100644 index 56549cdcc68..00000000000 --- a/build/pkgs/atlas/spkg-check +++ /dev/null @@ -1,57 +0,0 @@ -###################################################################### -### Sanity check -###################################################################### - -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -###################################################################### -### Skip building ATLAS on specific systems -###################################################################### - -if [ "$UNAME" = "Darwin" -a -z "$SAGE_ATLAS_ARCH" ]; then - echo "System-wide accelerate framework is used on Darwin; skipping ATLAS test suite." - exit 0 -fi - -if [ ! -z "$SAGE_ATLAS_LIB" ]; then - echo "SAGE_ATLAS_LIB is set to \"$SAGE_ATLAS_LIB\"; skipping ATLAS test suite." - exit 0 -fi - - -###################################################################### -### check and collect timings -###################################################################### - -make_check() -{ - # make sure everything builds correctly - $MAKE check - if [ $? -ne 0 ]; then - echo >&2 "Error: The ATLAS self-tests failed." - exit 1 - else - echo "The ATLAS self-tests successfully passed." - fi -} - - -make_time() -{ - # collect some timings - $MAKE time - if [ $? -ne 0 ]; then - echo >&2 "Error: The ATLAS timing data failed to be collected." - exit 1 - else - echo "The ATLAS timing data was successfully collected." - fi -} - -cd src/ATLAS-build -make_check -make_time diff --git a/build/pkgs/atlas/spkg-check.in b/build/pkgs/atlas/spkg-check.in new file mode 100644 index 00000000000..506f8f7ad89 --- /dev/null +++ b/build/pkgs/atlas/spkg-check.in @@ -0,0 +1,47 @@ +###################################################################### +### Skip building ATLAS on specific systems +###################################################################### + +if [ "$UNAME" = "Darwin" -a -z "$SAGE_ATLAS_ARCH" ]; then + echo "System-wide accelerate framework is used on Darwin; skipping ATLAS test suite." + exit 0 +fi + +if [ ! -z "$SAGE_ATLAS_LIB" ]; then + echo "SAGE_ATLAS_LIB is set to \"$SAGE_ATLAS_LIB\"; skipping ATLAS test suite." + exit 0 +fi + + +###################################################################### +### check and collect timings +###################################################################### + +make_check() +{ + # make sure everything builds correctly + $MAKE check + if [ $? -ne 0 ]; then + echo >&2 "The ATLAS self-tests failed." + exit 1 + else + echo "The ATLAS self-tests successfully passed." + fi +} + + +make_time() +{ + # collect some timings + $MAKE time + if [ $? -ne 0 ]; then + echo >&2 "The ATLAS timing data failed to be collected." + exit 1 + else + echo "The ATLAS timing data was successfully collected." + fi +} + +cd src/ATLAS-build +make_check +make_time diff --git a/build/pkgs/atlas/spkg-install b/build/pkgs/atlas/spkg-install.in similarity index 100% rename from build/pkgs/atlas/spkg-install rename to build/pkgs/atlas/spkg-install.in diff --git a/build/pkgs/autotools/Makefile.build b/build/pkgs/autotools/Makefile.build deleted file mode 100644 index 5420dd66890..00000000000 --- a/build/pkgs/autotools/Makefile.build +++ /dev/null @@ -1,890 +0,0 @@ -######################################################################## -# This file is automatically generated by ./spkg-write-makefile -######################################################################## - -all: autoconf-all automake-all libtool-all tools-all - -######################################################################## - -tools-all: $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/help2man - -$(SAGE_LOCAL)/bin/makeinfo: $(SRC)/texinfo-4.13 - if [ "$$UNAME" = CYGWIN ] ; then cp -p "$(SRC)/../install-info.exe.manifest" "$(SAGE_LOCAL)/bin" ; fi - cd $< && ./configure --prefix="$(SAGE_LOCAL)" && $(MAKE) && $(MAKE) install - -$(SAGE_LOCAL)/bin/m4: $(SRC)/m4-1.4.17 $(SAGE_LOCAL)/bin/makeinfo - cd $< && ./configure --prefix="$(SAGE_LOCAL)" && $(MAKE) && $(MAKE) install - -$(SAGE_LOCAL)/bin/help2man: $(SRC)/help2man-1.46.4 $(SAGE_LOCAL)/bin/makeinfo - cd $< && ./configure --prefix="$(SAGE_LOCAL)" && $(MAKE) && $(MAKE) install - -######################################################################## - -# Extract sources from git repository serially -autoconf-2.13.rc1/.tarball-version: - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.13.rc1/ autoconf-2-13-rc1 ) | tar xf - - echo 2.13.rc1 >autoconf-2.13.rc1/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.13.rc1: autoconf-2.13.rc1/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo - export MAKE='$(MAKE) -j1' ; \ - cd autoconf-2.13.rc1 && touch autoupdate.sh && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.13.rc1" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.13.rc1/* - -# Extract sources from git repository serially -autoconf-2.57/.tarball-version: autoconf-2.13.rc1/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.57/ AUTOCONF-2.57 ) | tar xf - - echo 2.57 >autoconf-2.57/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.57: autoconf-2.57/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man - export MAKE='$(MAKE) -j1' ; \ - cd autoconf-2.57 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.57" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.57/* - -# Extract sources from git repository serially -autoconf-2.58/.tarball-version: autoconf-2.57/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.58/ AUTOCONF-2.58 ) | tar xf - - echo 2.58 >autoconf-2.58/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.58: autoconf-2.58/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man - export MAKE='$(MAKE) -j1' ; \ - cd autoconf-2.58 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.58" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.58/* - -# Extract sources from git repository serially -autoconf-2.59/.tarball-version: autoconf-2.58/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.59/ AUTOCONF-2.59 ) | tar xf - - echo 2.59 >autoconf-2.59/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.59: autoconf-2.59/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man - export MAKE='$(MAKE) -j1' ; \ - cd autoconf-2.59 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.59" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.59/* - -# Extract sources from git repository serially -autoconf-2.60/.tarball-version: autoconf-2.59/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.60/ AUTOCONF-2.60 ) | tar xf - - echo 2.60 >autoconf-2.60/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.60: autoconf-2.60/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man - export MAKE='$(MAKE) -j1' ; \ - cd autoconf-2.60 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.60" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.60/* - -# Extract sources from git repository serially -autoconf-2.61/.tarball-version: autoconf-2.60/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.61/ AUTOCONF-2.61 ) | tar xf - - echo 2.61 >autoconf-2.61/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.61: autoconf-2.61/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man - export MAKE='$(MAKE) -j1' ; \ - cd autoconf-2.61 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.61" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.61/* - -# Extract sources from git repository serially -autoconf-2.62/.tarball-version: autoconf-2.61/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.62/ v2.62 ) | tar xf - - echo 2.62 >autoconf-2.62/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.62: autoconf-2.62/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.60 $(SAGE_LOCAL)/automake-1.10 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.60 ; \ - export AUTOMAKE_VERSION=1.10 ; \ - cd autoconf-2.62 && autoreconf -i -I m4 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.62" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.62/* - -# Extract sources from git repository serially -autoconf-2.63/.tarball-version: autoconf-2.62/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.63/ v2.63 ) | tar xf - - echo 2.63 >autoconf-2.63/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.63: autoconf-2.63/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.60 $(SAGE_LOCAL)/automake-1.10 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.60 ; \ - export AUTOMAKE_VERSION=1.10 ; \ - cd autoconf-2.63 && autoreconf -i -I m4 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.63" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.63/* - -# Extract sources from git repository serially -autoconf-2.64/.tarball-version: autoconf-2.63/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.64/ v2.64 ) | tar xf - - echo 2.64 >autoconf-2.64/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.64: autoconf-2.64/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.60 $(SAGE_LOCAL)/automake-1.10 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.60 ; \ - export AUTOMAKE_VERSION=1.10 ; \ - cd autoconf-2.64 && autoreconf -i -I m4 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.64" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.64/* - -# Extract sources from git repository serially -autoconf-2.65/.tarball-version: autoconf-2.64/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.65/ v2.65 ) | tar xf - - echo 2.65 >autoconf-2.65/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.65: autoconf-2.65/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.60 $(SAGE_LOCAL)/automake-1.10 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.60 ; \ - export AUTOMAKE_VERSION=1.10 ; \ - cd autoconf-2.65 && autoreconf -i -I m4 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.65" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.65/* - -# Extract sources from git repository serially -autoconf-2.66/.tarball-version: autoconf-2.65/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.66/ v2.66 ) | tar xf - - echo 2.66 >autoconf-2.66/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.66: autoconf-2.66/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.62 $(SAGE_LOCAL)/automake-1.11 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.62 ; \ - export AUTOMAKE_VERSION=1.11 ; \ - cd autoconf-2.66 && autoreconf -i -I m4 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.66" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.66/* - -# Extract sources from git repository serially -autoconf-2.67/.tarball-version: autoconf-2.66/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.67/ v2.67 ) | tar xf - - echo 2.67 >autoconf-2.67/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.67: autoconf-2.67/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.62 $(SAGE_LOCAL)/automake-1.11 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.62 ; \ - export AUTOMAKE_VERSION=1.11 ; \ - cd autoconf-2.67 && autoreconf -i -I m4 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.67" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.67/* - -# Extract sources from git repository serially -autoconf-2.68/.tarball-version: autoconf-2.67/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.68/ v2.68 ) | tar xf - - echo 2.68 >autoconf-2.68/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.68: autoconf-2.68/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.62 $(SAGE_LOCAL)/automake-1.11 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.62 ; \ - export AUTOMAKE_VERSION=1.11 ; \ - cd autoconf-2.68 && autoreconf -i -I m4 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.68" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.68/* - -# Extract sources from git repository serially -autoconf-2.69/.tarball-version: autoconf-2.68/.tarball-version - ( cd $(SRC)/autoconf && git archive --format=tar --prefix=autoconf-2.69/ v2.69 ) | tar xf - - echo 2.69 >autoconf-2.69/.tarball-version - -$(SAGE_LOCAL)/autoconf-2.69: autoconf-2.69/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.62 $(SAGE_LOCAL)/automake-1.11 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.62 ; \ - export AUTOMAKE_VERSION=1.11 ; \ - cd autoconf-2.69 && autoreconf -i -I m4 && \ - ./configure --prefix="$(SAGE_LOCAL)/autoconf-2.69" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf autoconf-2.69/* - -autoconf-all: $(SAGE_LOCAL)/autoconf-2.13.rc1 $(SAGE_LOCAL)/autoconf-2.57 $(SAGE_LOCAL)/autoconf-2.58 $(SAGE_LOCAL)/autoconf-2.59 $(SAGE_LOCAL)/autoconf-2.60 $(SAGE_LOCAL)/autoconf-2.61 $(SAGE_LOCAL)/autoconf-2.62 $(SAGE_LOCAL)/autoconf-2.63 $(SAGE_LOCAL)/autoconf-2.64 $(SAGE_LOCAL)/autoconf-2.65 $(SAGE_LOCAL)/autoconf-2.66 $(SAGE_LOCAL)/autoconf-2.67 $(SAGE_LOCAL)/autoconf-2.68 $(SAGE_LOCAL)/autoconf-2.69 - -######################################################################## - -# Extract sources from git repository serially -automake-1.9/.tarball-version: autoconf-2.69/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.9/ Release-1-9 ) | tar xf - - echo 1.9 >automake-1.9/.tarball-version - -$(SAGE_LOCAL)/automake-1.9: automake-1.9/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.59 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.59 ; \ - cd automake-1.9 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.9" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.9/* - -# Extract sources from git repository serially -automake-1.9.1/.tarball-version: automake-1.9/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.9.1/ Release-1-9-1 ) | tar xf - - echo 1.9.1 >automake-1.9.1/.tarball-version - -$(SAGE_LOCAL)/automake-1.9.1: automake-1.9.1/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.59 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.59 ; \ - cd automake-1.9.1 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.9.1" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.9.1/* - -# Extract sources from git repository serially -automake-1.9.2/.tarball-version: automake-1.9.1/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.9.2/ Release-1-9-2 ) | tar xf - - echo 1.9.2 >automake-1.9.2/.tarball-version - -$(SAGE_LOCAL)/automake-1.9.2: automake-1.9.2/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.59 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.59 ; \ - cd automake-1.9.2 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.9.2" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.9.2/* - -# Extract sources from git repository serially -automake-1.9.3/.tarball-version: automake-1.9.2/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.9.3/ Release-1-9-3 ) | tar xf - - echo 1.9.3 >automake-1.9.3/.tarball-version - -$(SAGE_LOCAL)/automake-1.9.3: automake-1.9.3/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.59 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.59 ; \ - cd automake-1.9.3 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.9.3" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.9.3/* - -# Extract sources from git repository serially -automake-1.9.4/.tarball-version: automake-1.9.3/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.9.4/ Release-1-9-4 ) | tar xf - - echo 1.9.4 >automake-1.9.4/.tarball-version - -$(SAGE_LOCAL)/automake-1.9.4: automake-1.9.4/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.59 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.59 ; \ - cd automake-1.9.4 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.9.4" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.9.4/* - -# Extract sources from git repository serially -automake-1.9.5/.tarball-version: automake-1.9.4/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.9.5/ Release-1-9-5 ) | tar xf - - echo 1.9.5 >automake-1.9.5/.tarball-version - -$(SAGE_LOCAL)/automake-1.9.5: automake-1.9.5/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.59 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.59 ; \ - cd automake-1.9.5 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.9.5" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.9.5/* - -# Extract sources from git repository serially -automake-1.9.6/.tarball-version: automake-1.9.5/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.9.6/ Release-1-9-6 ) | tar xf - - echo 1.9.6 >automake-1.9.6/.tarball-version - -$(SAGE_LOCAL)/automake-1.9.6: automake-1.9.6/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.59 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.59 ; \ - cd automake-1.9.6 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.9.6" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.9.6/* - -# Extract sources from git repository serially -automake-1.10/.tarball-version: automake-1.9.6/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.10/ Release-1-10 ) | tar xf - - echo 1.10 >automake-1.10/.tarball-version - -$(SAGE_LOCAL)/automake-1.10: automake-1.10/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.60 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.60 ; \ - cd automake-1.10 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.10" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.10/* - -# Extract sources from git repository serially -automake-1.10.1/.tarball-version: automake-1.10/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.10.1/ Release-1-10-1 ) | tar xf - - echo 1.10.1 >automake-1.10.1/.tarball-version - -$(SAGE_LOCAL)/automake-1.10.1: automake-1.10.1/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.60 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.60 ; \ - cd automake-1.10.1 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.10.1" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.10.1/* - -# Extract sources from git repository serially -automake-1.10.2/.tarball-version: automake-1.10.1/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.10.2/ v1.10.2 ) | tar xf - - echo 1.10.2 >automake-1.10.2/.tarball-version - -$(SAGE_LOCAL)/automake-1.10.2: automake-1.10.2/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.60 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.60 ; \ - cd automake-1.10.2 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.10.2" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.10.2/* - -# Extract sources from git repository serially -automake-1.10.3/.tarball-version: automake-1.10.2/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.10.3/ v1.10.3 ) | tar xf - - echo 1.10.3 >automake-1.10.3/.tarball-version - -$(SAGE_LOCAL)/automake-1.10.3: automake-1.10.3/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.60 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.60 ; \ - cd automake-1.10.3 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.10.3" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.10.3/* - -# Extract sources from git repository serially -automake-1.11/.tarball-version: automake-1.10.3/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.11/ v1.11 ) | tar xf - - echo 1.11 >automake-1.11/.tarball-version - -$(SAGE_LOCAL)/automake-1.11: automake-1.11/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.62 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.62 ; \ - cd automake-1.11 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.11" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.11/* - -# Extract sources from git repository serially -automake-1.11.1/.tarball-version: automake-1.11/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.11.1/ v1.11.1 ) | tar xf - - echo 1.11.1 >automake-1.11.1/.tarball-version - -$(SAGE_LOCAL)/automake-1.11.1: automake-1.11.1/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.62 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.62 ; \ - cd automake-1.11.1 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.11.1" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.11.1/* - -# Extract sources from git repository serially -automake-1.11.2/.tarball-version: automake-1.11.1/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.11.2/ v1.11.2 ) | tar xf - - echo 1.11.2 >automake-1.11.2/.tarball-version - -$(SAGE_LOCAL)/automake-1.11.2: automake-1.11.2/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.62 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.62 ; \ - cd automake-1.11.2 && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.11.2" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.11.2/* - -# Extract sources from git repository serially -automake-1.11.3/.tarball-version: automake-1.11.2/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.11.3/ v1.11.3 ) | tar xf - - echo 1.11.3 >automake-1.11.3/.tarball-version - -$(SAGE_LOCAL)/automake-1.11.3: automake-1.11.3/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.68 $(SAGE_LOCAL)/automake-1.10 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.68 ; \ - export AUTOMAKE_VERSION=1.10 ; \ - cd automake-1.11.3 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.11.3" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.11.3/* - -# Extract sources from git repository serially -automake-1.11.4/.tarball-version: automake-1.11.3/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.11.4/ v1.11.4 ) | tar xf - - echo 1.11.4 >automake-1.11.4/.tarball-version - -$(SAGE_LOCAL)/automake-1.11.4: automake-1.11.4/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.68 $(SAGE_LOCAL)/automake-1.10 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.68 ; \ - export AUTOMAKE_VERSION=1.10 ; \ - cd automake-1.11.4 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.11.4" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.11.4/* - -# Extract sources from git repository serially -automake-1.11.5/.tarball-version: automake-1.11.4/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.11.5/ v1.11.5 ) | tar xf - - echo 1.11.5 >automake-1.11.5/.tarball-version - -$(SAGE_LOCAL)/automake-1.11.5: automake-1.11.5/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.68 $(SAGE_LOCAL)/automake-1.10 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.68 ; \ - export AUTOMAKE_VERSION=1.10 ; \ - cd automake-1.11.5 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.11.5" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.11.5/* - -# Extract sources from git repository serially -automake-1.11.6/.tarball-version: automake-1.11.5/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.11.6/ v1.11.6 ) | tar xf - - echo 1.11.6 >automake-1.11.6/.tarball-version - -$(SAGE_LOCAL)/automake-1.11.6: automake-1.11.6/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.68 $(SAGE_LOCAL)/automake-1.10 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.68 ; \ - export AUTOMAKE_VERSION=1.10 ; \ - cd automake-1.11.6 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.11.6" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.11.6/* - -# Extract sources from git repository serially -automake-1.12/.tarball-version: automake-1.11.6/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.12/ v1.12 ) | tar xf - - echo 1.12 >automake-1.12/.tarball-version - -$(SAGE_LOCAL)/automake-1.12: automake-1.12/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.68 $(SAGE_LOCAL)/automake-1.10 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.68 ; \ - export AUTOMAKE_VERSION=1.10 ; \ - cd automake-1.12 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.12" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.12/* - -# Extract sources from git repository serially -automake-1.12.1/.tarball-version: automake-1.12/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.12.1/ v1.12.1 ) | tar xf - - echo 1.12.1 >automake-1.12.1/.tarball-version - -$(SAGE_LOCAL)/automake-1.12.1: automake-1.12.1/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.12.1 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.12.1" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.12.1/* - -# Extract sources from git repository serially -automake-1.12.2/.tarball-version: automake-1.12.1/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.12.2/ v1.12.2 ) | tar xf - - echo 1.12.2 >automake-1.12.2/.tarball-version - -$(SAGE_LOCAL)/automake-1.12.2: automake-1.12.2/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.12.2 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.12.2" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.12.2/* - -# Extract sources from git repository serially -automake-1.12.3/.tarball-version: automake-1.12.2/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.12.3/ v1.12.3 ) | tar xf - - echo 1.12.3 >automake-1.12.3/.tarball-version - -$(SAGE_LOCAL)/automake-1.12.3: automake-1.12.3/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.12.3 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.12.3" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.12.3/* - -# Extract sources from git repository serially -automake-1.12.4/.tarball-version: automake-1.12.3/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.12.4/ v1.12.4 ) | tar xf - - echo 1.12.4 >automake-1.12.4/.tarball-version - -$(SAGE_LOCAL)/automake-1.12.4: automake-1.12.4/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.12.4 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.12.4" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.12.4/* - -# Extract sources from git repository serially -automake-1.12.5/.tarball-version: automake-1.12.4/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.12.5/ v1.12.5 ) | tar xf - - echo 1.12.5 >automake-1.12.5/.tarball-version - -$(SAGE_LOCAL)/automake-1.12.5: automake-1.12.5/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.12.5 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.12.5" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.12.5/* - -# Extract sources from git repository serially -automake-1.12.6/.tarball-version: automake-1.12.5/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.12.6/ v1.12.6 ) | tar xf - - echo 1.12.6 >automake-1.12.6/.tarball-version - -$(SAGE_LOCAL)/automake-1.12.6: automake-1.12.6/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.12.6 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.12.6" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.12.6/* - -# Extract sources from git repository serially -automake-1.13/.tarball-version: automake-1.12.6/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.13/ v1.13 ) | tar xf - - echo 1.13 >automake-1.13/.tarball-version - -$(SAGE_LOCAL)/automake-1.13: automake-1.13/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.13 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.13" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.13/* - -# Extract sources from git repository serially -automake-1.13.1/.tarball-version: automake-1.13/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.13.1/ v1.13.1 ) | tar xf - - echo 1.13.1 >automake-1.13.1/.tarball-version - -$(SAGE_LOCAL)/automake-1.13.1: automake-1.13.1/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.13.1 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.13.1" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.13.1/* - -# Extract sources from git repository serially -automake-1.13.2/.tarball-version: automake-1.13.1/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.13.2/ v1.13.2 ) | tar xf - - echo 1.13.2 >automake-1.13.2/.tarball-version - -$(SAGE_LOCAL)/automake-1.13.2: automake-1.13.2/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.13.2 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.13.2" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.13.2/* - -# Extract sources from git repository serially -automake-1.13.3/.tarball-version: automake-1.13.2/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.13.3/ v1.13.3 ) | tar xf - - echo 1.13.3 >automake-1.13.3/.tarball-version - -$(SAGE_LOCAL)/automake-1.13.3: automake-1.13.3/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.13.3 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.13.3" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.13.3/* - -# Extract sources from git repository serially -automake-1.13.4/.tarball-version: automake-1.13.3/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.13.4/ v1.13.4 ) | tar xf - - echo 1.13.4 >automake-1.13.4/.tarball-version - -$(SAGE_LOCAL)/automake-1.13.4: automake-1.13.4/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.13.4 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.13.4" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.13.4/* - -# Extract sources from git repository serially -automake-1.14/.tarball-version: automake-1.13.4/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.14/ v1.14 ) | tar xf - - echo 1.14 >automake-1.14/.tarball-version - -$(SAGE_LOCAL)/automake-1.14: automake-1.14/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.14 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.14" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.14/* - -# Extract sources from git repository serially -automake-1.14.1/.tarball-version: automake-1.14/.tarball-version - ( cd $(SRC)/automake && git archive --format=tar --prefix=automake-1.14.1/ v1.14.1 ) | tar xf - - echo 1.14.1 >automake-1.14.1/.tarball-version - -$(SAGE_LOCAL)/automake-1.14.1: automake-1.14.1/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.69 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.69 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd automake-1.14.1 && bash -c 'set -e; source bootstrap.sh' && \ - ./configure --prefix="$(SAGE_LOCAL)/automake-1.14.1" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf automake-1.14.1/* - -automake-all: $(SAGE_LOCAL)/automake-1.9 $(SAGE_LOCAL)/automake-1.9.1 $(SAGE_LOCAL)/automake-1.9.2 $(SAGE_LOCAL)/automake-1.9.3 $(SAGE_LOCAL)/automake-1.9.4 $(SAGE_LOCAL)/automake-1.9.5 $(SAGE_LOCAL)/automake-1.9.6 $(SAGE_LOCAL)/automake-1.10 $(SAGE_LOCAL)/automake-1.10.1 $(SAGE_LOCAL)/automake-1.10.2 $(SAGE_LOCAL)/automake-1.10.3 $(SAGE_LOCAL)/automake-1.11 $(SAGE_LOCAL)/automake-1.11.1 $(SAGE_LOCAL)/automake-1.11.2 $(SAGE_LOCAL)/automake-1.11.3 $(SAGE_LOCAL)/automake-1.11.4 $(SAGE_LOCAL)/automake-1.11.5 $(SAGE_LOCAL)/automake-1.11.6 $(SAGE_LOCAL)/automake-1.12 $(SAGE_LOCAL)/automake-1.12.1 $(SAGE_LOCAL)/automake-1.12.2 $(SAGE_LOCAL)/automake-1.12.3 $(SAGE_LOCAL)/automake-1.12.4 $(SAGE_LOCAL)/automake-1.12.5 $(SAGE_LOCAL)/automake-1.12.6 $(SAGE_LOCAL)/automake-1.13 $(SAGE_LOCAL)/automake-1.13.1 $(SAGE_LOCAL)/automake-1.13.2 $(SAGE_LOCAL)/automake-1.13.3 $(SAGE_LOCAL)/automake-1.13.4 $(SAGE_LOCAL)/automake-1.14 $(SAGE_LOCAL)/automake-1.14.1 - -######################################################################## - -# Extract sources from git repository serially -libtool-1.5.20/.tarball-version: automake-1.14.1/.tarball-version - ( cd $(SRC)/libtool && git archive --format=tar --prefix=libtool-1.5.20/ release-1-5-20 ) | tar xf - - echo 1.5.20 >libtool-1.5.20/.tarball-version - echo 1886 > libtool-1.5.20/.serial - -$(SAGE_LOCAL)/libtool-1.5.20: libtool-1.5.20/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.59 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.59 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd libtool-1.5.20 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/libtool-1.5.20" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf libtool-1.5.20/* - -# Extract sources from git repository serially -libtool-1.5.22/.tarball-version: libtool-1.5.20/.tarball-version - ( cd $(SRC)/libtool && git archive --format=tar --prefix=libtool-1.5.22/ release-1-5-22 ) | tar xf - - echo 1.5.22 >libtool-1.5.22/.tarball-version - echo 1965 > libtool-1.5.22/.serial - -$(SAGE_LOCAL)/libtool-1.5.22: libtool-1.5.22/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.59 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.59 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd libtool-1.5.22 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/libtool-1.5.22" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf libtool-1.5.22/* - -# Extract sources from git repository serially -libtool-1.5.24/.tarball-version: libtool-1.5.22/.tarball-version - ( cd $(SRC)/libtool && git archive --format=tar --prefix=libtool-1.5.24/ release-1-5-24 ) | tar xf - - echo 1.5.24 >libtool-1.5.24/.tarball-version - echo 2056 > libtool-1.5.24/.serial - -$(SAGE_LOCAL)/libtool-1.5.24: libtool-1.5.24/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.59 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.59 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd libtool-1.5.24 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/libtool-1.5.24" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf libtool-1.5.24/* - -# Extract sources from git repository serially -libtool-1.5.26/.tarball-version: libtool-1.5.24/.tarball-version - ( cd $(SRC)/libtool && git archive --format=tar --prefix=libtool-1.5.26/ release-1-5-26 ) | tar xf - - echo 1.5.26 >libtool-1.5.26/.tarball-version - echo 2093 > libtool-1.5.26/.serial - -$(SAGE_LOCAL)/libtool-1.5.26: libtool-1.5.26/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.59 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.59 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd libtool-1.5.26 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/libtool-1.5.26" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf libtool-1.5.26/* - -# Extract sources from git repository serially -libtool-2.2.4/.tarball-version: libtool-1.5.26/.tarball-version - ( cd $(SRC)/libtool && git archive --format=tar --prefix=libtool-2.2.4/ v2.2.4 ) | tar xf - - echo 2.2.4 >libtool-2.2.4/.tarball-version - echo 3070 > libtool-2.2.4/.serial - -$(SAGE_LOCAL)/libtool-2.2.4: libtool-2.2.4/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.59 $(SAGE_LOCAL)/automake-1.9.6 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.59 ; \ - export AUTOMAKE_VERSION=1.9.6 ; \ - cd libtool-2.2.4 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/libtool-2.2.4" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf libtool-2.2.4/* - -# Extract sources from git repository serially -libtool-2.2.6/.tarball-version: libtool-2.2.4/.tarball-version - ( cd $(SRC)/libtool && git archive --format=tar --prefix=libtool-2.2.6/ v2.2.6 ) | tar xf - - echo 2.2.6 >libtool-2.2.6/.tarball-version - echo 3117 > libtool-2.2.6/.serial - -$(SAGE_LOCAL)/libtool-2.2.6: libtool-2.2.6/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.60 $(SAGE_LOCAL)/automake-1.10.1 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.60 ; \ - export AUTOMAKE_VERSION=1.10.1 ; \ - cd libtool-2.2.6 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/libtool-2.2.6" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf libtool-2.2.6/* - -# Extract sources from git repository serially -libtool-2.2.6b/.tarball-version: libtool-2.2.6/.tarball-version - ( cd $(SRC)/libtool && git archive --format=tar --prefix=libtool-2.2.6b/ v2.2.6b ) | tar xf - - echo 2.2.6b >libtool-2.2.6b/.tarball-version - echo 3123 > libtool-2.2.6b/.serial - -$(SAGE_LOCAL)/libtool-2.2.6b: libtool-2.2.6b/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/autoconf-2.60 $(SAGE_LOCAL)/automake-1.10.1 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.60 ; \ - export AUTOMAKE_VERSION=1.10.1 ; \ - cd libtool-2.2.6b && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/libtool-2.2.6b" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf libtool-2.2.6b/* - -# Extract sources from git repository serially -libtool-2.2.8/.tarball-version: libtool-2.2.6b/.tarball-version - ( cd $(SRC)/libtool && git archive --format=tar --prefix=libtool-2.2.8/ v2.2.8 ) | tar xf - - echo 2.2.8 >libtool-2.2.8/.tarball-version - echo 3328 > libtool-2.2.8/.serial - -$(SAGE_LOCAL)/libtool-2.2.8: libtool-2.2.8/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.62 $(SAGE_LOCAL)/automake-1.10.1 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.62 ; \ - export AUTOMAKE_VERSION=1.10.1 ; \ - cd libtool-2.2.8 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/libtool-2.2.8" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf libtool-2.2.8/* - -# Extract sources from git repository serially -libtool-2.2.10/.tarball-version: libtool-2.2.8/.tarball-version - ( cd $(SRC)/libtool && git archive --format=tar --prefix=libtool-2.2.10/ v2.2.10 ) | tar xf - - echo 2.2.10 >libtool-2.2.10/.tarball-version - echo 3349 > libtool-2.2.10/.serial - -$(SAGE_LOCAL)/libtool-2.2.10: libtool-2.2.10/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.62 $(SAGE_LOCAL)/automake-1.10.1 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.62 ; \ - export AUTOMAKE_VERSION=1.10.1 ; \ - cd libtool-2.2.10 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/libtool-2.2.10" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf libtool-2.2.10/* - -# Extract sources from git repository serially -libtool-2.4/.tarball-version: libtool-2.2.10/.tarball-version - ( cd $(SRC)/libtool && git archive --format=tar --prefix=libtool-2.4/ v2.4 ) | tar xf - - echo 2.4 >libtool-2.4/.tarball-version - echo 3560 > libtool-2.4/.serial - -$(SAGE_LOCAL)/libtool-2.4: libtool-2.4/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.62 $(SAGE_LOCAL)/automake-1.11.1 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.62 ; \ - export AUTOMAKE_VERSION=1.11.1 ; \ - cd libtool-2.4 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/libtool-2.4" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf libtool-2.4/* - -# Extract sources from git repository serially -libtool-2.4.2/.tarball-version: libtool-2.4/.tarball-version - ( cd $(SRC)/libtool && git archive --format=tar --prefix=libtool-2.4.2/ v2.4.2 ) | tar xf - - echo 2.4.2 >libtool-2.4.2/.tarball-version - echo 3620 > libtool-2.4.2/.serial - -$(SAGE_LOCAL)/libtool-2.4.2: libtool-2.4.2/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.62 $(SAGE_LOCAL)/automake-1.11.1 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.62 ; \ - export AUTOMAKE_VERSION=1.11.1 ; \ - cd libtool-2.4.2 && bash -c 'set -e; source bootstrap' && \ - ./configure --prefix="$(SAGE_LOCAL)/libtool-2.4.2" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf libtool-2.4.2/* - -# Extract sources from git repository serially -libtool-2.4.3/.tarball-version: libtool-2.4.2/.tarball-version - ( cd $(SRC)/libtool && git archive --format=tar --prefix=libtool-2.4.3/ v2.4.3 ) | tar xf - - echo 2.4.3 >libtool-2.4.3/.tarball-version - echo 4105 > libtool-2.4.3/.serial - -$(SAGE_LOCAL)/libtool-2.4.3: libtool-2.4.3/.tarball-version $(SAGE_LOCAL)/bin/m4 $(SAGE_LOCAL)/bin/makeinfo $(SAGE_LOCAL)/bin/help2man $(SAGE_LOCAL)/autoconf-2.62 $(SAGE_LOCAL)/automake-1.11.1 - export MAKE='$(MAKE) -j1' ; \ - export AUTOCONF_VERSION=2.62 ; \ - export AUTOMAKE_VERSION=1.11.1 ; \ - cd libtool-2.4.3 && bash bootstrap --skip-git --skip-po --gnulib-srcdir=../../src/gnulib && \ - ./configure --prefix="$(SAGE_LOCAL)/libtool-2.4.3" && \ - $$MAKE && $$MAKE install - # Remove all files except for the .* files - [ "$$SAGE_KEEP_BUILT_SPKGS" = yes ] || rm -rf libtool-2.4.3/* - -libtool-all: $(SAGE_LOCAL)/libtool-1.5.20 $(SAGE_LOCAL)/libtool-1.5.22 $(SAGE_LOCAL)/libtool-1.5.24 $(SAGE_LOCAL)/libtool-1.5.26 $(SAGE_LOCAL)/libtool-2.2.4 $(SAGE_LOCAL)/libtool-2.2.6 $(SAGE_LOCAL)/libtool-2.2.6b $(SAGE_LOCAL)/libtool-2.2.8 $(SAGE_LOCAL)/libtool-2.2.10 $(SAGE_LOCAL)/libtool-2.4 $(SAGE_LOCAL)/libtool-2.4.2 $(SAGE_LOCAL)/libtool-2.4.3 - -######################################################################## - diff --git a/build/pkgs/autotools/SPKG.txt b/build/pkgs/autotools/SPKG.txt deleted file mode 100644 index 11919b30bfa..00000000000 --- a/build/pkgs/autotools/SPKG.txt +++ /dev/null @@ -1,65 +0,0 @@ -= autotools = - -== Description == - -This package contains a recent version of Texinfo, GNU m4, and help2man. -It contains the git repository of autoconf, automake and libtool. - -For the latter 3 packages (commonly referred to as "autotools"), -many different versions are installed, by checking out the versions -from the git repo and building/installing them separately. Since the -complete git repository is shipped in the spkg, this does not require -internet access. - -For Texinfo, m4 and help2man, just one version is installed. These are -prerequisites for autotools. Moreover, Texinfo is often needed for -bootstrapping packages. Even though m4 is already a prerequisite of -Sage, autoconf requires an up-to-date version of GNU m4. - -The package makes wrapper scripts in $SAGE_LOCAL/bin for the various -autotools, which call the "correct" version. This means that, if a file -"configure" already exists in the current directory, the same autoconf -version is run which created the original file. Otherwise, the latest -version is run. The goal of all this is to make it easier to patch -configure.ac or Makefile.am files inside a spkg. By using the same -version of autotools as originally used, the patch files should be -relatively small. The environment variables AUTOCONF_VERSION, -AUTOMAKE_VERSION and LIBTOOL_VERSION can be used to override the -chosen version. - -== License == - -GNU General Public License version 3 or later. - -== Upstream Contact == - -* http://www.gnu.org/software/texinfo/ -* http://www.gnu.org/software/m4/ -* http://www.gnu.org/software/help2man/ -* http://www.gnu.org/software/autoconf/ -* http://www.gnu.org/software/automake/ -* http://www.gnu.org/software/libtool/ - -== Dependencies == - -To install the package: -* Perl -* Git - -To update the package: -* Sage with autotools package installed -* Internet access - -== Special Update/Build Instructions == - -The file spkg-src can be used to automatically create the upstream -tarball from the git repositories. This obviously requires internet -access. - -The file version-list defines the list of versions installed by this -spkg. If you edit this, you must update Makefile.build using the -spkg-write-makefile script. After optionally updating the git repos -using spkg-src, you need to run - ./spkg-write-makefile >Makefile.build -This must be run in a Sage shell, with the autotools spkg -installed. diff --git a/build/pkgs/autotools/autofoo b/build/pkgs/autotools/autofoo deleted file mode 100644 index 485c4a02796..00000000000 --- a/build/pkgs/autotools/autofoo +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -# Script to automatically call the "right" version of @AUTOFOO@ -# installed within Sage. We read the @AUTOFOO@ output file(s) to -# determine the version from that file. Otherwise, we simply run the -# latest version. In all cases, the version can be overridden by the -# @AUTOVAR@ environment variable. - -v="$@AUTOVAR@" - -if [ -z "$v" ]; then - for v_file in @AUTOFILES@; do - v=`exec 2>/dev/null; sed -n <$v_file \ - 's/^\(.*generated.*@AUTOFOO@\( *version\)*\|version=\)[ "]*\([0-9][-0-9a-z.]*\)/\3=/i; T; s/[.]*=.*//; p; q;'` - if [ -n "$v" ]; then break; fi - done -fi - -# Default version -if [ -z "$v" ]; then - v=@DEFAULT_VERSION@ -fi - -prog=`basename "$0"` -exec "$SAGE_LOCAL/@AUTOFOO@-$v/bin/$prog" "$@" diff --git a/build/pkgs/autotools/checksums.ini b/build/pkgs/autotools/checksums.ini deleted file mode 100644 index 63449e97680..00000000000 --- a/build/pkgs/autotools/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=autotools-VERSION.tar.bz2 -sha1=55943c621770cd7309ad12ce76c631893f7c66c8 -md5=f9742a0f469197a5b80dfa301b79c57d -cksum=311232283 diff --git a/build/pkgs/autotools/dependencies b/build/pkgs/autotools/dependencies deleted file mode 100644 index 3b812ffa6b6..00000000000 --- a/build/pkgs/autotools/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -ncurses git xz - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/autotools/install-info.exe.manifest b/build/pkgs/autotools/install-info.exe.manifest deleted file mode 100755 index aaaa2c391f8..00000000000 --- a/build/pkgs/autotools/install-info.exe.manifest +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - diff --git a/build/pkgs/autotools/latest_version b/build/pkgs/autotools/latest_version deleted file mode 100755 index f2ce2558cd7..00000000000 --- a/build/pkgs/autotools/latest_version +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env sage-system-python -# -# Read a whitespace-separated list of version numbers on stdin -# and output the latest version. -# -# A version number is a list of numbers separated by dots, followed -# by an optional alphanumeric string (there may or may not be a dot -# before this string). The numbers must not have leading zeros. -# Typical examples: "5.4.3a" or "1.10.rc0" -# -# Any word in the input which does not start with a digit, is ignored. -# Any word which does start with a digit, but is not of the required -# form, is an error. Trailing alphanumeric strings are chopped off and -# ignored; they don't appear in the output. -# -# AUTHOR: Jeroen Demeyer (2012-10-01) -# - -# Make this script compatible with Python 3. -from __future__ import print_function - - -def version_to_tuple(v): - """ - Convert a version number like "5.4.3a" to a tuple (5,4,3). - """ - n = "" # Current number as string - t = [] # List of numbers - for c in v: - if c.isdigit(): - n += c - elif c == ".": - if len(n) == 0: - raise ValueError("Empty number in version string '{}'".format(v)) - if n[0] == '0' and len(n) >= 2: - raise ValueError("Leading zeros not allowed in version string '{}'".format(v)) - t.append(int(n)) - n = "" - elif c.isalpha(): - break - else: - raise ValueError("Illegal character '{}' in version string '{}'".format(c,v)) - if len(n) > 0: - t.append(int(n)) - return tuple(t) - -def tuple_to_version(t): - """ - Convert a tuple like (5,4,3) to a version number "5.4.3" - """ - return str.join(".", [str(x) for x in t]) - - -import sys -L = sys.stdin.read().split() - -debug = ' '.join(L) - -L = [version_to_tuple(s) for s in L if len(s) > 0 and s[0].isdigit()] -if len(L) == 0: - sys.exit(0) - -L = sorted(L, reverse=True) -ver = tuple_to_version(L[0]) -debug = debug + ' => ' + ver - -print(ver) -print(debug, file=sys.stderr) diff --git a/build/pkgs/autotools/package-version.txt b/build/pkgs/autotools/package-version.txt deleted file mode 100644 index 0e03b2d382b..00000000000 --- a/build/pkgs/autotools/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -20141105.p0 diff --git a/build/pkgs/autotools/patches/texinfo-libtinfo.patch b/build/pkgs/autotools/patches/texinfo-libtinfo.patch deleted file mode 100644 index 74113f6c2e4..00000000000 --- a/build/pkgs/autotools/patches/texinfo-libtinfo.patch +++ /dev/null @@ -1,15 +0,0 @@ -* Our ncurses package is built using configure option --with-termlib, which causes tgetent and friends to be put into the separate library libtinfo, not libncurses. -* The ancient texinfo 4.13 that is used by our autotools package (with comment "texinfo 5.x breaks building old versions of autotools...") does not know about libtinfo. - -diff -r -u texinfo-4.13.orig/configure texinfo-4.13/configure ---- src.orig/texinfo-4.13/configure 2008-09-18 11:46:26.000000000 -0700 -+++ src/texinfo-4.13/configure 2016-10-18 00:04:15.000000000 -0700 -@@ -17477,7 +17477,7 @@ - # rather ncurses. So we check for it. - TERMLIBS= - # Check for termlib before termcap because Solaris termcap needs libucb. --TERMLIB_VARIANTS="ncurses curses termlib termcap terminfo" -+TERMLIB_VARIANTS="ncurses tinfo curses termlib termcap terminfo" - for termlib in ${TERMLIB_VARIANTS}; do - as_ac_Lib=`$as_echo "ac_cv_lib_${termlib}''_tgetent" | $as_tr_sh` - { $as_echo "$as_me:$LINENO: checking for tgetent in -l${termlib}" >&5 diff --git a/build/pkgs/autotools/spkg-install b/build/pkgs/autotools/spkg-install deleted file mode 100644 index 567bfa43610..00000000000 --- a/build/pkgs/autotools/spkg-install +++ /dev/null @@ -1,120 +0,0 @@ -set -e - -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -# Check that git is installed -if ! git --version &>/dev/null; then - echo >&2 "git is not installed, try running" - echo >&2 " sage -i git" - exit 3 -fi - -# Create an empty git repo here, otherwise the autotools installers -# get confused (they try to take version info from git if possible) -git init - - -SRC=`pwd`/src -BUILD=`pwd`/build - -# Mac OS X 10.11.5 installs GNU m4 1.4.6 as both "m4" and "gm4". -# This is too old to bootstrap libtool 1.4.3. -# The configure script prefers "gm4" to "m4" and thus does not -# find the m4 from SageMath. -# The following environment variable makes sure we use the m4 from -# SageMath. See trac #21047. -export M4="$SAGE_LOCAL/bin/m4" - -cd "$SRC" - -######################################################################## -# Remove old installed versions -######################################################################## - -cd "$SAGE_LOCAL" -rm -rf autoconf-* automake-* libtool-* -cd bin -rm -f m4 makeinfo help2man autoconf autoheader autom4te autoreconf \ - autoscan automake aclocal libtool libtoolize - -######################################################################## -# Install wrapper scripts in $SAGE_LOCAL/bin -######################################################################## - -# Determine the default versions of the various autotools, which is the -# last version in the list from version-list. -source "$SRC/../version-list" -for x in $autoconf_versions; do autoconf_latest=$x ; done -for x in $automake_versions; do automake_latest=$x ; done -for x in $libtool_versions; do libtool_latest=$x ; done - -# We install scripts for autoconf,... based on the generic "autofoo" script -cd "$SAGE_LOCAL/bin" -sed <"$SRC/../autofoo" >autoconf \ - "s/@AUTOFOO@/autoconf/; s/@AUTOFILES@/configure/; s/@AUTOVAR@/AUTOCONF_VERSION/; s/@DEFAULT_VERSION@/${autoconf_latest}/" -sed <"$SRC/../autofoo" >automake \ - "s/@AUTOFOO@/automake/; s/@AUTOFILES@/Makefile.in/; s/@AUTOVAR@/AUTOMAKE_VERSION/; s/@DEFAULT_VERSION@/${automake_latest}/" -sed <"$SRC/../autofoo" >libtool \ - "s/@AUTOFOO@/libtool/; s/@AUTOFILES@/ltmain.sh/; s/@AUTOVAR@/LIBTOOL_VERSION/; s/@DEFAULT_VERSION@/${libtool_latest}/" - -# Correct permissions -for prog in autoconf automake libtool; do - chmod 0755 $prog -done - -# Make symlinks -for prog in autoheader autom4te autoreconf autoscan; do - ln -s autoconf $prog -done -ln -s automake aclocal -ln -s libtool libtoolize - -# Make symlinks for some non-exact version matches -cd "$SAGE_LOCAL" -ln -s autoconf-2.13.rc1 autoconf-2.4 -ln -s autoconf-2.13.rc1 autoconf-2.13 -ln -s autoconf-2.60 autoconf-2.59a -ln -s autoconf-2.60 autoconf-2.59c -ln -s libtool-2.2.4 libtool-2.2.3a -ln -s libtool-2.2.8 libtool-2.2.7a - -######################################################################## -# Copy the Makefile and build everything -######################################################################## - -mkdir -p "$BUILD" -cd "$BUILD" -( - echo "SRC = $SRC" - echo "SAGE_LOCAL = $SAGE_LOCAL" - # Force the use of bash as shell (/bin/sh on OpenSolaris has - # problems with very long command lines used in some make rules). - echo "SHELL = bash" - echo - cat "$SRC/../Makefile.build" -) >Makefile - -$MAKE - -# Install symlinks bin/aclocal-AM_API_VERSION and bin/automake-AM_API_VERSION. -# (am__api_version). These are required for autoreconf to work (Trac #21047). -cd "$SAGE_LOCAL"/bin -for x in $automake_versions; do - ln -sf ../automake-$x/bin/automake-* ../automake-$x/bin/aclocal-* . -done - -# Some older versions of automake don't create this directory at install time -# because there aren't any files in it by default; however aclocal crashes if -# the directory is missing; https://trac.sagemath.org/ticket/21526 -for x in $automake_versions; do - aclocal_system_dir="$SAGE_LOCAL/automake-$x/share/aclocal" - if [ ! -d "$aclocal_system_dir" ]; then - mkdir -p "$aclocal_system_dir" - # Place a dummy file so that the directory at least isn't empty - touch "$aclocal_system_dir/README" - fi -done diff --git a/build/pkgs/autotools/spkg-src b/build/pkgs/autotools/spkg-src deleted file mode 100755 index 233e7b4a745..00000000000 --- a/build/pkgs/autotools/spkg-src +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [ -z "$SAGE_ROOT" ]; then - echo >&2 "SAGE_ROOT undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -cd "$SAGE_ROOT" - -TARGET=autotools-`cat build/pkgs/autotools/package-version.txt` - -rm -rf upstream/$TARGET -mkdir -p upstream/$TARGET -cd upstream/$TARGET - - -echo "Downloading m4 sources..." -sage-download-file http://ftp.gnu.org/gnu/m4/m4-1.4.17.tar.xz | tar xJf - - -echo "Downloading help2man sources..." -sage-download-file http://ftp.gnu.org/gnu/help2man/help2man-1.46.4.tar.xz | tar xJf - - -echo "Downloading texinfo sources..." -# texinfo 5.x breaks building old versions of autotools... -sage-download-file http://ftp.gnu.org/gnu/texinfo/texinfo-4.13.tar.lzma | tar xJf - - -git clone git://git.sv.gnu.org/gnulib -git clone --no-checkout git://git.sv.gnu.org/autoconf -git clone --no-checkout git://git.sv.gnu.org/automake -git clone --no-checkout git://git.sv.gnu.org/libtool - - -cd "$SAGE_ROOT/upstream" -tar cjf $TARGET.tar.bz2 $TARGET -rm -rf $TARGET - -echo "New autotools tarball is ready in $SAGE_ROOT/upstream/$TARGET.tar.bz2" diff --git a/build/pkgs/autotools/spkg-write-makefile b/build/pkgs/autotools/spkg-write-makefile deleted file mode 100755 index 2e2189a3466..00000000000 --- a/build/pkgs/autotools/spkg-write-makefile +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/env bash -# -# Write a Makefile for the autotools spkg. This actually requires a -# Sage with autotools installed, so run this from within a Sage shell. -# This script also requires git. -# -# Typical usage: -# ./spkg-write-makefile >Makefile.build -# - -set -e - -if [ -z "$SAGE_ROOT" ]; then - echo >&2 "SAGE_ROOT undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -# Sanity check that AUTOCONF_VERSION and AUTOMAKE_VERSION works -if ! env "AUTOCONF_VERSION=2.62" autoconf --version | grep >/dev/null '2[.]62'; then - echo >&2 "The environment variable AUTOCONF_VERSION does not seem to work." - echo >&2 "Make sure you are running $0 within a Sage shell" - echo >&2 "with the autotools spkg installed." - exit 3 -fi -if ! env "AUTOMAKE_VERSION=1.9.6" aclocal --version | grep >/dev/null '1[.]9[.]6'; then - echo >&2 "The environment variable AUTOMAKE_VERSION does not seem to work." - echo >&2 "Make sure you are running $0 within a Sage shell" - echo >&2 "with the autotools spkg installed." - exit 3 -fi -if ! env "LIBTOOL_VERSION=1.5.26" libtool --version | grep >/dev/null '1[.]5[.]26'; then - echo >&2 "The environment variable LIBTOOL_VERSION does not seem to work." - echo >&2 "Make sure you are running $0 within a Sage shell" - echo >&2 "with the autotools spkg installed." - exit 3 -fi - -export PATH="$SAGE_ROOT/build/pkgs/autotools:$PATH" - - -# Read versions -source version-list - -# Extract upstream autotools tarball -cd "$SAGE_ROOT" -PKG=autotools-`cat build/pkgs/autotools/package-version.txt` -mkdir -p "$SAGE_LOCAL/tmp/sage" -cd "$SAGE_LOCAL/tmp/sage" -tar xjf "$SAGE_ROOT/upstream/$PKG.tar.bz2" -cd $PKG - -cat <&2 "Processing $p-$v" - cd $p - - # Find out the correct tag for version $v - tag=`git tag -l | grep -i -x -e "v$v" -e "release-$v" -e "$p-$v" | head -1` - if [ -z "$tag" ]; then - echo >&2 "Cannot find tag for $p-$v" - exit 3 - fi - - # Checkout the version given by the tag (and remove all garbage) - git checkout -f $tag - git clean -f -d -x -q - - deps="\$(SAGE_LOCAL)/bin/m4 \$(SAGE_LOCAL)/bin/makeinfo" - ac_ver= - am_ver= - if cat configure.* | grep help2man >/dev/null; then - deps="$deps \$(SAGE_LOCAL)/bin/help2man" - fi - if [ -f configure.ac ]; then - # Minimum required version of Automake - if [ ! -f configure ]; then - # libtool-2.4.3 requires some gnulib files - if [ -d gnulib ]; then - cp -a ../gnulib/build-aux . - fi - # Run aclocal, such that AM_INIT_AUTOMAKE is available. - if [ -d m4 ]; then - aclocal -I m4 - else - aclocal - fi - # Require at least version 1.9.6, a reasonable default. - am_ver=`( echo 1.9.6; autoconf --trace='AM_INIT_AUTOMAKE:$1' configure.ac ) | latest_version` - # Run the *correct* version of aclocal, such that we do - # not introduce unneeded AC_PREREQ() definitions. - if [ -d m4 ]; then - env "AUTOMAKE_VERSION=$am_ver" aclocal -I m4 - else - env "AUTOMAKE_VERSION=$am_ver" aclocal - fi - fi - - # Minimum required version of Autoconf: always consider - # AC_PREREQ for Automake, even if "configure" exists. - if [ ! -f configure ] || [ $p = automake ]; then - # Require at least version 2.59, a reasonable default. - ac_ver=`( echo 2.59; autoconf --trace='AC_PREREQ:$1' configure.ac ) | latest_version` - fi - - # Automake 1.10 thinks it only needs autoconf 2.59, when in fact it needs 2.60. Fix it up. - if [ $p = automake ] && [ $v = 1.10 ]; then - ac_ver=2.60 - fi - - # Minimum required version of libtool. - # Empty by default. - lt_ver=`( autoconf --trace='LT_PREREQ:$1' configure.ac ) | latest_version` - fi - if [ -n "$ac_ver" ]; then - deps="$deps \$(SAGE_LOCAL)/autoconf-$ac_ver" - fi - if [ -n "$am_ver" ]; then - deps="$deps \$(SAGE_LOCAL)/automake-$am_ver" - fi - if [ -n "$lt_ver" ]; then - deps="$deps \$(SAGE_LOCAL)/libtool-$lt_ver" - fi - - # Figure out how to bootstrap - if [ -f configure ]; then - bootstrap= - elif [ -d gnulib ]; then - bootstrap="bash bootstrap --skip-git --skip-po --gnulib-srcdir=../../src/gnulib && " - elif [ -f bootstrap.sh ]; then - bootstrap="bash -c 'set -e; source bootstrap.sh' && " - elif [ -f bootstrap ]; then - bootstrap="bash -c 'set -e; source bootstrap' && " - else - bootstrap="autoreconf -i -I m4 && " - fi - - if [ -f autoheader.sh ]; then - # Work around Autoconf bootstrap bug - bootstrap="${bootstrap}touch autoupdate.sh && " - fi - - # Write make rules - echo "# Extract sources from git repository serially" - echo "$p-$v/.tarball-version: $prevextract" - echo -e "\t( cd \$(SRC)/$p && git archive --format=tar --prefix=$p-$v/ $tag ) | tar xf -" - echo -e "\techo $v >$p-$v/.tarball-version" - if [ $p = libtool -a ! -f .serial ] ; then - # The file .serial would be generated at "make dist" time. It is used in ltversion.m4. - # If .serial is missing, ltmversion.m4 will be malformed, causing the following warning - # when the user uses autoreconf. - # m4/ltversion.m4:12: warning: ill-formed serial number 'ltversion.m4', - # expecting a version string with only digits and dots - # See Trac #21047. - # Since we don't do "make dist" but rather build from a repository check-out, we have to - # supply the .serial file ourselves. The following recipe is from libtool's Makefile.am. - echo -e "\techo $( git log --pretty=oneline | wc -l | sed 's|[\t ]||g ' ) > $p-$v/.serial" - fi - echo - - echo "\$(SAGE_LOCAL)/$p-$v: $p-$v/.tarball-version $deps" - echo -e "\texport MAKE='\$(MAKE) -j1' ; \\\\" - [ -z "$lt_ver" ] || echo -e "\texport LIBTOOL_VERSION=$lt_ver ; \\\\" - [ -z "$ac_ver" ] || echo -e "\texport AUTOCONF_VERSION=$ac_ver ; \\\\" - [ -z "$am_ver" ] || echo -e "\texport AUTOMAKE_VERSION=$am_ver ; \\\\" - echo -e "\tcd $p-$v && ${bootstrap}\\\\" - echo -e "\t ./configure --prefix=\"\$(SAGE_LOCAL)/$p-$v\" && \\\\" - echo -e "\t \$\$MAKE && \$\$MAKE install" - echo -e "\t# Remove all files except for the .* files" - echo -e "\t[ \"\$\$SAGE_KEEP_BUILT_SPKGS\" = yes ] || rm -rf $p-$v/*" - echo - - prevextract="$p-$v/.tarball-version" - all="$all \$(SAGE_LOCAL)/$p-$v" - - cd .. # Back to upstream source directory - done - echo "$all" - echo - echo "########################################################################" - echo -} - -write_make_rules autoconf $autoconf_versions -write_make_rules automake $automake_versions -write_make_rules libtool $libtool_versions - -cd "$SAGE_LOCAL/tmp/sage" -rm -rf $PKG diff --git a/build/pkgs/autotools/type b/build/pkgs/autotools/type deleted file mode 100644 index 9839eb20815..00000000000 --- a/build/pkgs/autotools/type +++ /dev/null @@ -1 +0,0 @@ -experimental diff --git a/build/pkgs/autotools/version-list b/build/pkgs/autotools/version-list deleted file mode 100644 index c4a2f2ee385..00000000000 --- a/build/pkgs/autotools/version-list +++ /dev/null @@ -1,23 +0,0 @@ -# This file defines the versions installed by this spkg. If you edit -# this, re-create Makefile.build using -# -# ./spkg-write-makefile >Makefile.build -# -# The order of the versions is irrelevant, except that the last version -# is the one which will be used by default. -# - -autoconf_versions=" - 2.13.rc1 2.57 2.58 2.59 2.60 2.61 2.62 2.63 2.64 2.65 2.66 2.67 - 2.68 2.69" - -automake_versions=" - 1.9 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.10 1.10.1 1.10.2 1.10.3 - 1.11 1.11.1 1.11.2 1.11.3 1.11.4 1.11.5 1.11.6 1.12 1.12.1 1.12.2 - 1.12.3 1.12.4 1.12.5 1.12.6 1.13 1.13.1 1.13.2 1.13.3 1.13.4 - 1.14 1.14.1" - -libtool_versions=" - 1.5.20 1.5.22 1.5.24 1.5.26 - 2.2.4 2.2.6 2.2.6b 2.2.8 2.2.10 - 2.4 2.4.2 2.4.3" diff --git a/build/pkgs/awali/SPKG.rst b/build/pkgs/awali/SPKG.rst new file mode 100644 index 00000000000..11142d87cee --- /dev/null +++ b/build/pkgs/awali/SPKG.rst @@ -0,0 +1,43 @@ +awali +===== + +Description +----------- + +Awali is a software platform dedicated to the computation of, and with, +finite state machines. Here finite state machines is to be understood in +the broadest possible sense: finite automata with output — often called +transducers then — or even more generally finite automata with +multiplicity, that is, automata that not only accept, or recognize, +sequences of symbols but compute for every such sequence a 'value' that +is associated with it and which can be taken in any semiring. Hence the +variety of situations that can thus be modellized. + +License +------- + +- GPL 3.0 + + +Upstream Contact +---------------- + +- Website: http://vaucanson-project.org/Awali/index.html +- Releases: http://files.vaucanson-project.org/tarballs/ + +Dependencies +------------ + +- Python +- CMake +- Cython +- ncurses + +- graphviz must be installed from your distro, and available in the + path. + + +Special Update/Build Instructions +--------------------------------- + +- None diff --git a/build/pkgs/awali/SPKG.txt b/build/pkgs/awali/SPKG.txt deleted file mode 100644 index 37d42039b32..00000000000 --- a/build/pkgs/awali/SPKG.txt +++ /dev/null @@ -1,33 +0,0 @@ -= awali = - -== Description == - -Awali is a software platform dedicated to the computation of, and with, finite -state machines. Here finite state machines is to be understood in the broadest -possible sense: finite automata with output — often called transducers then — or -even more generally finite automata with multiplicity, that is, automata that -not only accept, or recognize, sequences of symbols but compute for every such -sequence a `value' that is associated with it and which can be taken in any -semiring. Hence the variety of situations that can thus be modellized. - -== License == - - * GPL 3.0 - -== Upstream Contact == - - * Website: http://vaucanson-project.org/Awali/index.html - * Releases: http://files.vaucanson-project.org/tarballs/ - -== Dependencies == - - * Python - * CMake - * Cython - * ncurses - - * graphviz must be installed from your distro, and available in the path. - -== Special Update/Build Instructions == - - * None diff --git a/build/pkgs/awali/spkg-check b/build/pkgs/awali/spkg-check deleted file mode 100644 index ee9809b0389..00000000000 --- a/build/pkgs/awali/spkg-check +++ /dev/null @@ -1,2 +0,0 @@ -cd src/_build -sdh_make check diff --git a/build/pkgs/awali/spkg-check.in b/build/pkgs/awali/spkg-check.in new file mode 100644 index 00000000000..7abdbf65a7d --- /dev/null +++ b/build/pkgs/awali/spkg-check.in @@ -0,0 +1,2 @@ +cd src/_build +sdh_make_check diff --git a/build/pkgs/awali/spkg-install b/build/pkgs/awali/spkg-install.in similarity index 100% rename from build/pkgs/awali/spkg-install rename to build/pkgs/awali/spkg-install.in diff --git a/build/pkgs/babel/SPKG.rst b/build/pkgs/babel/SPKG.rst new file mode 100644 index 00000000000..265c2757cee --- /dev/null +++ b/build/pkgs/babel/SPKG.rst @@ -0,0 +1,9 @@ +babel +===== + +Description +----------- + +Internationalization utilities + +A collection of tools for internationalizing Python applications. diff --git a/build/pkgs/babel/SPKG.txt b/build/pkgs/babel/SPKG.txt deleted file mode 100644 index ae0451d58be..00000000000 --- a/build/pkgs/babel/SPKG.txt +++ /dev/null @@ -1,7 +0,0 @@ -= babel = - -== Description == - -Internationalization utilities - -A collection of tools for internationalizing Python applications. diff --git a/build/pkgs/babel/dependencies b/build/pkgs/babel/dependencies index 247fe4de4d9..2fabe3177df 100644 --- a/build/pkgs/babel/dependencies +++ b/build/pkgs/babel/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip pytz +$(PYTHON) | $(PYTHON_TOOLCHAIN) pytz ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/babel/spkg-install b/build/pkgs/babel/spkg-install.in similarity index 100% rename from build/pkgs/babel/spkg-install rename to build/pkgs/babel/spkg-install.in diff --git a/build/pkgs/backcall/SPKG.txt b/build/pkgs/backcall/SPKG.txt new file mode 100644 index 00000000000..1fb8cf6651c --- /dev/null +++ b/build/pkgs/backcall/SPKG.txt @@ -0,0 +1,5 @@ += backcall = + +== Description == + +Specifications for callback functions passed in to an API diff --git a/build/pkgs/backcall/checksums.ini b/build/pkgs/backcall/checksums.ini new file mode 100644 index 00000000000..05df9f194a8 --- /dev/null +++ b/build/pkgs/backcall/checksums.ini @@ -0,0 +1,5 @@ +tarball=backcall-VERSION.tar.gz +sha1=e0ca4c1bde8f0a198505a090cd585a325789def1 +md5=87ce0c7839808e6a3427d57df6a792e7 +cksum=3908868358 +upstream_url=https://pypi.io/packages/source/b/backcall/backcall-VERSION.tar.gz diff --git a/build/pkgs/backcall/dependencies b/build/pkgs/backcall/dependencies new file mode 100644 index 00000000000..15df0c4d6d8 --- /dev/null +++ b/build/pkgs/backcall/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/backcall/package-version.txt b/build/pkgs/backcall/package-version.txt new file mode 100644 index 00000000000..6e8bf73aa55 --- /dev/null +++ b/build/pkgs/backcall/package-version.txt @@ -0,0 +1 @@ +0.1.0 diff --git a/build/pkgs/backports_functools_lru_cache/spkg-install b/build/pkgs/backcall/spkg-install.in similarity index 100% rename from build/pkgs/backports_functools_lru_cache/spkg-install rename to build/pkgs/backcall/spkg-install.in diff --git a/build/pkgs/backports_abc/type b/build/pkgs/backcall/type similarity index 100% rename from build/pkgs/backports_abc/type rename to build/pkgs/backcall/type diff --git a/build/pkgs/backports_abc/SPKG.txt b/build/pkgs/backports_abc/SPKG.txt deleted file mode 100644 index e6f57200183..00000000000 --- a/build/pkgs/backports_abc/SPKG.txt +++ /dev/null @@ -1,18 +0,0 @@ -= backports_abc = - -== Description == - -A backport of recent additions to the 'collections.abc' module. - -== License == - -Python Software Foundation License - -== Upstream Contact == - -Home page: https://pypi.python.org/pypi/backports_abc - -== Dependencies == - -Python, Setuptools - diff --git a/build/pkgs/backports_abc/checksums.ini b/build/pkgs/backports_abc/checksums.ini deleted file mode 100644 index 92117a45b4a..00000000000 --- a/build/pkgs/backports_abc/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=backports_abc-VERSION.tar.gz -sha1=7f19aefc840301effcd43d9d95e8e59ed8153ea2 -md5=7d1936ec183a3586290adf60f6f96764 -cksum=2833604013 diff --git a/build/pkgs/backports_abc/dependencies b/build/pkgs/backports_abc/dependencies deleted file mode 100644 index 2573414ce8a..00000000000 --- a/build/pkgs/backports_abc/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | setuptools pip - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/backports_abc/package-version.txt b/build/pkgs/backports_abc/package-version.txt deleted file mode 100644 index 2eb3c4fe4ee..00000000000 --- a/build/pkgs/backports_abc/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -0.5 diff --git a/build/pkgs/backports_abc/spkg-install b/build/pkgs/backports_abc/spkg-install deleted file mode 100644 index c36dcaaa89f..00000000000 --- a/build/pkgs/backports_abc/spkg-install +++ /dev/null @@ -1,14 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing backports_abc ... exiting" - exit 1 -fi diff --git a/build/pkgs/backports_functools_lru_cache/SPKG.txt b/build/pkgs/backports_functools_lru_cache/SPKG.txt deleted file mode 100644 index ecee62e1137..00000000000 --- a/build/pkgs/backports_functools_lru_cache/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= backports.functools_lru_cache = - -== Description == - -A backport of functools.lru_cache from Python 3.3 as published at ActiveState. diff --git a/build/pkgs/backports_functools_lru_cache/checksums.ini b/build/pkgs/backports_functools_lru_cache/checksums.ini deleted file mode 100644 index 19e72765bcd..00000000000 --- a/build/pkgs/backports_functools_lru_cache/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=backports.functools_lru_cache-VERSION.tar.gz -sha1=35a5895d22875cd024694d3c7393a793e9fd653d -md5=20f53f54cd3f04b3346ce75a54959754 -cksum=4241725171 diff --git a/build/pkgs/backports_functools_lru_cache/dependencies b/build/pkgs/backports_functools_lru_cache/dependencies deleted file mode 100644 index 84580f94937..00000000000 --- a/build/pkgs/backports_functools_lru_cache/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | setuptools_scm pip - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/backports_functools_lru_cache/package-version.txt b/build/pkgs/backports_functools_lru_cache/package-version.txt deleted file mode 100644 index c239c60cba2..00000000000 --- a/build/pkgs/backports_functools_lru_cache/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -1.5 diff --git a/build/pkgs/backports_shutil_get_terminal_size/SPKG.txt b/build/pkgs/backports_shutil_get_terminal_size/SPKG.txt deleted file mode 100644 index 6aa6bcfc6ce..00000000000 --- a/build/pkgs/backports_shutil_get_terminal_size/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= backports.shutil_get_terminal_size = - -== Description == - -A backport of the get_terminal_size function from Python 3.3's shutil. diff --git a/build/pkgs/backports_shutil_get_terminal_size/checksums.ini b/build/pkgs/backports_shutil_get_terminal_size/checksums.ini deleted file mode 100644 index 4b76671e1aa..00000000000 --- a/build/pkgs/backports_shutil_get_terminal_size/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=backports.shutil_get_terminal_size-VERSION.tar.gz -sha1=a9774b04db0abc2df1b4b603699469358967346c -md5=03267762480bd86b50580dc19dff3c66 -cksum=2627812760 diff --git a/build/pkgs/backports_shutil_get_terminal_size/dependencies b/build/pkgs/backports_shutil_get_terminal_size/dependencies deleted file mode 100644 index 2573414ce8a..00000000000 --- a/build/pkgs/backports_shutil_get_terminal_size/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | setuptools pip - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/backports_shutil_get_terminal_size/package-version.txt b/build/pkgs/backports_shutil_get_terminal_size/package-version.txt deleted file mode 100644 index b423d8166b0..00000000000 --- a/build/pkgs/backports_shutil_get_terminal_size/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.0.p1 diff --git a/build/pkgs/backports_shutil_get_terminal_size/patches/master.patch b/build/pkgs/backports_shutil_get_terminal_size/patches/master.patch deleted file mode 100644 index 70f55cc8066..00000000000 --- a/build/pkgs/backports_shutil_get_terminal_size/patches/master.patch +++ /dev/null @@ -1,69 +0,0 @@ -Diff between the latest release and the github master - -diff -ru backports.shutil_get_terminal_size-1.0.0/backports/shutil_get_terminal_size/get_terminal_size.py backports.shutil_get_terminal_size/backports/shutil_get_terminal_size/get_terminal_size.py ---- backports.shutil_get_terminal_size-1.0.0/backports/shutil_get_terminal_size/get_terminal_size.py 2014-08-19 01:52:24.000000000 +0200 -+++ backports.shutil_get_terminal_size/backports/shutil_get_terminal_size/get_terminal_size.py 2016-05-12 17:59:57.208494332 +0200 -@@ -16,30 +16,30 @@ - terminal_size = namedtuple("terminal_size", "columns lines") - - try: -- from ctypes import windll, create_string_buffer -+ from ctypes import windll, create_string_buffer, WinError - -- _handles = { -- 0: windll.kernel32.GetStdHandle(-10), -- 1: windll.kernel32.GetStdHandle(-11), -- 2: windll.kernel32.GetStdHandle(-12), -+ _handle_ids = { -+ 0: -10, -+ 1: -11, -+ 2: -12, - } - - def _get_terminal_size(fd): -- columns = lines = 0 -- -- try: -- handle = _handles[fd] -- csbi = create_string_buffer(22) -- res = windll.kernel32.GetConsoleScreenBufferInfo(handle, csbi) -- if res: -- res = struct.unpack("hhhhHhhhhhh", csbi.raw) -- left, top, right, bottom = res[5:9] -- columns = right - left + 1 -- lines = bottom - top + 1 -- except Exception: -- pass -- -- return terminal_size(columns, lines) -+ handle = windll.kernel32.GetStdHandle(_handle_ids[fd]) -+ if handle == 0: -+ raise OSError('handle cannot be retrieved') -+ if handle == -1: -+ raise WinError() -+ csbi = create_string_buffer(22) -+ res = windll.kernel32.GetConsoleScreenBufferInfo(handle, csbi) -+ if res: -+ res = struct.unpack("hhhhHhhhhhh", csbi.raw) -+ left, top, right, bottom = res[5:9] -+ columns = right - left + 1 -+ lines = bottom - top + 1 -+ return terminal_size(columns, lines) -+ else: -+ raise WinError() - - except ImportError: - import fcntl -@@ -48,9 +48,9 @@ - def _get_terminal_size(fd): - try: - res = fcntl.ioctl(fd, termios.TIOCGWINSZ, b"\x00" * 4) -- lines, columns = struct.unpack("hh", res) -- except Exception: -- columns = lines = 0 -+ except IOError as e: -+ raise OSError(e) -+ lines, columns = struct.unpack("hh", res) - - return terminal_size(columns, lines) - diff --git a/build/pkgs/backports_ssl_match_hostname/SPKG.txt b/build/pkgs/backports_ssl_match_hostname/SPKG.txt deleted file mode 100644 index 503b01228f0..00000000000 --- a/build/pkgs/backports_ssl_match_hostname/SPKG.txt +++ /dev/null @@ -1,24 +0,0 @@ -= backports.ssl_match_hostname = - -== Description == - -This backport brings match_hostname() to users of -Python 2.x - -== License == - -Python Software Foundation License - -== Upstream Contact == - -Home page: https://pypi.python.org/pypi/backports.ssl_match_hostname - -== Dependencies == - -Python, Setuptools - -== Special Update/Build Instructions == - -* Unpack the tarball -* rename backports.ssl_match_hostname -> backports_ssl_match_hostname -* Tar as backports_ssl_match_hostname.VERSION.tar.gz diff --git a/build/pkgs/backports_ssl_match_hostname/checksums.ini b/build/pkgs/backports_ssl_match_hostname/checksums.ini deleted file mode 100644 index 25b368b83c4..00000000000 --- a/build/pkgs/backports_ssl_match_hostname/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=backports_ssl_match_hostname-VERSION.tar.gz -sha1=1d7500574eef84c826dfaf507722cd9249bf0672 -md5=c21f63bb4729eeab399932410a012934 -cksum=541176683 diff --git a/build/pkgs/backports_ssl_match_hostname/dependencies b/build/pkgs/backports_ssl_match_hostname/dependencies deleted file mode 100644 index 2573414ce8a..00000000000 --- a/build/pkgs/backports_ssl_match_hostname/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | setuptools pip - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/backports_ssl_match_hostname/package-version.txt b/build/pkgs/backports_ssl_match_hostname/package-version.txt deleted file mode 100644 index fda4604388d..00000000000 --- a/build/pkgs/backports_ssl_match_hostname/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -3.5.0.1.p0 diff --git a/build/pkgs/backports_ssl_match_hostname/spkg-install b/build/pkgs/backports_ssl_match_hostname/spkg-install deleted file mode 100644 index 9cbe3bc3242..00000000000 --- a/build/pkgs/backports_ssl_match_hostname/spkg-install +++ /dev/null @@ -1,14 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing backports.ssl_match_hostname ... exiting" - exit 1 -fi diff --git a/build/pkgs/barvinok/SPKG.rst b/build/pkgs/barvinok/SPKG.rst new file mode 100644 index 00000000000..d24f1f0f9f7 --- /dev/null +++ b/build/pkgs/barvinok/SPKG.rst @@ -0,0 +1,20 @@ +barvinok +======== + +Description +----------- + +barvinok is a library for counting the number of integer points in +parametric and non-parametric polytopes as well as projections of such +sets. + +License +------- + +GPL v2 + + +Upstream Contact +---------------- + +- http://groups.google.com/group/isl-development diff --git a/build/pkgs/barvinok/SPKG.txt b/build/pkgs/barvinok/SPKG.txt deleted file mode 100644 index 6b658a9a21c..00000000000 --- a/build/pkgs/barvinok/SPKG.txt +++ /dev/null @@ -1,15 +0,0 @@ -= barvinok = - -== Description == - -barvinok is a library for counting the number of integer points -in parametric and non-parametric polytopes as well as projections -of such sets. - -== License == - -GPL v2 - -== Upstream Contact == - - * http://groups.google.com/group/isl-development diff --git a/build/pkgs/barvinok/spkg-check b/build/pkgs/barvinok/spkg-check deleted file mode 100644 index 8cd43978e7f..00000000000 --- a/build/pkgs/barvinok/spkg-check +++ /dev/null @@ -1,2 +0,0 @@ -cd src -sdh_make check diff --git a/build/pkgs/barvinok/spkg-check.in b/build/pkgs/barvinok/spkg-check.in new file mode 100644 index 00000000000..45b317a382c --- /dev/null +++ b/build/pkgs/barvinok/spkg-check.in @@ -0,0 +1,2 @@ +cd src +sdh_make_check diff --git a/build/pkgs/barvinok/spkg-install b/build/pkgs/barvinok/spkg-install.in similarity index 100% rename from build/pkgs/barvinok/spkg-install rename to build/pkgs/barvinok/spkg-install.in diff --git a/build/pkgs/beautifulsoup4/requirements.txt b/build/pkgs/beautifulsoup4/requirements.txt new file mode 100644 index 00000000000..c1f5f713cda --- /dev/null +++ b/build/pkgs/beautifulsoup4/requirements.txt @@ -0,0 +1 @@ +beautifulsoup4 diff --git a/build/pkgs/beautifulsoup4/type b/build/pkgs/beautifulsoup4/type index a1b589e38a3..134d9bc32d5 100644 --- a/build/pkgs/beautifulsoup4/type +++ b/build/pkgs/beautifulsoup4/type @@ -1 +1 @@ -pip +optional diff --git a/build/pkgs/benzene/SPKG.rst b/build/pkgs/benzene/SPKG.rst new file mode 100644 index 00000000000..cfec92c289a --- /dev/null +++ b/build/pkgs/benzene/SPKG.rst @@ -0,0 +1,29 @@ +Benzene +======= + +Description +----------- + +Benzene is a program for the efficient generation of all nonisomorphic +fusenes and benzenoids with a given number of faces. Fusenes are planar +polycyclic hydrocarbons with all bounded faces hexagons. Benzenoids are +fusenes that are subgraphs of the hexagonal lattice. + +License +------- + +Benzene is licensed under the GNU General Public License v2 or later +(June 2007) + + +Upstream Contact +---------------- + +Benzene was written by Gunnar Brinkmann and Gilles Caporossi. This +version was adapted by Gunnar Brinkmann and Nico Van Cleemput for +Grinvin. http://www.grinvin.org/ + +Dependencies +------------ + +- None diff --git a/build/pkgs/benzene/SPKG.txt b/build/pkgs/benzene/SPKG.txt deleted file mode 100644 index 9a5f543f797..00000000000 --- a/build/pkgs/benzene/SPKG.txt +++ /dev/null @@ -1,23 +0,0 @@ -= Benzene = - -== Description == -Benzene is a program for the efficient generation of all nonisomorphic fusenes and benzenoids with a given number of faces. Fusenes are planar polycyclic hydrocarbons with all bounded faces hexagons. Benzenoids are fusenes that are subgraphs of the hexagonal lattice. - -== License == -Benzene is licensed under the GNU General Public License v2 or later ( June 2007 ) - -== Upstream Contact == -Benzene was written by Gunnar Brinkmann and Gilles Caporossi. This version was adapted by Gunnar Brinkmann and Nico Van Cleemput for Grinvin. -http://www.grinvin.org/ - -== Dependencies == - * None - -== Changelog == - -=== benzene-20130630 Nico Van Cleemput (10th September 2014) === - * #16963: Update for the sage-git directory layout. - -=== benzene-20130630 Nico Van Cleemput (30th June 2013) === - * First release put into Sage. - diff --git a/build/pkgs/benzene/spkg-install b/build/pkgs/benzene/spkg-install.in similarity index 100% rename from build/pkgs/benzene/spkg-install rename to build/pkgs/benzene/spkg-install.in diff --git a/build/pkgs/biopython/requirements.txt b/build/pkgs/biopython/requirements.txt new file mode 100644 index 00000000000..e0116bd4ed3 --- /dev/null +++ b/build/pkgs/biopython/requirements.txt @@ -0,0 +1 @@ +biopython diff --git a/build/pkgs/biopython/type b/build/pkgs/biopython/type index a1b589e38a3..134d9bc32d5 100644 --- a/build/pkgs/biopython/type +++ b/build/pkgs/biopython/type @@ -1 +1 @@ -pip +optional diff --git a/build/pkgs/bleach/SPKG.rst b/build/pkgs/bleach/SPKG.rst new file mode 100644 index 00000000000..c003a348c41 --- /dev/null +++ b/build/pkgs/bleach/SPKG.rst @@ -0,0 +1,23 @@ +bleach +====== + +Description +----------- + +An easy safelist-based HTML-sanitizing tool. + +License +------- + +Apache License v2 + + +Upstream Contact +---------------- + +Home Page: https://github.com/mozilla/bleach + +Dependencies +------------ + +Python, html5lib, six diff --git a/build/pkgs/bleach/SPKG.txt b/build/pkgs/bleach/SPKG.txt deleted file mode 100644 index d999ee7f8a5..00000000000 --- a/build/pkgs/bleach/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= bleach = - -== Description == - -An easy safelist-based HTML-sanitizing tool. - -== License == - -Apache License v2 - -== Upstream Contact == - -Home Page: https://github.com/mozilla/bleach - -== Dependencies == - -Python, html5lib, six diff --git a/build/pkgs/bleach/dependencies b/build/pkgs/bleach/dependencies index 2573414ce8a..15df0c4d6d8 100644 --- a/build/pkgs/bleach/dependencies +++ b/build/pkgs/bleach/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/bleach/spkg-install b/build/pkgs/bleach/spkg-install.in similarity index 100% rename from build/pkgs/bleach/spkg-install rename to build/pkgs/bleach/spkg-install.in diff --git a/build/pkgs/bliss/SPKG.rst b/build/pkgs/bliss/SPKG.rst new file mode 100644 index 00000000000..1ce6bbf9d56 --- /dev/null +++ b/build/pkgs/bliss/SPKG.rst @@ -0,0 +1,32 @@ + +bliss 0.73+debian-1 +=================== + +Description +----------- + +bliss is an open source tool for computing automorphism groups and +canonical forms of graphs. + +License +------- + +LGPL + + +Upstream Contact +---------------- + +Bliss is currently being maintained by Tommi Junttila and Petteri Kaski. + +http://www.tcs.tkk.fi/Software/bliss/index.html + +We apply patches generated from https://github.com/mkoeppe/bliss (branch +apply_debian_patches) as our upstream. This tracks the patches from the +Debian package, adding an autotools build system and adjusting the +include file locations. + +Dependencies +------------ + +None diff --git a/build/pkgs/bliss/SPKG.txt b/build/pkgs/bliss/SPKG.txt deleted file mode 100644 index cff1683e16e..00000000000 --- a/build/pkgs/bliss/SPKG.txt +++ /dev/null @@ -1,26 +0,0 @@ -= bliss 0.73+debian-1 = - -== Description == - -bliss is an open source tool for computing automorphism groups and canonical -forms of graphs. - -== License == - -LGPL - -== Upstream Contact == - -Bliss is currently being maintained by Tommi Junttila and Petteri Kaski. - -http://www.tcs.tkk.fi/Software/bliss/index.html - -We apply patches generated from https://github.com/mkoeppe/bliss -(branch apply_debian_patches) as our upstream. This tracks the -patches from the Debian package, adding an autotools build system -and adjusting the include file locations. - -== Dependencies == - -None - diff --git a/build/pkgs/bliss/spkg-install b/build/pkgs/bliss/spkg-install.in similarity index 100% rename from build/pkgs/bliss/spkg-install rename to build/pkgs/bliss/spkg-install.in diff --git a/build/pkgs/boost/SPKG.rst b/build/pkgs/boost/SPKG.rst new file mode 100644 index 00000000000..13fdbc2df52 --- /dev/null +++ b/build/pkgs/boost/SPKG.rst @@ -0,0 +1,23 @@ +boost +===== + +Description +----------- + +Boost provides free peer-reviewed portable C++ source libraries. + +License +------- + +Boost software license (GPL compatible) + + +Upstream Contact +---------------- + +Home page: http://boost.org + +Dependencies +------------ + +None diff --git a/build/pkgs/boost/SPKG.txt b/build/pkgs/boost/SPKG.txt deleted file mode 100644 index 950edcbbcea..00000000000 --- a/build/pkgs/boost/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= boost = - -== Description == - -Boost provides free peer-reviewed portable C++ source libraries. - -== License == - -Boost software license (GPL compatible) - -== Upstream Contact == - -Home page: http://boost.org - -== Dependencies == - -None diff --git a/build/pkgs/boost/distros/cygwin.txt b/build/pkgs/boost/distros/cygwin.txt new file mode 100644 index 00000000000..444ab77a410 --- /dev/null +++ b/build/pkgs/boost/distros/cygwin.txt @@ -0,0 +1 @@ +libboost-devel diff --git a/build/pkgs/boost/distros/homebrew.txt b/build/pkgs/boost/distros/homebrew.txt new file mode 100644 index 00000000000..d579dbe4edb --- /dev/null +++ b/build/pkgs/boost/distros/homebrew.txt @@ -0,0 +1 @@ +boost diff --git a/build/pkgs/boost/distros/slackware.txt b/build/pkgs/boost/distros/slackware.txt new file mode 100644 index 00000000000..d579dbe4edb --- /dev/null +++ b/build/pkgs/boost/distros/slackware.txt @@ -0,0 +1 @@ +boost diff --git a/build/pkgs/boost/spkg-install b/build/pkgs/boost/spkg-install deleted file mode 100644 index 24b3c2e804b..00000000000 --- a/build/pkgs/boost/spkg-install +++ /dev/null @@ -1,59 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -echo "Running boost bootstrap" -# We provide the toolset as the only toolset properly auto-detected are -# gcc (or icc) on linux -# clang on OS X -# but not clang on linux for example -# First sanitize toolset name from CC, it assume something of the form -# CC=/path/to/CC_name-version at worst. -# Please do not mix CC and CXX from different vendors. -C_COMPILER_NAME=${CC##*/} -C_COMPILER_NAME=${C_COMPILER_NAME%-*} -BOOST_TOOLSET="" -if [ "$UNAME" = "Darwin" ]; then - # On darwin provided the compiler is clang, default is fine. - # Remember that /usr/bin/gcc is clang. - if [ "$(which $C_COMPILER_NAME)" != "/usr/bin/gcc" -a "$C_COMPILER_NAME" != "clang" ]; then - BOOST_TOOLSET="--with-toolset=$C_COMPILER_NAME" - fi -else - BOOST_TOOLSET="--with-toolset=$C_COMPILER_NAME" -fi - -./bootstrap.sh $BOOST_TOOLSET -if [[ $? -ne 0 ]]; then - echo >&2 "Failed to bootstrap boost." - exit 1 -fi - -echo "Building boost" -# By default this is populated by a system value. -# If the boost build system (b2, bjam and associated files under /usr/share/boost-build) -# has been installed system wide it can cause interference. -# The build will fail purely and simply without much of an explanation. -# see http://trac.sagemath.org/ticket/20776 for details. -export BOOST_BUILD_PATH="${SAGE_LOCAL}"/share/boost-build -./b2 -if [[ $? -ne 0 ]]; then - echo >&2 "Failed to build boost." - exit 1 -fi - -echo "Clean out old boost headers and libraries" -rm -rf "$SAGE_LOCAL"/include/boost -rm -rf "$SAGE_LOCAL"/lib/libboost* - -echo "Installing boost" -./b2 install --prefix="$SAGE_LOCAL" -if [[ $? -ne 0 ]]; then - echo >&2 "Failed to install boost." - exit 1 -fi - diff --git a/build/pkgs/boost/spkg-install.in b/build/pkgs/boost/spkg-install.in new file mode 100644 index 00000000000..348a434de5d --- /dev/null +++ b/build/pkgs/boost/spkg-install.in @@ -0,0 +1,53 @@ +cd src + +echo "Running boost bootstrap" +# We provide the toolset as the only toolset properly auto-detected are +# gcc (or icc) on linux +# clang on OS X +# but not clang on linux for example +# First sanitize toolset name from CC, it assume something of the form +# CC=/path/to/CC_name-version at worst. +# Please do not mix CC and CXX from different vendors. +C_COMPILER_NAME=${CC##*/} +C_COMPILER_NAME=${C_COMPILER_NAME%-*} +BOOST_TOOLSET="" +if [ "$UNAME" = "Darwin" ]; then + # On darwin provided the compiler is clang, default is fine. + # Remember that /usr/bin/gcc is clang. + if [ "$(which $C_COMPILER_NAME)" != "/usr/bin/gcc" -a "$C_COMPILER_NAME" != "clang" ]; then + BOOST_TOOLSET="--with-toolset=$C_COMPILER_NAME" + fi +else + BOOST_TOOLSET="--with-toolset=$C_COMPILER_NAME" +fi + +./bootstrap.sh $BOOST_TOOLSET +if [[ $? -ne 0 ]]; then + echo >&2 "Failed to bootstrap boost." + exit 1 +fi + +echo "Building boost" +# By default this is populated by a system value. +# If the boost build system (b2, bjam and associated files under /usr/share/boost-build) +# has been installed system wide it can cause interference. +# The build will fail purely and simply without much of an explanation. +# see http://trac.sagemath.org/ticket/20776 for details. +export BOOST_BUILD_PATH="${SAGE_LOCAL}"/share/boost-build +./b2 +if [[ $? -ne 0 ]]; then + echo >&2 "Failed to build boost." + exit 1 +fi + +echo "Clean out old boost headers and libraries" +rm -rf "$SAGE_LOCAL"/include/boost +rm -rf "$SAGE_LOCAL"/lib/libboost* + +echo "Installing boost" +./b2 install --prefix="$SAGE_LOCAL" +if [[ $? -ne 0 ]]; then + echo >&2 "Failed to install boost." + exit 1 +fi + diff --git a/build/pkgs/boost_cropped/SPKG.rst b/build/pkgs/boost_cropped/SPKG.rst new file mode 100644 index 00000000000..33336a460f1 --- /dev/null +++ b/build/pkgs/boost_cropped/SPKG.rst @@ -0,0 +1,38 @@ +Boost +===== + +Description +----------- + +Boost provides free peer-reviewed portable C++ source libraries. + +We emphasize libraries that work well with the C++ Standard Library. +Boost libraries are intended to be widely useful, and usable across a +broad spectrum of applications. The Boost license encourages both +commercial and non-commercial use. + +We aim to establish "existing practice" and provide reference +implementations so that Boost libraries are suitable for eventual +standardization. Ten Boost libraries are already included in the C++ +Standards Committee's Library Technical Report (TR1) and will be in the +new C++0x Standard now being finalized. C++0x will also include several +more Boost libraries in addition to those from TR1. More Boost libraries +are proposed for TR2. + +Website: http://www.boost.org/ + +License +------- + +Boost Software License - see http://www.boost.org/users/license.html + + +Upstream Contact +---------------- + +See mailing list page at http://www.boost.org/community/groups.html + +Dependencies +------------ + +None diff --git a/build/pkgs/boost_cropped/SPKG.txt b/build/pkgs/boost_cropped/SPKG.txt deleted file mode 100644 index 0ed6c59696b..00000000000 --- a/build/pkgs/boost_cropped/SPKG.txt +++ /dev/null @@ -1,42 +0,0 @@ -= Boost = - -== Description == - -Boost provides free peer-reviewed portable C++ source libraries. - -We emphasize libraries that work well with the C++ Standard Library. Boost libraries are intended to be widely useful, and usable across a broad spectrum of applications. The Boost license encourages both commercial and non-commercial use. - -We aim to establish "existing practice" and provide reference implementations so that Boost libraries are suitable for eventual standardization. Ten Boost libraries are already included in the C++ Standards Committee's Library Technical Report (TR1) and will be in the new C++0x Standard now being finalized. C++0x will also include several more Boost libraries in addition to those from TR1. More Boost libraries are proposed for TR2. - -Website: http://www.boost.org/ - -== License == - -Boost Software License - see http://www.boost.org/users/license.html - -== Upstream Contact == - -See mailing list page at http://www.boost.org/community/groups.html - -== Dependencies == - -None - -== Releases == - -=== boost_cropped-1.58.0 (Emmanuel Charpentier, November 13th, 2015) === - * Updated to boost_1_58_0/boost - * Created spkg-src - -=== boost_cropped-1.52.0 (Timo Kluck, February 26th, 2013) === - * Drop sage patches; take all upstream headers - - This package contains the complete contents of the directory - - boost_1_52_0/boost - - of the upstream tarball. - -=== boost-cropped-1.34.1 (Michael Abshoff, May 15th, 2009) === - * Split boost sources off of polybori.spkg - diff --git a/build/pkgs/boost_cropped/distros/cygwin.txt b/build/pkgs/boost_cropped/distros/cygwin.txt new file mode 100644 index 00000000000..444ab77a410 --- /dev/null +++ b/build/pkgs/boost_cropped/distros/cygwin.txt @@ -0,0 +1 @@ +libboost-devel diff --git a/build/pkgs/boost_cropped/distros/homebrew.txt b/build/pkgs/boost_cropped/distros/homebrew.txt new file mode 100644 index 00000000000..d579dbe4edb --- /dev/null +++ b/build/pkgs/boost_cropped/distros/homebrew.txt @@ -0,0 +1 @@ +boost diff --git a/build/pkgs/boost_cropped/distros/slackware.txt b/build/pkgs/boost_cropped/distros/slackware.txt new file mode 100644 index 00000000000..d579dbe4edb --- /dev/null +++ b/build/pkgs/boost_cropped/distros/slackware.txt @@ -0,0 +1 @@ +boost diff --git a/build/pkgs/boost_cropped/spkg-install b/build/pkgs/boost_cropped/spkg-install.in similarity index 100% rename from build/pkgs/boost_cropped/spkg-install rename to build/pkgs/boost_cropped/spkg-install.in diff --git a/build/pkgs/boost_cropped/spkg-legacy-uninstall b/build/pkgs/boost_cropped/spkg-legacy-uninstall.in similarity index 100% rename from build/pkgs/boost_cropped/spkg-legacy-uninstall rename to build/pkgs/boost_cropped/spkg-legacy-uninstall.in diff --git a/build/pkgs/brial/SPKG.rst b/build/pkgs/brial/SPKG.rst new file mode 100644 index 00000000000..93e89b2205f --- /dev/null +++ b/build/pkgs/brial/SPKG.rst @@ -0,0 +1,28 @@ +BRiAl +===== + +Description +----------- + +BRiAl is the successor to PolyBoRi. + +The core of PolyBoRi is a C++ library, which provides high-level data +types for Boolean polynomials and monomials, exponent vectors, as well +as for the underlying polynomial rings and subsets of the powerset of +the Boolean variables. As a unique approach, binary decision diagrams +are used as internal storage type for polynomial structures. On top of +this C++-library we provide a Python interface. This allows parsing of +complex polynomial systems, as well as sophisticated and extendable +strategies for Gröbner base computation. PolyBoRi features a powerful +reference implementation for Gröbner basis computation. + +License +------- + +GPL version 2 or later + + +Upstream Contact +---------------- + +https://github.com/BRiAl/BRiAl diff --git a/build/pkgs/brial/SPKG.txt b/build/pkgs/brial/SPKG.txt deleted file mode 100644 index 3525513c243..00000000000 --- a/build/pkgs/brial/SPKG.txt +++ /dev/null @@ -1,23 +0,0 @@ -= BRiAl = - -== Description == - -BRiAl is the successor to PolyBoRi. - -The core of PolyBoRi is a C++ library, which provides high-level data -types for Boolean polynomials and monomials, exponent vectors, as well -as for the underlying polynomial rings and subsets of the powerset of -the Boolean variables. As a unique approach, binary decision diagrams -are used as internal storage type for polynomial structures. On top of -this C++-library we provide a Python interface. This allows parsing of -complex polynomial systems, as well as sophisticated and extendable -strategies for Gröbner base computation. PolyBoRi features a powerful -reference implementation for Gröbner basis computation. - -== License == - -GPL version 2 or later - -== Upstream Contact == - -https://github.com/BRiAl/BRiAl diff --git a/build/pkgs/brial/checksums.ini b/build/pkgs/brial/checksums.ini index a587613da7f..2f3607207f7 100644 --- a/build/pkgs/brial/checksums.ini +++ b/build/pkgs/brial/checksums.ini @@ -1,4 +1,5 @@ tarball=brial-VERSION.tar.bz2 -sha1=5795c0d73b63e9daa5318b0f22514b7797c59823 -md5=fddbc0cebfbac161de110acf30a6b89d -cksum=2578889224 +sha1=ea69faff56fb7068536723f3fb5b3583b8467831 +md5=d6c6a01d4fc80062550e02d9185bfbff +cksum=318826732 +upstream_url=https://github.com/BRiAl/BRiAl/releases/download/VERSION/brial-VERSION.tar.bz2 diff --git a/build/pkgs/brial/dependencies b/build/pkgs/brial/dependencies index f6e60fbb6f3..1944cbeb530 100644 --- a/build/pkgs/brial/dependencies +++ b/build/pkgs/brial/dependencies @@ -1,4 +1,4 @@ -boost_cropped m4ri libpng $(PYTHON) | pip pkgconf +boost_cropped m4ri libpng | pkgconf ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/brial/distros/arch.txt b/build/pkgs/brial/distros/arch.txt new file mode 100644 index 00000000000..96f2ad18fa7 --- /dev/null +++ b/build/pkgs/brial/distros/arch.txt @@ -0,0 +1 @@ +brial diff --git a/build/pkgs/brial/distros/conda.txt b/build/pkgs/brial/distros/conda.txt new file mode 100644 index 00000000000..96f2ad18fa7 --- /dev/null +++ b/build/pkgs/brial/distros/conda.txt @@ -0,0 +1 @@ +brial diff --git a/build/pkgs/brial/distros/debian.txt b/build/pkgs/brial/distros/debian.txt new file mode 100644 index 00000000000..6e5844815be --- /dev/null +++ b/build/pkgs/brial/distros/debian.txt @@ -0,0 +1 @@ +libbrial-dev libbrial-groebner-dev diff --git a/build/pkgs/brial/distros/fedora.txt b/build/pkgs/brial/distros/fedora.txt new file mode 100644 index 00000000000..d57478d44c4 --- /dev/null +++ b/build/pkgs/brial/distros/fedora.txt @@ -0,0 +1,2 @@ +brial +brial-devel diff --git a/build/pkgs/brial/distros/gentoo.txt b/build/pkgs/brial/distros/gentoo.txt new file mode 100644 index 00000000000..3414c20f57d --- /dev/null +++ b/build/pkgs/brial/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/brial diff --git a/build/pkgs/brial/distros/nix.txt b/build/pkgs/brial/distros/nix.txt new file mode 100644 index 00000000000..96f2ad18fa7 --- /dev/null +++ b/build/pkgs/brial/distros/nix.txt @@ -0,0 +1 @@ +brial diff --git a/build/pkgs/brial/package-version.txt b/build/pkgs/brial/package-version.txt index c813fe116c9..db6fb4a9113 100644 --- a/build/pkgs/brial/package-version.txt +++ b/build/pkgs/brial/package-version.txt @@ -1 +1 @@ -1.2.5 +1.2.8 diff --git a/build/pkgs/brial/spkg-configure.m4 b/build/pkgs/brial/spkg-configure.m4 new file mode 100644 index 00000000000..3d69c88f82d --- /dev/null +++ b/build/pkgs/brial/spkg-configure.m4 @@ -0,0 +1,63 @@ +SAGE_SPKG_CONFIGURE([brial], [ + SAGE_SPKG_DEPCHECK([boost m4ri], [ + # If we're using the system m4ri and boost, ensure that we can + # compile and run an executable linked against both libbrial and + # libbrial_groebner (both are used by SageMath). + AC_LANG_PUSH(C++) + SAVED_LIBS=$LIBS + LIBS="$LIBS -lbrial -lbrial_groebner" + AC_MSG_CHECKING([if we can link against brial libraries]) + AC_RUN_IFELSE([ + AC_LANG_PROGRAM([ + #include + #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; + ]) + ], + [ + dnl check we're not on Fedora 30 - more precisely, we reject version 1.2.5 + dnl for which the version is verifiable by the following code. + AC_MSG_CHECKING([version not equal to 1.2.5]) + AC_RUN_IFELSE([ + AC_LANG_PROGRAM( + [[#include + #include + ]], [[ + if (VERSION=="1.2.5") return 0; + else return 1; + ]]) + ], [ + AC_MSG_RESULT([found a possibly buggy 1.2.5. Rejecting]) + sage_spkg_install_brial=yes + ], [ + 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/brial/spkg-install b/build/pkgs/brial/spkg-install deleted file mode 100644 index a59dc244f48..00000000000 --- a/build/pkgs/brial/spkg-install +++ /dev/null @@ -1,21 +0,0 @@ -cd src - -export PYTHON=sage-python23 - -# -# BRiAl consists of a C++ library and a Python module -# -# First, install the library -# - -sdh_configure \ - --enable-shared --disable-static \ - --with-boost-unit-test-framework=no -sdh_make -sdh_make_install - -# -# Next, install the Python module -# - -cd sage-brial && sdh_pip_install . diff --git a/build/pkgs/brial/spkg-install.in b/build/pkgs/brial/spkg-install.in new file mode 100644 index 00000000000..e048eda6bdc --- /dev/null +++ b/build/pkgs/brial/spkg-install.in @@ -0,0 +1,11 @@ +# +# BRiAl consists of a both C++ library and a SageMath-specific python +# module. This spkg installs only the C++ library; the python module +# is installed by the "sage_brial" spkg. +# +cd src +sdh_configure \ + --enable-shared --disable-static \ + --with-boost-unit-test-framework=no +sdh_make +sdh_make_install diff --git a/build/pkgs/brial/spkg-legacy-uninstall b/build/pkgs/brial/spkg-legacy-uninstall deleted file mode 100644 index 782687d6d41..00000000000 --- a/build/pkgs/brial/spkg-legacy-uninstall +++ /dev/null @@ -1,5 +0,0 @@ -echo "Cleaning out old PolyBoRi and BRiAl installations" -rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/{polybori,brial} -rm -f "$SAGE_LOCAL"/lib/lib{polybori,brial}* -rm -rf "$SAGE_LOCAL"/include/polybori* -rm -rf "$SAGE_LOCAL"/share/polybori diff --git a/build/pkgs/brial/spkg-legacy-uninstall.in b/build/pkgs/brial/spkg-legacy-uninstall.in new file mode 100644 index 00000000000..8349992e01c --- /dev/null +++ b/build/pkgs/brial/spkg-legacy-uninstall.in @@ -0,0 +1,4 @@ +echo "Cleaning out old PolyBoRi and BRiAl installations" +rm -f "$SAGE_LOCAL"/lib/lib{polybori,brial}* +rm -rf "$SAGE_LOCAL"/include/polybori* +rm -rf "$SAGE_LOCAL"/share/polybori diff --git a/build/pkgs/buckygen/SPKG.rst b/build/pkgs/buckygen/SPKG.rst new file mode 100644 index 00000000000..62409dc6a3e --- /dev/null +++ b/build/pkgs/buckygen/SPKG.rst @@ -0,0 +1,28 @@ +Buckygen +======== + +Description +----------- + +Buckygen is a program for the efficient generation of all nonisomorphic +fullerenes. These are triangulations where all vertices have degree 5 or +6. Or if the dual representation is used: cubic plane graphs where all +faces are pentagons or hexagons. + +License +------- + +Buckygen is licensed under the GNU General Public License v3 ( June 2007 +) + + +Upstream Contact +---------------- + +Buckygen was mainly written by Jan Goedgebeur, +jan.goedgebeur[at]ugent.be. http://caagt.ugent.be/buckygen/ + +Dependencies +------------ + +- None diff --git a/build/pkgs/buckygen/SPKG.txt b/build/pkgs/buckygen/SPKG.txt deleted file mode 100644 index b2c90529321..00000000000 --- a/build/pkgs/buckygen/SPKG.txt +++ /dev/null @@ -1,26 +0,0 @@ -= Buckygen = - -== Description == -Buckygen is a program for the efficient generation of all nonisomorphic fullerenes. These are triangulations where all vertices have degree 5 or 6. Or if the dual representation is used: cubic plane graphs where all faces are pentagons or hexagons. - -== License == -Buckygen is licensed under the GNU General Public License v3 ( June 2007 ) - -== Upstream Contact == -Buckygen was mainly written by Jan Goedgebeur, jan.goedgebeur[at]ugent.be. -http://caagt.ugent.be/buckygen/ - -== Dependencies == - * None - -== Changelog == - -=== buckygen-1.0 (Nico Van Cleemput, 9th September 2014) === - * #16945: Update for the sage-git directory layout. - -=== buckygen-1.0 (Nico Van Cleemput, 3rd September 2013) === - * Fixed error in license info. - -=== buckygen-1.0 (Nico Van Cleemput, 25th May 2013) === - * First release put into Sage. - diff --git a/build/pkgs/buckygen/spkg-install b/build/pkgs/buckygen/spkg-install.in similarity index 100% rename from build/pkgs/buckygen/spkg-install rename to build/pkgs/buckygen/spkg-install.in diff --git a/build/pkgs/bzip2/SPKG.rst b/build/pkgs/bzip2/SPKG.rst new file mode 100644 index 00000000000..b6aeb3c0d61 --- /dev/null +++ b/build/pkgs/bzip2/SPKG.rst @@ -0,0 +1,42 @@ +bzip2 +===== + +Description +----------- + +bzip2 is a freely available, patent free, high-quality data compressor. +It typically compresses files to within 10% to 15% of the best available +techniques (the PPM family of statistical compressors), whilst being +around twice as fast at compression and six times faster at +decompression. + +License +------- + +BSD-style + + +Upstream Contact +---------------- + +- Website http://bzip.org/ +- Author: Julian Seward + +Dependencies +------------ + +None + + +Special Update/Build Instructions +--------------------------------- + +This package must not be bzip2 compressed, so create it using :: + + tar c bzip2-1.0.6 | gzip --best >bzip2-1.0.6.spkg + +The build system has been autotoolized based on a patch by the Suse folk +at +http://ftp.uni-kl.de/pub/linux/suse/people/sbrabec/bzip2/for_downstream/bzip2-1.0.6-autoconfiscated.patch + +See patches/autotools and spkg-src for details. diff --git a/build/pkgs/bzip2/SPKG.txt b/build/pkgs/bzip2/SPKG.txt deleted file mode 100644 index 63950e6014d..00000000000 --- a/build/pkgs/bzip2/SPKG.txt +++ /dev/null @@ -1,32 +0,0 @@ -= bzip2 = - -== Description == - -bzip2 is a freely available, patent free, high-quality data compressor. -It typically compresses files to within 10% to 15% of the best available -techniques (the PPM family of statistical compressors), whilst being -around twice as fast at compression and six times faster at -decompression. - -== License == - -BSD-style - -== Upstream Contact == - - * Website http://bzip.org/ - * Author: Julian Seward - -== Dependencies == - -None - -== Special Update/Build Instructions == - -This package must not be bzip2 compressed, so create it using -tar c bzip2-1.0.6 | gzip --best >bzip2-1.0.6.spkg - -The build system has been autotoolized based on a patch by the Suse folk at -http://ftp.uni-kl.de/pub/linux/suse/people/sbrabec/bzip2/for_downstream/bzip2-1.0.6-autoconfiscated.patch - -See patches/autotools and spkg-src for details. diff --git a/build/pkgs/bzip2/distros/cygwin.txt b/build/pkgs/bzip2/distros/cygwin.txt new file mode 100644 index 00000000000..8e127a3d331 --- /dev/null +++ b/build/pkgs/bzip2/distros/cygwin.txt @@ -0,0 +1 @@ +bzip2 libbz2-devel diff --git a/build/pkgs/bzip2/distros/slackware.txt b/build/pkgs/bzip2/distros/slackware.txt new file mode 100644 index 00000000000..7a457127148 --- /dev/null +++ b/build/pkgs/bzip2/distros/slackware.txt @@ -0,0 +1 @@ +bzip2 diff --git a/build/pkgs/bzip2/spkg-check b/build/pkgs/bzip2/spkg-check.in similarity index 100% rename from build/pkgs/bzip2/spkg-check rename to build/pkgs/bzip2/spkg-check.in diff --git a/build/pkgs/bzip2/spkg-install b/build/pkgs/bzip2/spkg-install.in similarity index 100% rename from build/pkgs/bzip2/spkg-install rename to build/pkgs/bzip2/spkg-install.in diff --git a/build/pkgs/cbc/SPKG.rst b/build/pkgs/cbc/SPKG.rst new file mode 100644 index 00000000000..44c316385c8 --- /dev/null +++ b/build/pkgs/cbc/SPKG.rst @@ -0,0 +1,35 @@ + +COIN-OR / CBC +============= + +Description +----------- + +The Computational Infrastructure for Operations Research (COIN-OR**, or +simply COIN) project is an initiative to spur the development of +open-source software for the operations research community. + +The COIN Branch and Cut solver (CBC) is an open-source mixed-integer +program (MIP) solver written in C++. CBC is intended to be used +primarily as a callable library to create customized branch-and-cut +solvers. A basic, stand-alone executable version is also available. CBC +is an active open-source project led by John Forrest at www.coin-or.org. + +License +------- + +Eclipse Public License, Version 1.0 (EPL-1.0) +(http://opensource.org/licenses/eclipse-1.0) + + +Upstream Contact +---------------- + +- John Forrest +- Robin Lougee-Heimer + + +Project Home Page +----------------- + +- https://projects.coin-or.org/Cbc diff --git a/build/pkgs/cbc/SPKG.txt b/build/pkgs/cbc/SPKG.txt deleted file mode 100644 index 5d0d63b3728..00000000000 --- a/build/pkgs/cbc/SPKG.txt +++ /dev/null @@ -1,28 +0,0 @@ -= COIN-OR / CBC = - -== Description == - -The Computational Infrastructure for Operations Research (COIN-OR**, -or simply COIN) project is an initiative to spur the development of -open-source software for the operations research community. - -The COIN Branch and Cut solver (CBC) is an open-source -mixed-integer program (MIP) solver written in C++. CBC is intended to -be used primarily as a callable library to create customized -branch-and-cut solvers. A basic, stand-alone executable version is -also available. CBC is an active open-source project led by John -Forrest at www.coin-or.org. - -== License == - -Eclipse Public License, Version 1.0 (EPL-1.0) -(http://opensource.org/licenses/eclipse-1.0) - -== Upstream Contact == - - * John Forrest - * Robin Lougee-Heimer - -== Project Home Page == - - * https://projects.coin-or.org/Cbc diff --git a/build/pkgs/cbc/distros/fedora.txt b/build/pkgs/cbc/distros/fedora.txt new file mode 100644 index 00000000000..3e1af7ad66b --- /dev/null +++ b/build/pkgs/cbc/distros/fedora.txt @@ -0,0 +1 @@ +coin-or-Cbc coin-or-Cbc-devel diff --git a/build/pkgs/cbc/spkg-install b/build/pkgs/cbc/spkg-install.in similarity index 100% rename from build/pkgs/cbc/spkg-install rename to build/pkgs/cbc/spkg-install.in diff --git a/build/pkgs/ccache/SPKG.rst b/build/pkgs/ccache/SPKG.rst new file mode 100644 index 00000000000..bade4edeb0b --- /dev/null +++ b/build/pkgs/ccache/SPKG.rst @@ -0,0 +1,22 @@ +ccache +====== + +Description +----------- + +ccache is a compiler cache. It speeds up recompilation by caching +previous compilations and detecting when the same compilation is being +done again. Supported languages are C, C++, Objective-C and +Objective-C++. + +License +------- + +GNU General Public License version 3 or later + + +Upstream Contact +---------------- + +- Author: Andrew Tridgell +- Website: http://ccache.samba.org/ diff --git a/build/pkgs/ccache/SPKG.txt b/build/pkgs/ccache/SPKG.txt deleted file mode 100644 index f8abc4bb638..00000000000 --- a/build/pkgs/ccache/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= ccache = - -== Description == - -ccache is a compiler cache. It speeds up recompilation by caching -previous compilations and detecting when the same compilation is being -done again. -Supported languages are C, C++, Objective-C and Objective-C++. - -== License == - -GNU General Public License version 3 or later - -== Upstream Contact == - - * Author: Andrew Tridgell - * Website: http://ccache.samba.org/ diff --git a/build/pkgs/ccache/spkg-check b/build/pkgs/ccache/spkg-check.in similarity index 100% rename from build/pkgs/ccache/spkg-check rename to build/pkgs/ccache/spkg-check.in diff --git a/build/pkgs/ccache/spkg-install b/build/pkgs/ccache/spkg-install.in similarity index 100% rename from build/pkgs/ccache/spkg-install rename to build/pkgs/ccache/spkg-install.in diff --git a/build/pkgs/cddlib/SPKG.rst b/build/pkgs/cddlib/SPKG.rst new file mode 100644 index 00000000000..406cfb74d91 --- /dev/null +++ b/build/pkgs/cddlib/SPKG.rst @@ -0,0 +1,38 @@ +cddlib +====== + +Description +----------- + +The C-library cddlib is a C implementation of the Double Description +Method of Motzkin et al. for generating all vertices (i.e. extreme +points) and extreme rays of a general convex polyhedron in R^d given by +a system of linear inequalities: + + P = { x=(x1, ..., xd)^T : b - A x >= 0 } + +where A is a given m x d real matrix, b is a given m-vector and 0 is the +m-vector of all zeros. + +The program can be used for the reverse operation (i.e. convex hull +computation). This means that one can move back and forth between an +inequality representation and a generator (i.e. vertex and ray) +representation of a polyhedron with cdd. Also, cdd can solve a linear +programming problem, i.e. a problem of maximizing and minimizing a +linear function over P. + +License +------- + +GPL v2 + + +Upstream Contact +---------------- + +https://github.com/cddlib/cddlib + +Dependencies +------------ + +- gmp (or its fork mpir) diff --git a/build/pkgs/cddlib/SPKG.txt b/build/pkgs/cddlib/SPKG.txt deleted file mode 100644 index 3254e8cef89..00000000000 --- a/build/pkgs/cddlib/SPKG.txt +++ /dev/null @@ -1,28 +0,0 @@ -= cddlib = - -== Description == -The C-library cddlib is a C implementation of the Double Description -Method of Motzkin et al. for generating all vertices (i.e. extreme points) -and extreme rays of a general convex polyhedron in R^d given by a system -of linear inequalities: - - P = { x=(x1, ..., xd)^T : b - A x >= 0 } - -where A is a given m x d real matrix, b is a given m-vector -and 0 is the m-vector of all zeros. - -The program can be used for the reverse operation (i.e. convex hull -computation). This means that one can move back and forth between -an inequality representation and a generator (i.e. vertex and ray) -representation of a polyhedron with cdd. Also, cdd can solve a linear -programming problem, i.e. a problem of maximizing and minimizing -a linear function over P. - -== License == -GPL v2 - -== Upstream Contact == -https://github.com/cddlib/cddlib - -== Dependencies == - * gmp (or its fork mpir) diff --git a/build/pkgs/cddlib/checksums.ini b/build/pkgs/cddlib/checksums.ini index 943d71a78c6..1baefa11c19 100644 --- a/build/pkgs/cddlib/checksums.ini +++ b/build/pkgs/cddlib/checksums.ini @@ -2,3 +2,4 @@ tarball=cddlib-VERSION.tar.gz sha1=4bad24b32d9c056615377ff3103e6b95e17628ac md5=73e5f7dfa72b5c3339c09564721813d6 cksum=3211115816 +upstream_url=https://github.com/cddlib/cddlib/releases/download/VERSION/cddlib-VERSION.tar.gz diff --git a/build/pkgs/cddlib/distros/arch.txt b/build/pkgs/cddlib/distros/arch.txt new file mode 100644 index 00000000000..f9afcc0b330 --- /dev/null +++ b/build/pkgs/cddlib/distros/arch.txt @@ -0,0 +1 @@ +cddlib diff --git a/build/pkgs/cddlib/distros/cygwin.txt b/build/pkgs/cddlib/distros/cygwin.txt new file mode 100644 index 00000000000..f9eaae4c7ad --- /dev/null +++ b/build/pkgs/cddlib/distros/cygwin.txt @@ -0,0 +1 @@ +cddlib-devel cddlib-tools diff --git a/build/pkgs/cddlib/distros/debian.txt b/build/pkgs/cddlib/distros/debian.txt new file mode 100644 index 00000000000..c7bdbe3b085 --- /dev/null +++ b/build/pkgs/cddlib/distros/debian.txt @@ -0,0 +1 @@ +libcdd-dev libcdd-tools diff --git a/build/pkgs/cddlib/distros/fedora.txt b/build/pkgs/cddlib/distros/fedora.txt new file mode 100644 index 00000000000..f9afcc0b330 --- /dev/null +++ b/build/pkgs/cddlib/distros/fedora.txt @@ -0,0 +1 @@ +cddlib diff --git a/build/pkgs/cddlib/distros/freebsd.txt b/build/pkgs/cddlib/distros/freebsd.txt new file mode 100644 index 00000000000..14e2777cd05 --- /dev/null +++ b/build/pkgs/cddlib/distros/freebsd.txt @@ -0,0 +1 @@ +math/cddlib diff --git a/build/pkgs/cddlib/distros/gentoo.txt b/build/pkgs/cddlib/distros/gentoo.txt new file mode 100644 index 00000000000..87d6dce523d --- /dev/null +++ b/build/pkgs/cddlib/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/cddlib diff --git a/build/pkgs/cddlib/distros/nix.txt b/build/pkgs/cddlib/distros/nix.txt new file mode 100644 index 00000000000..f9afcc0b330 --- /dev/null +++ b/build/pkgs/cddlib/distros/nix.txt @@ -0,0 +1 @@ +cddlib diff --git a/build/pkgs/cddlib/distros/opensuse.txt b/build/pkgs/cddlib/distros/opensuse.txt new file mode 100644 index 00000000000..f9afcc0b330 --- /dev/null +++ b/build/pkgs/cddlib/distros/opensuse.txt @@ -0,0 +1 @@ +cddlib diff --git a/build/pkgs/cddlib/spkg-configure.m4 b/build/pkgs/cddlib/spkg-configure.m4 new file mode 100644 index 00000000000..d18080209cb --- /dev/null +++ b/build/pkgs/cddlib/spkg-configure.m4 @@ -0,0 +1,36 @@ +SAGE_SPKG_CONFIGURE([cddlib], [ + SAGE_SPKG_DEPCHECK([gmp mpir], [ + dnl The sage library uses BOTH cddexec and cddexec_gmp. + dnl These two executables were introduced in cddlib-094j. + AC_CHECK_PROGS([CDDEXEC], [cddexec]) + AS_IF([test x$CDDEXEC = x], [sage_spkg_install_cddlib=yes]) + + AC_CHECK_PROGS([CDDEXECGMP], [cddexec_gmp]) + AS_IF([test x$CDDEXECGMP = x], [sage_spkg_install_cddlib=yes]) + + dnl LattE needs redcheck_gmp... + AC_CHECK_PROGS([REDCHECKGMP], [redcheck_gmp]) + AS_IF([test x$REDCHECKGMP = x], [sage_spkg_install_cddlib=yes]) + + dnl and EITHER scdd or scdd_gmp. + AC_CHECK_PROGS(SCDD, [scdd_gmp scdd]) + AS_IF([test x$SCDD = x], [sage_spkg_install_cddlib=yes]) + + dnl Future versions (>= 0.94k) of cddlib will put these headers in + dnl a "cddlib" subdirectory, and Debian currently relocates them + dnl under "cdd". But for now they're at the top-level, in e.g. + dnl /usr/include/cdd.h. The lattE and gfan packages within + dnl SageMath both look for them there, so that's where we have to + dnl check, passing up a chance to detect cddlib on Fedora and Debian + dnl for now. Once all of cddlib's consumers know about the new (or + dnl both) locations, we can update this check to support them. + AC_CHECK_HEADER([cdd.h],[],[sage_spkg_install_cddlib=yes],[ + #include + #include + ]) + + dnl Both lattE and gfan try to link against libcddgmp (as + dnl opposed to libcdd). + AC_SEARCH_LIBS([dd_abs],[cddgmp],[],[sage_spkg_install_cddlib=yes]) + ]) +]) diff --git a/build/pkgs/cddlib/spkg-install b/build/pkgs/cddlib/spkg-install.in similarity index 100% rename from build/pkgs/cddlib/spkg-install rename to build/pkgs/cddlib/spkg-install.in diff --git a/build/pkgs/certifi/SPKG.rst b/build/pkgs/certifi/SPKG.rst new file mode 100644 index 00000000000..9fc7bde2d70 --- /dev/null +++ b/build/pkgs/certifi/SPKG.rst @@ -0,0 +1,23 @@ +Certifi +======= + +Description +----------- + +Python package for providing Mozilla's CA Bundle. + +License +------- + +ISC + + +Upstream Contact +---------------- + +Home page: https://pypi.python.org/pypi/certifi + +Dependencies +------------ + +Python, Setuptools diff --git a/build/pkgs/certifi/SPKG.txt b/build/pkgs/certifi/SPKG.txt deleted file mode 100644 index 3985ae82ea4..00000000000 --- a/build/pkgs/certifi/SPKG.txt +++ /dev/null @@ -1,18 +0,0 @@ -= Certifi = - -== Description == - -Python package for providing Mozilla's CA Bundle. - -== License == - -ISC - -== Upstream Contact == - -Home page: https://pypi.python.org/pypi/certifi - -== Dependencies == - -Python, Setuptools - diff --git a/build/pkgs/certifi/dependencies b/build/pkgs/certifi/dependencies index 2573414ce8a..15df0c4d6d8 100644 --- a/build/pkgs/certifi/dependencies +++ b/build/pkgs/certifi/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/certifi/spkg-install b/build/pkgs/certifi/spkg-install.in similarity index 100% rename from build/pkgs/certifi/spkg-install rename to build/pkgs/certifi/spkg-install.in diff --git a/build/pkgs/cffi/SPKG.txt b/build/pkgs/cffi/SPKG.txt new file mode 100644 index 00000000000..075e8abf25b --- /dev/null +++ b/build/pkgs/cffi/SPKG.txt @@ -0,0 +1,15 @@ += cffi = + +== Description == + +development website: https://foss.heptapod.net/pypy/cffi +documentation website: https://cffi.readthedocs.io/en/latest/ +PyPI page: https://pypi.org/project/cffi/ + +== License == + + * MIT + +== Upstream Contact == + + * https://foss.heptapod.net/pypy/cffi diff --git a/build/pkgs/cffi/checksums.ini b/build/pkgs/cffi/checksums.ini new file mode 100644 index 00000000000..88dd9628d4c --- /dev/null +++ b/build/pkgs/cffi/checksums.ini @@ -0,0 +1,5 @@ +tarball=cffi-VERSION.tar.gz +sha1=2a8f05a7d51d77ef1e641cb359a54e4d8fa019cb +md5=74845f8d2b7b583dd9a3574f402edf39 +cksum=1594930691 +upstream_url=https://pypi.io/packages/source/c/cffi/cffi-VERSION.tar.gz diff --git a/build/pkgs/cffi/dependencies b/build/pkgs/cffi/dependencies new file mode 100644 index 00000000000..15df0c4d6d8 --- /dev/null +++ b/build/pkgs/cffi/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/cffi/package-version.txt b/build/pkgs/cffi/package-version.txt new file mode 100644 index 00000000000..850e742404b --- /dev/null +++ b/build/pkgs/cffi/package-version.txt @@ -0,0 +1 @@ +1.14.0 diff --git a/build/pkgs/configparser/spkg-install b/build/pkgs/cffi/spkg-install.in similarity index 100% rename from build/pkgs/configparser/spkg-install rename to build/pkgs/cffi/spkg-install.in diff --git a/build/pkgs/backports_functools_lru_cache/type b/build/pkgs/cffi/type similarity index 100% rename from build/pkgs/backports_functools_lru_cache/type rename to build/pkgs/cffi/type diff --git a/build/pkgs/cliquer/SPKG.rst b/build/pkgs/cliquer/SPKG.rst new file mode 100644 index 00000000000..4f65497f1c8 --- /dev/null +++ b/build/pkgs/cliquer/SPKG.rst @@ -0,0 +1,31 @@ +Cliquer +======= + +Description +----------- + +Cliquer is a set of C routines for finding cliques in an arbitrary +weighted graph. It uses an exact branch-and-bound algorithm recently +developed by Patr Ostergard. + +License +------- + +GNU General Public License v2 + + +Upstream Contact +---------------- + +Cliquer was mainly written by Sampo Niskanen, sampo.niskanenQiki.fi +(Q=@). http://users.tkk.fi/pat/cliquer.html + +Dependencies +------------ + +- None + +Patches +------- + +- autotoolized - see https://github.com/dimpase/autocliquer diff --git a/build/pkgs/cliquer/SPKG.txt b/build/pkgs/cliquer/SPKG.txt deleted file mode 100644 index 1dfd7d48e6d..00000000000 --- a/build/pkgs/cliquer/SPKG.txt +++ /dev/null @@ -1,19 +0,0 @@ -= Cliquer = - -== Description == -Cliquer is a set of C routines for finding cliques in an arbitrary -weighted graph. It uses an exact branch-and-bound algorithm recently -developed by Patr Ostergard. - -== License == -GNU General Public License v2 - -== Upstream Contact == -Cliquer was mainly written by Sampo Niskanen, sampo.niskanenQiki.fi (Q=@). -http://users.tkk.fi/pat/cliquer.html - -== Dependencies == - * None - -== Patches == - * autotoolized - see https://github.com/dimpase/autocliquer diff --git a/build/pkgs/cliquer/distros/fedora.txt b/build/pkgs/cliquer/distros/fedora.txt new file mode 100644 index 00000000000..4dadef53faa --- /dev/null +++ b/build/pkgs/cliquer/distros/fedora.txt @@ -0,0 +1 @@ +cliquer cliquer-devel diff --git a/build/pkgs/cliquer/distros/gentoo.txt b/build/pkgs/cliquer/distros/gentoo.txt new file mode 100644 index 00000000000..9b5042364bd --- /dev/null +++ b/build/pkgs/cliquer/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/cliquer diff --git a/build/pkgs/cliquer/spkg-check b/build/pkgs/cliquer/spkg-check deleted file mode 100644 index c24b3cfc30d..00000000000 --- a/build/pkgs/cliquer/spkg-check +++ /dev/null @@ -1,16 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -echo "Now building and running cliquer's test suite..." -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error: The cliquer's test suite failed." - exit 1 -fi - -echo "The cliquer's test suite passed successfully." diff --git a/build/pkgs/cliquer/spkg-check.in b/build/pkgs/cliquer/spkg-check.in new file mode 100644 index 00000000000..917c8eb7209 --- /dev/null +++ b/build/pkgs/cliquer/spkg-check.in @@ -0,0 +1,3 @@ +cd src + +$MAKE check diff --git a/build/pkgs/cliquer/spkg-install b/build/pkgs/cliquer/spkg-install.in similarity index 100% rename from build/pkgs/cliquer/spkg-install rename to build/pkgs/cliquer/spkg-install.in diff --git a/build/pkgs/cmake/SPKG.rst b/build/pkgs/cmake/SPKG.rst new file mode 100644 index 00000000000..549a231f8dc --- /dev/null +++ b/build/pkgs/cmake/SPKG.rst @@ -0,0 +1,37 @@ +CMake +===== + +Description +----------- + +The "cmake" executable is the CMake command-line interface. It may be +used to configure projects in scripts. Project configuration settings +may be specified on the command line with the -D option. The -i option +will cause cmake to interactively prompt for such settings. + +CMake is a cross-platform build system generator. Projects specify their +build process with platform-independent CMake listfiles included in each +directory of a source tree with the name CMakeLists.txt. Users build a +project by using CMake to generate a build system for a native tool on +their platform. + +Website: www.cmake.org + +License +------- + +CMake is distributed under the OSI-approved BSD 3-clause License. + + +Upstream Contact +---------------- + +- cmake-developers@cmake.org + +Dependencies +------------ + +- curl +- zlib +- bzip2 +- xz diff --git a/build/pkgs/cmake/SPKG.txt b/build/pkgs/cmake/SPKG.txt deleted file mode 100644 index 57770de3604..00000000000 --- a/build/pkgs/cmake/SPKG.txt +++ /dev/null @@ -1,32 +0,0 @@ -= CMake = - -== Description == - -The "cmake" executable is the CMake command-line interface. It may be -used to configure projects in scripts. Project configuration settings -may be specified on the command line with the -D option. The -i -option will cause cmake to interactively prompt for such settings. - -CMake is a cross-platform build system generator. Projects specify -their build process with platform-independent CMake listfiles -included in each directory of a source tree with the name -CMakeLists.txt. Users build a project by using CMake to generate a -build system for a native tool on their platform. - -Website: www.cmake.org - -== License == - -CMake is distributed under the OSI-approved BSD 3-clause License. - -== Upstream Contact == - - * cmake-developers@cmake.org - -== Dependencies == - -* curl -* zlib -* bzip2 -* xz - diff --git a/build/pkgs/cmake/distros/cygwin.txt b/build/pkgs/cmake/distros/cygwin.txt new file mode 100644 index 00000000000..a3ea3e4380f --- /dev/null +++ b/build/pkgs/cmake/distros/cygwin.txt @@ -0,0 +1 @@ +cmake diff --git a/build/pkgs/cmake/distros/homebrew.txt b/build/pkgs/cmake/distros/homebrew.txt new file mode 100644 index 00000000000..a3ea3e4380f --- /dev/null +++ b/build/pkgs/cmake/distros/homebrew.txt @@ -0,0 +1 @@ +cmake diff --git a/build/pkgs/cmake/distros/slackware.txt b/build/pkgs/cmake/distros/slackware.txt new file mode 100644 index 00000000000..a3ea3e4380f --- /dev/null +++ b/build/pkgs/cmake/distros/slackware.txt @@ -0,0 +1 @@ +cmake diff --git a/build/pkgs/cmake/spkg-check b/build/pkgs/cmake/spkg-check deleted file mode 100644 index 12244a0bfda..00000000000 --- a/build/pkgs/cmake/spkg-check +++ /dev/null @@ -1,24 +0,0 @@ -if [ "$UNAME" = Darwin ]; then - CC=clang - CXX=clang++ -fi - -cd src -unset MACOSX_DEPLOYMENT_TARGET -# Supress tests failing for reason out of our controls -FAILING_TESTS="CTestTestStopTime|TestUpload|ctest_submit" -# gui interface may pull dependency conflicting with -# sage libraries -# (1) QT libraries -FAILING_TESTS="${FAILING_TESTS}|Qt5Autogen|Qt4Autogen|Qt4Targets|Qt4And5AutomocReverse" -# (2) GTK2 -FAILING_TESTS="${FAILING_TESTS}|GTK2Components|GTK2Targets" -# BundleUtilities test is failing on some platforms, skipped in Gentoo for that reason -FAILING_TESTS="${FAILING_TESTS}|BundleUtilities" -# Test failing on freeBSD. Upstream acknoledged the test is faulty -# on that platform https://gitlab.kitware.com/cmake/cmake/issues/16887 -if [ "$UNAME" = "FreeBSD" ]; then - FAILING_TESTS="${FAILING_TESTS}|RunCMake.GNUInstallDirs" -fi -ctest --extra-verbose --output-on-failure -E "(${FAILING_TESTS})" - diff --git a/build/pkgs/cmake/spkg-check.in b/build/pkgs/cmake/spkg-check.in new file mode 100644 index 00000000000..0a46f8bb57f --- /dev/null +++ b/build/pkgs/cmake/spkg-check.in @@ -0,0 +1,8 @@ +# Do not run a test suite: see https://trac.sagemath.org/ticket/30093. +# +# - The test suite takes a long time and can fail in minor ways. +# - If some major aspect of cmake fails, it only affects the build +# process of Sage, not the mathematical correctness of its results. +# - If some minor aspect of cmake fails, it won't affect anything about Sage. + +echo "We skip the test suite for cmake: see https://trac.sagemath.org/ticket/30093." diff --git a/build/pkgs/cmake/spkg-install b/build/pkgs/cmake/spkg-install.in similarity index 100% rename from build/pkgs/cmake/spkg-install rename to build/pkgs/cmake/spkg-install.in diff --git a/build/pkgs/cocoalib/SPKG.rst b/build/pkgs/cocoalib/SPKG.rst new file mode 100644 index 00000000000..94e820a9aae --- /dev/null +++ b/build/pkgs/cocoalib/SPKG.rst @@ -0,0 +1,32 @@ +cocoalib +======== + +Description +----------- + +CoCoA is a program to compute with numbers and polynomials. + +License +------- + +- GPL v3 + + +Upstream Contact +---------------- + +- Authors: http://cocoa.dima.unige.it/research/ +- Email: cocoa@dima.unige.it +- Website: http://cocoa.dima.unige.it/ +- Releases: http://cocoa.dima.unige.it/cocoalib/ + +Dependencies +------------ + +- GMP/MPIR + + +Special Update/Build Instructions +--------------------------------- + +None. diff --git a/build/pkgs/cocoalib/SPKG.txt b/build/pkgs/cocoalib/SPKG.txt deleted file mode 100644 index 153cf580470..00000000000 --- a/build/pkgs/cocoalib/SPKG.txt +++ /dev/null @@ -1,24 +0,0 @@ -= cocoalib = - -== Description == - -CoCoA is a program to compute with numbers and polynomials. - -== License == - - * GPL v3 - -== Upstream Contact == - - * Authors: http://cocoa.dima.unige.it/research/ - * Email: cocoa@dima.unige.it - * Website: http://cocoa.dima.unige.it/ - * Releases: http://cocoa.dima.unige.it/cocoalib/ - -== Dependencies == - - * GMP/MPIR - -== Special Update/Build Instructions == - -None. diff --git a/build/pkgs/cocoalib/spkg-check b/build/pkgs/cocoalib/spkg-check deleted file mode 100644 index 6923c60ea95..00000000000 --- a/build/pkgs/cocoalib/spkg-check +++ /dev/null @@ -1,2 +0,0 @@ -cd src/ -sdh_make check diff --git a/build/pkgs/cocoalib/spkg-check.in b/build/pkgs/cocoalib/spkg-check.in new file mode 100644 index 00000000000..301342d48a9 --- /dev/null +++ b/build/pkgs/cocoalib/spkg-check.in @@ -0,0 +1,2 @@ +cd src/ +sdh_make_check diff --git a/build/pkgs/cocoalib/spkg-install b/build/pkgs/cocoalib/spkg-install.in similarity index 100% rename from build/pkgs/cocoalib/spkg-install rename to build/pkgs/cocoalib/spkg-install.in diff --git a/build/pkgs/combinatorial_designs/SPKG.rst b/build/pkgs/combinatorial_designs/SPKG.rst new file mode 100644 index 00000000000..59ad44d893b --- /dev/null +++ b/build/pkgs/combinatorial_designs/SPKG.rst @@ -0,0 +1,27 @@ + +Combinatorial Designs +===================== + +Description +----------- + +Data for Combinatorial Designs. Current content: + +- The table of MOLS (10 000 integers) from the Handbook of + Combinatorial Designs, 2ed. + +License +------- + +Public domain. + + +Upstream Contact +---------------- + + None + +Dependencies +------------ + +N/A diff --git a/build/pkgs/combinatorial_designs/SPKG.txt b/build/pkgs/combinatorial_designs/SPKG.txt deleted file mode 100644 index e0fb5cfb6b2..00000000000 --- a/build/pkgs/combinatorial_designs/SPKG.txt +++ /dev/null @@ -1,25 +0,0 @@ -= Combinatorial Designs = - -== Description == - -Data for Combinatorial Designs. Current content: - -- The table of MOLS (10 000 integers) from the Handbook of Combinatorial - Designs, 2ed. - -== License == - -Public domain. - -== Upstream Contact == - - None - -== Dependencies == - -N/A - -== Changelog == - -== designs-20140630 (Nathann Cohen, 2014-06-30) == - * #16541: Table of MOLS diff --git a/build/pkgs/combinatorial_designs/spkg-install b/build/pkgs/combinatorial_designs/spkg-install.in similarity index 100% rename from build/pkgs/combinatorial_designs/spkg-install rename to build/pkgs/combinatorial_designs/spkg-install.in diff --git a/build/pkgs/compilerwrapper/SPKG.rst b/build/pkgs/compilerwrapper/SPKG.rst new file mode 100644 index 00000000000..0f702b4f423 --- /dev/null +++ b/build/pkgs/compilerwrapper/SPKG.rst @@ -0,0 +1,35 @@ + +Compiler Wrapper +================ + +Description +----------- + +A wrapper for compiler and binutils that sets rpath, removes +optimizations on broken archs and gcc versions, and generally helps to +compile Sage more easily. + +License +------- + +GPL v2+ + + +Upstream Contact +---------------- + +- https://bitbucket.org/vbraun/compilerwrapper +- Volker Braun + +Dependencies +------------ + +- None + + +Special Update/Build Instructions +--------------------------------- + +The src/ subdirectory is a clone of my mercurial repository at +https://bitbucket.org/vbraun/compilerwrapper. You can update the source +tree with "hg pull -u". diff --git a/build/pkgs/compilerwrapper/SPKG.txt b/build/pkgs/compilerwrapper/SPKG.txt deleted file mode 100644 index 8f364a787ff..00000000000 --- a/build/pkgs/compilerwrapper/SPKG.txt +++ /dev/null @@ -1,26 +0,0 @@ -= Compiler Wrapper = - -== Description == - -A wrapper for compiler and binutils that sets rpath, removes -optimizations on broken archs and gcc versions, and generally helps to -compile Sage more easily. - -== License == - -GPL v2+ - -== Upstream Contact == - -https://bitbucket.org/vbraun/compilerwrapper -Volker Braun - -== Dependencies == - - * None - -== Special Update/Build Instructions == - -The src/ subdirectory is a clone of my mercurial repository at -https://bitbucket.org/vbraun/compilerwrapper. You can update the -source tree with "hg pull -u". diff --git a/build/pkgs/compilerwrapper/spkg-install b/build/pkgs/compilerwrapper/spkg-install deleted file mode 100644 index e352d9a16d3..00000000000 --- a/build/pkgs/compilerwrapper/spkg-install +++ /dev/null @@ -1,49 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -# remove wrapper if it is already installed -rm -f $SAGE_LOCAL/bin/gcc -rm -f $SAGE_LOCAL/bin/cc -rm -f $SAGE_LOCAL/bin/c89 -rm -f $SAGE_LOCAL/bin/c99 -rm -f $SAGE_LOCAL/bin/c++ -rm -f $SAGE_LOCAL/bin/g++ -rm -f $SAGE_LOCAL/bin/ld - -CC_FILENAME=`src/scripts/find_executable.sh --CC --exclude="$SAGE_LOCAL/bin"` -CC_PATH=`dirname "$CC_FILENAME"` - -LD_FILENAME=`src/scripts/find_executable.sh --LD --exclude="$SAGE_LOCAL/bin"` -GFORTRAN_FILENAME=`src/scripts/find_executable.sh --exclude="$SAGE_LOCAL/bin" gfortran` - -echo "Setting up the compiler/binutils wrapper" -echo "Real location of compiler: $CC_PATH" -echo "Real linker: $LD_FILENAME" -echo "Real fortran: $GFORTRAN_FILENAME" - -cd src -./configure --prefix="$SAGE_LOCAL" \ - --with-ccpath="$CC_PATH" \ - --with-ld="$LD_FILENAME" \ - --with-gfortran="$GFORTRAN_FILENAME" \ - --enable-sage -if [ $? -ne 0 ]; then - echo >&2 "Error configuring the compiler wrapper." - exit 1 -fi - -$MAKE -if [ $? -ne 0 ]; then - echo >&2 "Error building the compiler wrapper." - exit 1 -fi - -$MAKE install -if [ $? -ne 0 ]; then - echo >&2 "Error installing the gcc wrapper." - exit 1 -fi - diff --git a/build/pkgs/compilerwrapper/spkg-install.in b/build/pkgs/compilerwrapper/spkg-install.in new file mode 100644 index 00000000000..3bbccf4ac10 --- /dev/null +++ b/build/pkgs/compilerwrapper/spkg-install.in @@ -0,0 +1,43 @@ +# remove wrapper if it is already installed +rm -f $SAGE_LOCAL/bin/gcc +rm -f $SAGE_LOCAL/bin/cc +rm -f $SAGE_LOCAL/bin/c89 +rm -f $SAGE_LOCAL/bin/c99 +rm -f $SAGE_LOCAL/bin/c++ +rm -f $SAGE_LOCAL/bin/g++ +rm -f $SAGE_LOCAL/bin/ld + +CC_FILENAME=`src/scripts/find_executable.sh --CC --exclude="$SAGE_LOCAL/bin"` +CC_PATH=`dirname "$CC_FILENAME"` + +LD_FILENAME=`src/scripts/find_executable.sh --LD --exclude="$SAGE_LOCAL/bin"` +GFORTRAN_FILENAME=`src/scripts/find_executable.sh --exclude="$SAGE_LOCAL/bin" gfortran` + +echo "Setting up the compiler/binutils wrapper" +echo "Real location of compiler: $CC_PATH" +echo "Real linker: $LD_FILENAME" +echo "Real fortran: $GFORTRAN_FILENAME" + +cd src +./configure --prefix="$SAGE_LOCAL" \ + --with-ccpath="$CC_PATH" \ + --with-ld="$LD_FILENAME" \ + --with-gfortran="$GFORTRAN_FILENAME" \ + --enable-sage +if [ $? -ne 0 ]; then + echo >&2 "Error configuring the compiler wrapper." + exit 1 +fi + +$MAKE +if [ $? -ne 0 ]; then + echo >&2 "Error building the compiler wrapper." + exit 1 +fi + +$MAKE install +if [ $? -ne 0 ]; then + echo >&2 "Error installing the gcc wrapper." + exit 1 +fi + diff --git a/build/pkgs/conda.txt b/build/pkgs/conda.txt index d76388ce7bb..8d8ac6480f4 100644 --- a/build/pkgs/conda.txt +++ b/build/pkgs/conda.txt @@ -2,6 +2,6 @@ compilers make m4 perl -python +"python<3.8" tar bc diff --git a/build/pkgs/configparser/SPKG.txt b/build/pkgs/configparser/SPKG.txt deleted file mode 100644 index 6c687197d7d..00000000000 --- a/build/pkgs/configparser/SPKG.txt +++ /dev/null @@ -1,22 +0,0 @@ -= configparser = - -== Description == - -This library brings the updated configparser from Python 3.5 to Python 2.6-3.5. - -The ancient ConfigParser module available in the standard library 2.x -has seen a major update in Python 3.2. This is a backport of those -changes so that they can be used directly in Python 2.6 - 3.5. - -To use the configparser backport instead of the built-in version on both -Python 2 and Python 3, simply import it explicitly as a backport: - - from backports import configparser - -If you'd like to use the backport on Python 2 and the built-in version -on Python 3, use that invocation instead: - - import configparser - -For detailed documentation consult the vanilla version at -http://docs.python.org/3/library/configparser.html. diff --git a/build/pkgs/configparser/checksums.ini b/build/pkgs/configparser/checksums.ini deleted file mode 100644 index a34386e96b0..00000000000 --- a/build/pkgs/configparser/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=configparser-VERSION.tar.gz -sha1=81351574c345e2a8600b7f2b2afb2b8f1c6aded2 -md5=aaa80b58b6b0810e54f66486860b3ed1 -cksum=4208301028 diff --git a/build/pkgs/configparser/dependencies b/build/pkgs/configparser/dependencies deleted file mode 100644 index d5dab729e18..00000000000 --- a/build/pkgs/configparser/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | pip - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/configparser/package-version.txt b/build/pkgs/configparser/package-version.txt deleted file mode 100644 index 0833a98f140..00000000000 --- a/build/pkgs/configparser/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -3.7.4 diff --git a/build/pkgs/configure/SPKG.rst b/build/pkgs/configure/SPKG.rst new file mode 100644 index 00000000000..4927e8d64d1 --- /dev/null +++ b/build/pkgs/configure/SPKG.rst @@ -0,0 +1,34 @@ +Configure +========= + +Description +----------- + +This package contains a tar archive of auto-generated files. They are +shipped with Sage in case you do not have a sufficiently recent +autotools version installed. + +License +------- + +GPLv3+ + + +Upstream Contact +---------------- + +Automatically generated by Sage, use trac and/or sage-devel for +questions. + +Dependencies +------------ + +None + + +Special Update/Build Instructions +--------------------------------- + +This tarball is automatically generated by Sage whenever you run the +$SAGE_ROOT/bootstrap -s or the $SAGE_ROOT/src/bin/sage-update-version +script. diff --git a/build/pkgs/configure/SPKG.txt b/build/pkgs/configure/SPKG.txt deleted file mode 100644 index 72f87196c77..00000000000 --- a/build/pkgs/configure/SPKG.txt +++ /dev/null @@ -1,26 +0,0 @@ -= Configure = - -== Description == - -This package contains a tar archive of auto-generated files. They are -shipped with Sage in case you do not have a sufficiently recent -autotools version installed. - -== License == - -GPLv3+ - -== Upstream Contact == - - -Automatically generated by Sage, use trac and/or sage-devel for questions. - -== Dependencies == - -None - -== Special Update/Build Instructions == - -This tarball is automatically generated by Sage whenever you run the -$SAGE_ROOT/bootstrap -s or the $SAGE_ROOT/src/bin/sage-update-version -script. diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 20fbf60ba52..e72a2d59dc5 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=5331e588682dfea9308e8308e510d47429500e76 -md5=fe1a9a72f856549d946340703d20d791 -cksum=888161184 +sha1=3f0bae0f42cabe487db185d354ffce5401f41c0d +md5=0f6b2ddc8280958cde5b5d089a2400e0 +cksum=1841781857 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 2fc001ad88f..daa7f1f01ee 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -158b93057a44377a3dd8235795317aa0bd412ed9 +ab502c6901350ba3418d03f479c27128b14de479 diff --git a/build/pkgs/conway_polynomials/SPKG.rst b/build/pkgs/conway_polynomials/SPKG.rst new file mode 100644 index 00000000000..16036a280f2 --- /dev/null +++ b/build/pkgs/conway_polynomials/SPKG.rst @@ -0,0 +1,13 @@ + +Conway Polynomials Database +=========================== + +Description +----------- + + Contains a small database of Conway polynomials. + +Dependencies +------------ + +- Sage library diff --git a/build/pkgs/conway_polynomials/SPKG.txt b/build/pkgs/conway_polynomials/SPKG.txt deleted file mode 100644 index de510059e7a..00000000000 --- a/build/pkgs/conway_polynomials/SPKG.txt +++ /dev/null @@ -1,24 +0,0 @@ -= Conway Polynomials Database = - -== Description == - - Contains a small database of Conway polynomials. - -== Dependencies == - - * Sage library - -== Changelog == - -=== conway_polynomials-0.4.p0 (Karl-Dieter Crisman, 13 February 2013) === - * #14075: Remove printing of primes while installing - -=== conway_polynomials-0.4 (R. Andrew Ohana and William Stein, 18 June 2012) === - * #12205: Rewrite database to not use ZODB - -=== conway_polynomials-0.3 (R. Andrew Ohana, 18 June 2012) === - * #13123: Move SAGE_DATA to SAGE_LOCAL/share/sage/data - -=== conway_polynomials-early_versions (unknown, unknown) === - * previous version(s) - * lost to history diff --git a/build/pkgs/conway_polynomials/dependencies b/build/pkgs/conway_polynomials/dependencies index b28747a2248..304d0c987a2 100644 --- a/build/pkgs/conway_polynomials/dependencies +++ b/build/pkgs/conway_polynomials/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | six +$(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/conway_polynomials/spkg-install b/build/pkgs/conway_polynomials/spkg-install.in similarity index 100% rename from build/pkgs/conway_polynomials/spkg-install rename to build/pkgs/conway_polynomials/spkg-install.in diff --git a/build/pkgs/conway_polynomials/spkg-install.py b/build/pkgs/conway_polynomials/spkg-install.py index 8ed45676a35..cb192719d63 100644 --- a/build/pkgs/conway_polynomials/spkg-install.py +++ b/build/pkgs/conway_polynomials/spkg-install.py @@ -1,5 +1,5 @@ import os -from six.moves import cPickle as pickle +import pickle SAGE_SHARE = os.getenv('SAGE_SHARE') install_root = os.path.join(SAGE_SHARE, 'conway_polynomials') diff --git a/build/pkgs/coxeter3/SPKG.rst b/build/pkgs/coxeter3/SPKG.rst new file mode 100644 index 00000000000..fc198fa9814 --- /dev/null +++ b/build/pkgs/coxeter3/SPKG.rst @@ -0,0 +1,54 @@ +coxeter3 +======== + +Description +----------- + +This package wraps Fokko Ducloux's Coxeter 3 C++ library + +Features: + +- General Coxeter groups, implemented through the combinatorics of + reduced words; +- Reduced expression and normal form computations; +- Bruhat ordering; +- Ordinary Kazhdan-Lusztig polynomials; +- Kazhdan-Lusztig polynomials with unequal parameters; +- Inverse Kazhdan-Lusztig polynomials; +- Cells and W-graphs; + +http://math.univ-lyon1.fr/~ducloux/coxeter/coxeter3/english/coxeter3_e.html + +This is a patched version done by Mike Hansen 2009-2013 and some fixes +by Nicolas M. Thiéry and Jean-Pierre Flori. + +License +------- + +GPL + + +Upstream Contact +---------------- + +github: https://github.com/tscrim/coxeter + +Alas, Fokko Ducloux passed away in 2006. + +http://math.univ-lyon1.fr/~ducloux/du_Cloux.html + +Dependencies +------------ + +None + + +Special Update/Build Instructions +--------------------------------- + +The source package was created by running :: + + commit=8ac9c71723c8ca57a836d6381aed125261e44e9e + git clone https://github.com/tscrim/coxeter.git + cd coxeter + git archive $commit | bzip2 --best >coxeter-$commit.tar.bz2 diff --git a/build/pkgs/coxeter3/SPKG.txt b/build/pkgs/coxeter3/SPKG.txt deleted file mode 100644 index 773bfc87ab8..00000000000 --- a/build/pkgs/coxeter3/SPKG.txt +++ /dev/null @@ -1,45 +0,0 @@ -= coxeter3 = - -== Description == - -This package wraps Fokko Ducloux's Coxeter 3 C++ library - -Features: - -- General Coxeter groups, implemented through the combinatorics of reduced words; -- Reduced expression and normal form computations; -- Bruhat ordering; -- Ordinary Kazhdan-Lusztig polynomials; -- Kazhdan-Lusztig polynomials with unequal parameters; -- Inverse Kazhdan-Lusztig polynomials; -- Cells and W-graphs; - -http://math.univ-lyon1.fr/~ducloux/coxeter/coxeter3/english/coxeter3_e.html - -This is a patched version done by Mike Hansen 2009-2013 and some fixes -by Nicolas M. Thiéry and Jean-Pierre Flori. - -== License == - -GPL - -== Upstream Contact == - -github: https://github.com/tscrim/coxeter - -Alas, Fokko Ducloux passed away in 2006. - -http://math.univ-lyon1.fr/~ducloux/du_Cloux.html - -== Dependencies == - -None - -== Special Update/Build Instructions == - -The source package was created by running - -commit=8ac9c71723c8ca57a836d6381aed125261e44e9e -git clone https://github.com/tscrim/coxeter.git -cd coxeter -git archive $commit |bzip2 --best >coxeter-$commit.tar.bz2 diff --git a/build/pkgs/coxeter3/distros/fedora.txt b/build/pkgs/coxeter3/distros/fedora.txt new file mode 100644 index 00000000000..3cfeb2f9fa0 --- /dev/null +++ b/build/pkgs/coxeter3/distros/fedora.txt @@ -0,0 +1 @@ +coxeter coxeter-devel coxeter-tools diff --git a/build/pkgs/coxeter3/package-version.txt b/build/pkgs/coxeter3/package-version.txt index 1d0d8e866ea..5b76852c821 100644 --- a/build/pkgs/coxeter3/package-version.txt +++ b/build/pkgs/coxeter3/package-version.txt @@ -1 +1 @@ -8ac9c71723c8ca57a836d6381aed125261e44e9e +8ac9c71723c8ca57a836d6381aed125261e44e9e.p0 diff --git a/build/pkgs/coxeter3/patches/makefile.patch b/build/pkgs/coxeter3/patches/makefile.patch index 3652bca8506..f5b26d89d72 100644 --- a/build/pkgs/coxeter3/patches/makefile.patch +++ b/build/pkgs/coxeter3/patches/makefile.patch @@ -9,7 +9,7 @@ diff -druN src/makefile src/makefile ifdef optimize NDEBUG = true -@@ -22,18 +23,74 @@ +@@ -22,18 +27,78 @@ cflags = $(pflags) endif @@ -53,21 +53,25 @@ diff -druN src/makefile src/makefile +executable: $(objects) + $(CXX) -o $(EXENAME)$(EXEEXT) $(objects) + -+DATADIR="$$SAGE_LOCAL/coxeter/" -+INCLUDEDIR="$$SAGE_LOCAL/include/coxeter/" -+LIBRARYDIR="$$SAGE_LOCAL/$(LIBDIR)" ++BINDIR=$$SAGE_LOCAL/bin/ ++DATADIR=$$SAGE_LOCAL/coxeter/ ++INCLUDEDIR=$$SAGE_LOCAL/include/coxeter/ ++LIBRARYDIR=$$SAGE_LOCAL/$(LIBDIR) + +install: coxeter executable -+ cp $(EXENAME)$(EXEEXT) "$$SAGE_LOCAL/bin/" -+ cp $(LIBRARY) $(LIBRARYDIR) ++ mkdir -p "$(DESTDIR)$(BINDIR)" ++ mkdir -p "$(DESTDIR)$(LIBRARYDIR)" ++ cp $(EXENAME)$(EXEEXT) "$(DESTDIR)$(BINDIR)" ++ cp $(LIBRARY) "$(DESTDIR)$(LIBRARYDIR)" + if [ $(UNAME) = "CYGWIN" ]; then \ -+ cp $(IMPLIB) "$$SAGE_LOCAL/lib/"; \ ++ mkdir -p "$(DESTDIR)$$SAGE_LOCAL/lib/"; \ ++ cp $(IMPLIB) "$(DESTDIR)$$SAGE_LOCAL/lib/"; \ + fi + -+ mkdir -p $(DATADIR) -+ cp -r coxeter_matrices headers messages $(DATADIR) -+ mkdir -p $(INCLUDEDIR) -+ cp -r *.h *.hpp $(INCLUDEDIR) ++ mkdir -p "$(DESTDIR)$(DATADIR)" ++ cp -r coxeter_matrices headers messages "$(DESTDIR)$(DATADIR)" ++ mkdir -p "$(DESTDIR)$(INCLUDEDIR)" ++ cp -r *.h *.hpp "$(DESTDIR)$(INCLUDEDIR)" + +check: coxeter executable + $(EXENAME)$(EXEEXT) < test.input > test.output diff --git a/build/pkgs/coxeter3/spkg-check b/build/pkgs/coxeter3/spkg-check deleted file mode 100644 index b3c5dea40c0..00000000000 --- a/build/pkgs/coxeter3/spkg-check +++ /dev/null @@ -1,13 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting"; - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error running coxeter testsuite" - exit 1 -fi diff --git a/build/pkgs/coxeter3/spkg-check.in b/build/pkgs/coxeter3/spkg-check.in new file mode 100644 index 00000000000..917c8eb7209 --- /dev/null +++ b/build/pkgs/coxeter3/spkg-check.in @@ -0,0 +1,3 @@ +cd src + +$MAKE check diff --git a/build/pkgs/coxeter3/spkg-configure.m4 b/build/pkgs/coxeter3/spkg-configure.m4 new file mode 100644 index 00000000000..108c76503c2 --- /dev/null +++ b/build/pkgs/coxeter3/spkg-configure.m4 @@ -0,0 +1,21 @@ +SAGE_SPKG_CONFIGURE([coxeter3], [ + AC_LANG_PUSH(C++) + AC_MSG_CHECKING([for library coxeter3]) + SAVE_LIBS="$LIBS" + LIBS="$LIBS -lcoxeter3" + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[ + #include + #include + ]], [[ + coxeter::CoxGroup *g = interactive::coxeterGroup("B", 2); + ]]) + ], [ + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) + sage_spkg_install_coxeter3=yes + ]) + LIBS="$SAVE_LIBS" + AC_LANG_POP(C++) +]) diff --git a/build/pkgs/coxeter3/spkg-install b/build/pkgs/coxeter3/spkg-install.in similarity index 100% rename from build/pkgs/coxeter3/spkg-install rename to build/pkgs/coxeter3/spkg-install.in diff --git a/build/pkgs/cryptominisat/SPKG.rst b/build/pkgs/cryptominisat/SPKG.rst new file mode 100644 index 00000000000..6f7f0ce55d0 --- /dev/null +++ b/build/pkgs/cryptominisat/SPKG.rst @@ -0,0 +1,37 @@ +CryptoMiniSat +============= + +Description +----------- + + CryptoMiniSat is a SAT solver that aims to become a premiere SAT + solver with all the features and speed of successful SAT solvers, + such as MiniSat and PrecoSat. The long-term goals of CryptoMiniSat + are to be an efficient sequential, parallel and distributed + solver. There are solvers that are good at one or the other, + e.g. ManySat (parallel) or PSolver (distributed), but we wish to + excel at all. + + CryptoMiniSat 2.5 won the SAT Race 2010 among 20 solvers submitted + by researchers and industry. + +License +------- + +MIT License + + +Upstream Contact +---------------- + +- Authors: Mate Soos +- Email: soos.mate@gmail.com +- Website: http://www.msoos.org/ +- Releases: https://github.com/msoos/cryptominisat/releases + + +Special Update/Build Instructions +--------------------------------- + +CryptoMiniSat's tarball downloaded from github is called VERSION.tar.gz +and should be renamed to cryptominisat-VERSION.tar.gz diff --git a/build/pkgs/cryptominisat/SPKG.txt b/build/pkgs/cryptominisat/SPKG.txt deleted file mode 100644 index c1ee354a588..00000000000 --- a/build/pkgs/cryptominisat/SPKG.txt +++ /dev/null @@ -1,31 +0,0 @@ -= CryptoMiniSat = - -== Description == - - CryptoMiniSat is a SAT solver that aims to become a premiere SAT - solver with all the features and speed of successful SAT solvers, - such as MiniSat and PrecoSat. The long-term goals of CryptoMiniSat - are to be an efficient sequential, parallel and distributed - solver. There are solvers that are good at one or the other, - e.g. ManySat (parallel) or PSolver (distributed), but we wish to - excel at all. - - CryptoMiniSat 2.5 won the SAT Race 2010 among 20 solvers submitted - by researchers and industry. - -== License == - -MIT License - -== Upstream Contact == - - * Authors: Mate Soos - * Email: soos.mate@gmail.com - * Website: http://www.msoos.org/ - * Releases: https://github.com/msoos/cryptominisat/releases - -== Special Update/Build Instructions == - -CryptoMiniSat's tarball downloaded from github is called VERSION.tar.gz -and should be renamed to cryptominisat-VERSION.tar.gz - diff --git a/build/pkgs/cryptominisat/spkg-install b/build/pkgs/cryptominisat/spkg-install.in similarity index 100% rename from build/pkgs/cryptominisat/spkg-install rename to build/pkgs/cryptominisat/spkg-install.in diff --git a/build/pkgs/csdp/SPKG.rst b/build/pkgs/csdp/SPKG.rst new file mode 100644 index 00000000000..88b87acaa88 --- /dev/null +++ b/build/pkgs/csdp/SPKG.rst @@ -0,0 +1,52 @@ +csdp +==== + +Description +----------- + +This is a fast SDP solver written in C, with a callable library namely, +an autotool'ed version of CSDP, by Brian Borchers, see +https://projects.coin-or.org/Csdp + +License +------- + +Common Public License Version 1.0 + + +Upstream Contact +---------------- + +Dmitrii Pasechnik + +Dependencies +------------ + + +Special Update/Build Instructions +--------------------------------- + +csdp is an autotool'ed version of CSDP, see +https://projects.coin-or.org/Csdp, developed in its own repository at +http://github.org/dimpase/csdp. + +To update to a new version, you need to bump the version number in +configure.ac and rerun autotools (autoreconf -fiv). Any changes should +be merged to the upstream repo. + +The build is done with NOSHORTS variable defined; this makes it +compatible with packages, where NOSHORTS must be defined, e.g. +https://github.com/dimpase/pycsdp; also the Sage Cython interface needs +NOSHORTS defined. + +Detailed steps to build the spkg are as follows. You need + +- git +- autotools and libtool (the full autohell suite, version at least + 2.67) + +With these ready: + +- ./spkg-src +- copy the resulting csdp-.tar.gz to SAGE_ROOT/upstream, + or somewhere else appropriate diff --git a/build/pkgs/csdp/SPKG.txt b/build/pkgs/csdp/SPKG.txt deleted file mode 100644 index 993a28bb3c0..00000000000 --- a/build/pkgs/csdp/SPKG.txt +++ /dev/null @@ -1,41 +0,0 @@ -= csdp = - -== Description == - -This is a fast SDP solver written in C, with a callable library -namely, an autotool'ed version of CSDP, by Brian Borchers, -see https://projects.coin-or.org/Csdp - -== License == - -Common Public License Version 1.0 - -== Upstream Contact == - -Dmitrii Pasechnik - -== Dependencies == - - -== Special Update/Build Instructions == - -csdp is an autotool'ed version of CSDP, see https://projects.coin-or.org/Csdp, -developed in its own repository at http://github.org/dimpase/csdp. - -To update to a new version, you need to bump the version number in -configure.ac and rerun autotools (autoreconf -fiv). -Any changes should be merged to the upstream repo. - -The build is done with NOSHORTS variable defined; this makes it compatible -with packages, where NOSHORTS must be defined, e.g. -https://github.com/dimpase/pycsdp; -also the Sage Cython interface needs NOSHORTS defined. - -Detailed steps to build the spkg are as follows. You need - * git - * autotools and libtool (the full autohell suite, version at least 2.67) - -With these ready: - * ./spkg-src - * copy the resulting csdp-.tar.gz to SAGE_ROOT/upstream, - or somewhere else appropriate diff --git a/build/pkgs/csdp/spkg-install b/build/pkgs/csdp/spkg-install.in similarity index 100% rename from build/pkgs/csdp/spkg-install rename to build/pkgs/csdp/spkg-install.in diff --git a/build/pkgs/cunningham_tables/SPKG.txt b/build/pkgs/cunningham_tables/SPKG.txt new file mode 100644 index 00000000000..920aef8a904 --- /dev/null +++ b/build/pkgs/cunningham_tables/SPKG.txt @@ -0,0 +1,11 @@ +cunningham_tables + +The script read_cunningham_prime_factors.py was used to generate the data set +from the file http://cage.ugent.be/~jdemeyer/cunningham/main.gz +We include a local copy, main.gz +(see comments in the file for details) + +== Changelog == + +2009/10/18 - version 1.0 - Initial version +* add cunningham_prime_factors diff --git a/build/pkgs/cunningham_tables/checksums.ini b/build/pkgs/cunningham_tables/checksums.ini new file mode 100644 index 00000000000..d6de22c0850 --- /dev/null +++ b/build/pkgs/cunningham_tables/checksums.ini @@ -0,0 +1,5 @@ +tarball=cunningham_tables-VERSION.tar.gz +sha1=8bea1a113d85bb9c37d8f213dd19525d9d026f22 +md5=e71b32f12e9a46c1c86e275e8441a06b +cksum=1990403877 +upstream_url=http://users.ox.ac.uk/~coml0531/sage/cunningham_tables-1.0.tar.gz diff --git a/build/pkgs/cunningham_tables/main.gz b/build/pkgs/cunningham_tables/main.gz new file mode 100644 index 00000000000..2265df843d6 Binary files /dev/null and b/build/pkgs/cunningham_tables/main.gz differ diff --git a/build/pkgs/cunningham_tables/package-version.txt b/build/pkgs/cunningham_tables/package-version.txt new file mode 100644 index 00000000000..d3827e75a5c --- /dev/null +++ b/build/pkgs/cunningham_tables/package-version.txt @@ -0,0 +1 @@ +1.0 diff --git a/build/pkgs/cunningham_tables/read_cunningham_prime_factors.py b/build/pkgs/cunningham_tables/read_cunningham_prime_factors.py new file mode 100644 index 00000000000..adc28b35336 --- /dev/null +++ b/build/pkgs/cunningham_tables/read_cunningham_prime_factors.py @@ -0,0 +1,56 @@ +# python2/Sage script used to create the package +# (needs porting to python3 Sage) +import urllib +import gzip +import StringIO +from re import match,findall + +# the following URL is gone, but may be found on archive.org +# webfile=urllib.urlopen("http://cage.ugent.be/~jdemeyer/cunningham/main.gz") +webfile=urllib.request.urlopen("https://web.archive.org/web/20190315214633/http://cage.ugent.be/~jdemeyer/cunningham/main.gz") +localfile=StringIO.StringIO(webfile.read()) +data = gzip.GzipFile(fileobj=localfile) + +L = [] + +for line in data: + l=match("\(.*\)( .*)",line).groups() + if len(l[0])>0: + L += map(Integer,findall("[ \.]([1234567890]+)",l[0])) + +# I add the factorisation of (2^4096-1) because I use it + +L += [ + 3, + 5, + 17, + 257, + 641, + 65537, + 274177, + 319489, + 974849, + 2424833, + 6700417, + 45592577, + 6487031809, + 67280421310721, + 1238926361552897, + 59649589127497217, + 167988556341760475137, + 5704689200685129054721, + 3560841906445833920513, + 4659775785220018543264560743076778192897, + 7455602825647884208337395736200454918783366342657, + 93461639715357977769163558199606896584051237541638188580280321, + 741640062627530801524787141901937474059940781097519023905821316144415759504705008092818711693940737, + 130439874405488189727484768796509903946608530841611892186895295776832416251471863574140227977573104895898783928842923844831149032913798729088601617946094119449010595906710130531906171018354491609619193912488538116080712299672322806217820753127014424577, + 173462447179147555430258970864309778377421844723664084649347019061363579192879108857591038330408837177983810868451546421940712978306134189864280826014542758708589243873685563973118948869399158545506611147420216132557017260564139394366945793220968665108959685482705388072645828554151936401912464931182546092879815733057795573358504982279280090942872567591518912118622751714319229788100979251036035496917279912663527358783236647193154777091427745377038294584918917590325110939381322486044298573971650711059244462177542540706913047034664643603491382441723306598834177 +] + +#sort and unique +cunningham_prime_factors = sorted(list(set(L))) + +F = file("cunningham_prime_factors.sobj","w") +F.write(dumps(cunningham_prime_factors)) +F.close() diff --git a/build/pkgs/cunningham_tables/spkg-install.in b/build/pkgs/cunningham_tables/spkg-install.in new file mode 100755 index 00000000000..e613f07744d --- /dev/null +++ b/build/pkgs/cunningham_tables/spkg-install.in @@ -0,0 +1 @@ +cp -r src/src/cunningham_tables $SAGE_SHARE/cunningham_tables diff --git a/build/pkgs/cunningham_tables/spkg-legacy-uninstall.in b/build/pkgs/cunningham_tables/spkg-legacy-uninstall.in new file mode 100644 index 00000000000..d80dcc284c0 --- /dev/null +++ b/build/pkgs/cunningham_tables/spkg-legacy-uninstall.in @@ -0,0 +1 @@ +rm -rf $SAGE_SHARE/cunningham_tables diff --git a/build/pkgs/python2/type b/build/pkgs/cunningham_tables/type similarity index 100% rename from build/pkgs/python2/type rename to build/pkgs/cunningham_tables/type diff --git a/build/pkgs/curl/SPKG.rst b/build/pkgs/curl/SPKG.rst new file mode 100644 index 00000000000..a5afed66d66 --- /dev/null +++ b/build/pkgs/curl/SPKG.rst @@ -0,0 +1,31 @@ +curl +==== + +Description +----------- + +Multiprotocols data transfer library (and utility). + +License +------- + +"MIT style license" : see file "COPYING" at the root of the source +tarball, explanations at https://curl.haxx.se/docs/copyright.html. + + +Upstream Contact +---------------- + +According to the file README at the root of the tarball, contact is done +by mailing https://curl.haxx.se/mail/ + +Dependencies +------------ + +None listed. + + +Special Update/Build Instructions +--------------------------------- + +None. diff --git a/build/pkgs/curl/SPKG.txt b/build/pkgs/curl/SPKG.txt deleted file mode 100644 index 682d4e60574..00000000000 --- a/build/pkgs/curl/SPKG.txt +++ /dev/null @@ -1,23 +0,0 @@ -= curl = - -== Description == - -Multiprotocols data transfer library (and utility). - -== License == - -"MIT style license" : see file "COPYING" at the root of the source -tarball, explanations at https://curl.haxx.se/docs/copyright.html. - -== Upstream Contact == - -According to the file README at the root of the tarball, contact is -done by mailing https://curl.haxx.se/mail/ - -== Dependencies == - -None listed. - -== Special Update/Build Instructions == - -None. diff --git a/build/pkgs/curl/distros/cygwin.txt b/build/pkgs/curl/distros/cygwin.txt new file mode 100644 index 00000000000..7bcb9889a4d --- /dev/null +++ b/build/pkgs/curl/distros/cygwin.txt @@ -0,0 +1 @@ +libcurl-devel curl diff --git a/build/pkgs/curl/distros/slackware.txt b/build/pkgs/curl/distros/slackware.txt new file mode 100644 index 00000000000..ec2284c2d68 --- /dev/null +++ b/build/pkgs/curl/distros/slackware.txt @@ -0,0 +1,5 @@ +curl +cyrus-sasl +# giac build needs libldap +openldap-client +libssh2 diff --git a/build/pkgs/curl/spkg-check.in b/build/pkgs/curl/spkg-check.in new file mode 100644 index 00000000000..1e5337a2242 --- /dev/null +++ b/build/pkgs/curl/spkg-check.in @@ -0,0 +1,8 @@ +# Do not run a test suite: see https://trac.sagemath.org/ticket/30093. +# +# - The test suite takes a long time and can fail in minor ways. +# - If some major aspect of curl fails, it only affects the build +# process of Sage, not the mathematical correctness of its results. +# - If some minor aspect of curl fails, it won't affect anything about Sage. + +echo "We skip the test suite for curl: see https://trac.sagemath.org/ticket/30093." diff --git a/build/pkgs/curl/spkg-install b/build/pkgs/curl/spkg-install.in similarity index 100% rename from build/pkgs/curl/spkg-install rename to build/pkgs/curl/spkg-install.in diff --git a/build/pkgs/cvxopt/SPKG.rst b/build/pkgs/cvxopt/SPKG.rst new file mode 100644 index 00000000000..6ab03adb886 --- /dev/null +++ b/build/pkgs/cvxopt/SPKG.rst @@ -0,0 +1,56 @@ +CVXOPT +====== + +Description +----------- + +CVXOPT is a free software package for convex optimization based on the +Python programming language. It can be used with the interactive Python +interpreter, on the command line by executing Python scripts, or +integrated in other software via Python extension modules. Its main +purpose is to make the development of software for convex optimization +applications straightforward by building on Python's extensive standard +library and on the strengths of Python as a high-level programming +language. + + +Upstream Contact +---------------- + +- J. Dahl +- L. Vandenberghe + +Downloaded from http://cvxopt.org + +License +------- + +GPLv3 or later. Includes parts under GPLv2, GNU Lesser General Public +License, v2.1. See src/LICENSE for more details. (Sage-compatible) + +Dependencies +------------ + +- GNU patch +- GSL +- GLPK + + +Special Update/Build Instructions +--------------------------------- + +- cvxopt.h.patch: Fix building with GCC on Solaris. + +- setup.py.patch: look for libraries and includes in $SAGE_LOCAL + instead of /usr. Add fortran, blas,... libraries if needed. + Build with GSL and GLPK support. + +- remove doc/html/, as it can be rebuild by invoking 'sage -sh' and + running 'make html' in doc/ + +- TODO: Add more tests in spkg-check + +- TODO: one might want to enhance the code to allow other Sage + random sources, at the moment only GSL is used in CVXOPT-1.1.3 + spkg, apparently it will need an unclear to me "with seed(..)" + construct. diff --git a/build/pkgs/cvxopt/SPKG.txt b/build/pkgs/cvxopt/SPKG.txt deleted file mode 100644 index e479b4719cd..00000000000 --- a/build/pkgs/cvxopt/SPKG.txt +++ /dev/null @@ -1,140 +0,0 @@ -= CVXOPT = - -== Description == - -CVXOPT is a free software package for convex optimization based on the -Python programming language. It can be used with the interactive -Python interpreter, on the command line by executing Python scripts, -or integrated in other software via Python extension modules. Its main -purpose is to make the development of software for convex optimization -applications straightforward by building on Python's extensive -standard library and on the strengths of Python as a high-level -programming language. - -== Upstream Contact == - - * J. Dahl - * L. Vandenberghe - -Downloaded from http://cvxopt.org - -== License == - -GPLv3 or later. Includes parts under GPLv2, -GNU Lesser General Public License, v2.1. See src/LICENSE for more details. -(Sage-compatible) - -== Dependencies == - - * GNU patch - * GSL - * GLPK - -== Special Update/Build Instructions == - - * cvxopt.h.patch: Fix building with GCC on Solaris. - - * setup.py.patch: look for libraries and includes in $SAGE_LOCAL - instead of /usr. Add fortran, blas,... libraries if needed. - Build with GSL and GLPK support. - - * remove doc/html/, as it can be rebuild by invoking 'sage -sh' and - running 'make html' in doc/ - - * TODO: Add more tests in spkg-check - - * TODO: one might want to enhance the code to allow other Sage - random sources, at the moment only GSL is used in CVXOPT-1.1.3 - spkg, apparently it will need an unclear to me "with seed(..)" - construct. - -== Changelog == - -=== cvxopt-1.1.8 (Dima Pasechnik, 10 Dec 2015) === - * #19687: upgrade to version 1.1.8. - -=== cvxopt-1.1.7 (Dima Pasechnik, 24 Aug 2014) === - * #16874: upgrade to version 1.1.7. - -=== cvxopt-1.1.6.p0 (Volker Braun, Jeroen Demeyer, 15 May 2013) === - * #12832: upgrade to version 1.1.6. - * Remove undocumented patch changing int_t to int. - * Fix BLAS_LIB_DIR on Cygwin and OS X. - -=== cvxopt-1.1.5.p0 (Jean-Pierre Flori, 5 December 2012) === - * #13799: add back linking dependencies removed by #13160 and needed on Cygwin. - -=== cvxopt-1.1.5 (François Bissey, 25 June 2012) === - * #13160: update to 1.1.5, drop dense.c patch as it is not needed anymore. - * rewrite and simplify the patch for setup.py. Keep the necessary linking minimal. - * First attempt at doc building - -=== cvxopt-1.1.4.p1 (Jeroen Demeyer, 3 April 2012) === - * Trac #12011: edit cvxopt.h.patch to apply with all GCC versions. - -=== cvxopt-1.1.4.p0 (John Palmieri, 31 March 2012) === - * Upgrade to version 1.1.4. - * Trac #12011: apply upstream fix (from 1.1.5) dense.c.patch to fix - self-tests on OS X Lion. - -=== cvxopt-1.1.3.p1 (Jeroen Demeyer, 23 February 2012) === - * Trac #12519: Do not add -lcblas and -latlas on Darwin, since those - libraries aren't installed on Darwin. - * Use patch for patching. - * Make spkg-check actually fail when there is a failure. - * Removed useless bits from patches/setup.py.patch - * Removed useless __init__.py.patch - -=== cvxopt-1.1.3 (Dima Pasechnik, Mike Hansen, Harald Schilly, David Kirkby, Peter Jeremy, November, 2010) === - * fiddled with the lists of libraries to link, on per Fortran compiler basis. - * added recognition of g95 directly, rather than by platform. - * added getting the right -L path to libf95 for g95. - * #6456 Updated to 1.1.3. - * Modified setup.py to build correctly under Cygwin. - * applied P.Jeremy's FreeBSD patch (see #6456) - * corrected the 64-bit specific int* bug reported by pjeremy - * turned on GSL extension (this is the default mode for CVXOPT, and GSL is a standard Sage spkg, so - this makes perfect sense); this in particular allowed to get rid of strange random seed-related - import bugs uncovered by David Kirkby's spkg-check - * modified spkg-check to report test names, cd to appropriate subdirs, and skip .bin files. - * corrected the .patch files in patches/ to be in right order --- just run the makepatchfiles - script to re-create these files! - * removed html doc files in src/doc; the .rst doc files are there, so it's a question of - rebuilding them - (e.g. one can do sage -sh; cd src/doc; make html) - * included David's shortened and gcc-version targeted Sun-specific patch - (comment 88 on #6456); removed sun_complex.h - * took care of SPKG.txt sections, as mentioned in comment 87 on #6456 - * incorporating building the GLPK extension (track #9598) - * #9525 Correct the fact that installation of cvxopt will allways - report it was - successful, even if it failed. - * Add a spkg-check file, as #9281 listed cvxopt was missing such a file - * implemented changes requested in comment 28 of track ticket #6456: - - (kept a harmless SAGE_LOCAL check) - - On building cvxopt extensions: - the non-required extension are not built; - however, it is possible to switch on the dsdp and dsl extensions by - flipping the BUILD_..-flags in setup.py (and modifying dirs for dsdp) - Remaining extensions were not tested; it should not be hard to have - them provided the corresponding libs are there (e.g. parts of Sage) - - * modified setup.py to streamline and make it more Sage-like; - in particular got rid of f77blas and f95 dependences that - are obsolete, since Sage switched to gfortran - * rebased Solaris-specific patches from cvxopt-0.9 spkg - (only cvxopt.h needed to be patched, while the old - native cvxopt random framework is now gone (since cvxopt-1.1 - at least) - * modified setup.py and src/python/__init__.py - * moved old patches to patches-old (solaris, ...) for future reference - * simplifying spkg-install - -=== cvxopt-0.9.p8 (Gonzalo Tornaria, June 19th, 2009) === - * Replace perl spkg-install with a bash one. - -=== cvxopt-0.9.p7 (Michael Abshoff, September 24th, 2008) === - * Work around Solaris 10 problem with complex.h - diff --git a/build/pkgs/cvxopt/dependencies b/build/pkgs/cvxopt/dependencies index d86041c7d51..8930227ba65 100644 --- a/build/pkgs/cvxopt/dependencies +++ b/build/pkgs/cvxopt/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) numpy $(BLAS) gsl glpk suitesparse | pkgconfig pip nose matplotlib +$(PYTHON) numpy $(BLAS) gsl glpk suitesparse | $(PYTHON_TOOLCHAIN) pkgconfig nose matplotlib matplotlib is needed to test cvxopt (i.e., if SAGE_CHECK=yes). See #12742. diff --git a/build/pkgs/cvxopt/spkg-check b/build/pkgs/cvxopt/spkg-check deleted file mode 100644 index ecb9ebcb76b..00000000000 --- a/build/pkgs/cvxopt/spkg-check +++ /dev/null @@ -1,22 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -cd src - -if ! command -v nosetests ; then - echo >&2 'Testing cvxopt requires the package nose to be installed' - exit 1 -fi - -echo "Testing cvxopt..." - -nosetests - -if [ $? -ne 0 ]; then - echo >&2 "Error running self tests." - exit 1 -fi - diff --git a/build/pkgs/cvxopt/spkg-check.in b/build/pkgs/cvxopt/spkg-check.in new file mode 100644 index 00000000000..e251b2cbb6f --- /dev/null +++ b/build/pkgs/cvxopt/spkg-check.in @@ -0,0 +1,8 @@ +cd src + +if ! command -v nosetests ; then + echo >&2 'Testing cvxopt requires the package nose to be installed' + exit 1 +fi + +nosetests diff --git a/build/pkgs/cvxopt/spkg-install b/build/pkgs/cvxopt/spkg-install deleted file mode 100644 index e5cfab9e1b6..00000000000 --- a/build/pkgs/cvxopt/spkg-install +++ /dev/null @@ -1,46 +0,0 @@ -cd src - -# Ensure FreeBSD build finds new, local math.h and complex.h -if [ "$UNAME" = FreeBSD ]; then - export CPPFLAGS="$CPPFLAGS -I$SAGE_LOCAL/include" -fi - -# Stolen from Gentoo -pkg_libs() { - pkg-config --libs-only-l $* | \ - sed -e 's:[ ]-l*\(pthread\|m\)\([ ]\|$\)::g' -e 's:[ ]*$::' | \ - tr ' ' '\n' | sort -u | sed -e "s:^-l\(.*\):\1:g" | \ - tr '\n' ';' | sed -e 's:;$::' -} - - -# configure cvxopt by variables -# Note that *_INC_DIR variables have to be non-empty. -# Compilers don't like "-I ". -export CVXOPT_BLAS_LIB="$(pkg_libs blas)" -export CVXOPT_BLAS_LIB_DIR="$(pkg-config --variable=libdir blas)" -export CVXOPT_LAPACK_LIB="$(pkg_libs lapack)" - -export CVXOPT_SUITESPARSE_LIB_DIR="${SAGE_LOCAL}" -export CVXOPT_SUITESPARSE_INC_DIR="${SAGE_LOCAL}/include" - -export CVXOPT_BUILD_GLPK=1 -export CVXOPT_GLPK_LIB_DIR="${SAGE_LOCAL}" -export CVXOPT_GLPK_INC_DIR="${SAGE_LOCAL}/include" - -export CVXOPT_BUILD_GSL=1 -export CVXOPT_GSL_LIB_DIR="$(pkg-config --variable=libdir gsl)" -export CVXOPT_GSL_INC_DIR="$(pkg-config --variable=includedir gsl)" - -sdh_pip_install . - -if [ "x$SAGE_SPKG_INSTALL_DOCS" = xyes ] ; then - cd doc - # checking to see if there is previously installed documentation. - if [ -d $SAGE_LOCAL/share/doc/cvxopt/html ] ; then - rm -rf $SAGE_LOCAL/share/doc/cvxopt/html - fi - mkdir -p "${SAGE_DESTDIR}${SAGE_LOCAL}/share/doc/cvxopt/html" - cp -r html/* "${SAGE_DESTDIR}${SAGE_LOCAL}/share/doc/cvxopt/html/" -fi - diff --git a/build/pkgs/cvxopt/spkg-install.in b/build/pkgs/cvxopt/spkg-install.in new file mode 100644 index 00000000000..bed0f34ceee --- /dev/null +++ b/build/pkgs/cvxopt/spkg-install.in @@ -0,0 +1,92 @@ +cd src + +# This is a POSIX (non-bash) compatible version of the same function +# in the Gentoo cvxopt package. It's more general than it needs to +# be right now because we may need to use the "L" and "I" modes in +# the future to support system installations of e.g. suitesparse. +# +# The BLAS_LIB and LAPACK_LIB variables (among others) in cvxopt's +# setup.py are passed in as colon-delimited strings. So, for example, +# if your blas "l" flags are "-lblas -lcblas", then cvxopt wants +# "blas;cblas" for BLAS_LIB. +# +# The following function takes a flag type ("l", "L", or "I") as its +# first argument and a list of packages as its remaining arguments. It +# outputs a list of libraries, library paths, or include paths, +# respectively, for the given packages, retrieved using pkg-config and +# deduplicated, in the appropriate format. +# +cvxopt_output() { + FLAGNAME="${1}" + shift + PACKAGES="${@}" + + case "${FLAGNAME}" in + l) PKGCONFIG_MODE="--libs-only-l";; + L) PKGCONFIG_MODE="--libs-only-L";; + I) PKGCONFIG_MODE="--cflags-only-I";; + *) echo "invalid flag name: ${FLAGNAME}"; exit 1;; + esac + + CVXOPT_OUTPUT="" + for PKGCONFIG_ITEM in $(pkg-config ${PKGCONFIG_MODE} ${PACKAGES}); do + # First strip off the leading "-l", "-L", or "-I", and replace + # it with a semicolon... + PKGCONFIG_ITEM=";${PKGCONFIG_ITEM#-${FLAGNAME}}" + + # Now check to see if this element is already present in the + # list, and skip it if it is. This eliminates multiple entries + # from winding up in the list when multiple package arguments are + # passed to this function. + if [ "${CVXOPT_OUTPUT}" != "${CVXOPT_OUTPUT%${PKGCONFIG_ITEM}}" ] + then + # It was already the last entry in the list, so skip it. + continue + elif [ "${CVXOPT_OUTPUT}" != "${CVXOPT_OUTPUT%${PKGCONFIG_ITEM};*}" ] + then + # It was an earlier entry in the list. These two cases are + # separate to ensure that we can e.g. find ";m" at the end + # of the list, but that we don't find ";metis" in the process. + continue + fi + + # It isn't in the list yet, so append it. + CVXOPT_OUTPUT="${CVXOPT_OUTPUT}${PKGCONFIG_ITEM}" + done + + # Strip the leading ";" from ";foo;bar" before output. + echo "${CVXOPT_OUTPUT#;}" +} + +# configure cvxopt by variables +# Note that *_INC_DIR variables have to be non-empty. +# Compilers don't like "-I ". +export CVXOPT_BLAS_LIB="$(cvxopt_output l blas)" +export CVXOPT_BLAS_LIB_DIR="$(pkg-config --variable=libdir blas)" +export CVXOPT_LAPACK_LIB="$(cvxopt_output l lapack)" + +if test "x$SAGE_SUITESPARSE_LOCALINSTALL" != "x"; then + export CVXOPT_SUITESPARSE_LIB_DIR="${SAGE_LOCAL}" + export CVXOPT_SUITESPARSE_INC_DIR="${SAGE_LOCAL}/include" +fi + +export CVXOPT_BUILD_GLPK=1 +export CVXOPT_GLPK_LIB_DIR="${SAGE_LOCAL}" +export CVXOPT_GLPK_INC_DIR="${SAGE_LOCAL}/include" + +export CVXOPT_BUILD_GSL=1 +export CVXOPT_GSL_LIB_DIR="$(pkg-config --variable=libdir gsl)" +export CVXOPT_GSL_INC_DIR="$(pkg-config --variable=includedir gsl)" + +sdh_pip_install . + +if [ "x$SAGE_SPKG_INSTALL_DOCS" = xyes ] ; then + cd doc + # checking to see if there is previously installed documentation. + if [ -d $SAGE_LOCAL/share/doc/cvxopt/html ] ; then + rm -rf $SAGE_LOCAL/share/doc/cvxopt/html + fi + mkdir -p "${SAGE_DESTDIR}${SAGE_LOCAL}/share/doc/cvxopt/html" + cp -r html/* "${SAGE_DESTDIR}${SAGE_LOCAL}/share/doc/cvxopt/html/" +fi + diff --git a/build/pkgs/cycler/SPKG.rst b/build/pkgs/cycler/SPKG.rst new file mode 100644 index 00000000000..7849d027a22 --- /dev/null +++ b/build/pkgs/cycler/SPKG.rst @@ -0,0 +1,29 @@ +cycler +====== + +Description +----------- + +Cycler is a small break of of matplotlib to deal with "composable +cycles". It is a required dependency of matplotlib 1.5.0. + +License +------- + +BSD + + +Upstream Contact +---------------- + +cycler is developed on github: https://github.com/matplotlib/cycler + +A more informative webpage about cycler, its motivation and usage is at +http://tacaswell.github.io/cycler/ + +Dependencies +------------ + +- python +- setuptools +- six diff --git a/build/pkgs/cycler/SPKG.txt b/build/pkgs/cycler/SPKG.txt deleted file mode 100644 index 2b8a4665363..00000000000 --- a/build/pkgs/cycler/SPKG.txt +++ /dev/null @@ -1,24 +0,0 @@ -= cycler = - -== Description == - -Cycler is a small break of of matplotlib to -deal with "composable cycles". -It is a required dependency of matplotlib 1.5.0. - -== License == -BSD - -== Upstream Contact == - -cycler is developed on github: https://github.com/matplotlib/cycler - -A more informative webpage about cycler, its motivation and usage -is at http://tacaswell.github.io/cycler/ - -== Dependencies == - - * python - * setuptools - * six - diff --git a/build/pkgs/cycler/dependencies b/build/pkgs/cycler/dependencies index d7188c0a5cd..fd848c100a8 100644 --- a/build/pkgs/cycler/dependencies +++ b/build/pkgs/cycler/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) six | setuptools pip +$(PYTHON) six | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/cycler/spkg-install b/build/pkgs/cycler/spkg-install deleted file mode 100644 index 73436b8b992..00000000000 --- a/build/pkgs/cycler/spkg-install +++ /dev/null @@ -1,14 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . -if [ $? -ne 0 ]; then - echo "Error installing cycler ... exiting" - exit 1 -fi - diff --git a/build/pkgs/backports_shutil_get_terminal_size/spkg-install b/build/pkgs/cycler/spkg-install.in similarity index 100% rename from build/pkgs/backports_shutil_get_terminal_size/spkg-install rename to build/pkgs/cycler/spkg-install.in diff --git a/build/pkgs/cygwin-bootstrap.txt b/build/pkgs/cygwin-bootstrap.txt new file mode 100644 index 00000000000..5cd20fd529c --- /dev/null +++ b/build/pkgs/cygwin-bootstrap.txt @@ -0,0 +1,2 @@ +# Packages needed for ./bootstrap +gettext-devel autoconf automake libtool diff --git a/build/pkgs/cygwin.txt b/build/pkgs/cygwin.txt new file mode 100644 index 00000000000..635afe8f9c9 --- /dev/null +++ b/build/pkgs/cygwin.txt @@ -0,0 +1,29 @@ +# This file, build/pkgs/cygwin.txt, contains name Cygwin packages +# needed for installation of Sage from source. +# +# In addition, the files build/pkgs/SPKG/cygwin.txt contain the names +# of packages that provide the equivalent of SPKG. +# +# See build/bin/sage-spkg, where this information is processed +# for use in "sage -info SPKG". +# +# Everything on a line after a # character is ignored. +# +binutils +make +m4 +# a system python is needed for downloading the sage packages, https://trac.sagemath.org/ticket/29090 +python37-urllib3 python37 +perl +perl-ExtUtils-MakeMaker +tar +gcc-core +gcc-g++ +# Needed according to embray at https://trac.sagemath.org/ticket/26964: +# The need for which comes [...] from MPIR's configure script +findutils +which +# For python3 build +libcrypt-devel +# For R build +libiconv-devel diff --git a/build/pkgs/cypari/SPKG.rst b/build/pkgs/cypari/SPKG.rst new file mode 100644 index 00000000000..c680d58e2d1 --- /dev/null +++ b/build/pkgs/cypari/SPKG.rst @@ -0,0 +1,26 @@ +cypari2 +======= + +Description +----------- + +A Python interface to the number theory library libpari. + +License +------- + +GPL version 2 or later + + +Upstream Contact +---------------- + +https://github.com/defeo/cypari2 + +Dependencies +------------ + +- Python +- Cython +- PARI +- cysignals diff --git a/build/pkgs/cypari/SPKG.txt b/build/pkgs/cypari/SPKG.txt deleted file mode 100644 index a4a383d0dde..00000000000 --- a/build/pkgs/cypari/SPKG.txt +++ /dev/null @@ -1,20 +0,0 @@ -= cypari2 = - -== Description == - -A Python interface to the number theory library libpari. - -== License == - -GPL version 2 or later - -== Upstream Contact == - -https://github.com/defeo/cypari2 - -== Dependencies == - -* Python -* Cython -* PARI -* cysignals diff --git a/build/pkgs/cypari/dependencies b/build/pkgs/cypari/dependencies index e8630bba15a..3f04dd2b8f3 100644 --- a/build/pkgs/cypari/dependencies +++ b/build/pkgs/cypari/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) cython pari cysignals | pip +$(PYTHON) cython pari cysignals | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/cypari/spkg-check b/build/pkgs/cypari/spkg-check.in similarity index 100% rename from build/pkgs/cypari/spkg-check rename to build/pkgs/cypari/spkg-check.in diff --git a/build/pkgs/cypari/spkg-install b/build/pkgs/cypari/spkg-install.in similarity index 100% rename from build/pkgs/cypari/spkg-install rename to build/pkgs/cypari/spkg-install.in diff --git a/build/pkgs/cysignals/SPKG.rst b/build/pkgs/cysignals/SPKG.rst new file mode 100644 index 00000000000..55f1e2b2dd0 --- /dev/null +++ b/build/pkgs/cysignals/SPKG.rst @@ -0,0 +1,25 @@ +cysignals +========= + +Description +----------- + +Interrupt and signal handling for Cython + +License +------- + +LGPL version 3 or later + + +Upstream Contact +---------------- + +https://github.com/sagemath/cysignals + +Dependencies +------------ + +- Python +- Cython +- PARI (optional) diff --git a/build/pkgs/cysignals/SPKG.txt b/build/pkgs/cysignals/SPKG.txt deleted file mode 100644 index acbf6169d3d..00000000000 --- a/build/pkgs/cysignals/SPKG.txt +++ /dev/null @@ -1,19 +0,0 @@ -= cysignals = - -== Description == - -Interrupt and signal handling for Cython - -== License == - -LGPL version 3 or later - -== Upstream Contact == - -https://github.com/sagemath/cysignals - -== Dependencies == - -* Python -* Cython -* PARI (optional) diff --git a/build/pkgs/cysignals/dependencies b/build/pkgs/cysignals/dependencies index 752273c0a69..19bc31a739c 100644 --- a/build/pkgs/cysignals/dependencies +++ b/build/pkgs/cysignals/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) cython pari | pip +$(PYTHON) cython pari | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/cysignals/spkg-check b/build/pkgs/cysignals/spkg-check deleted file mode 100644 index 37fa0dddfcc..00000000000 --- a/build/pkgs/cysignals/spkg-check +++ /dev/null @@ -1,7 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -cd src && $MAKE check-install PYTHON=sage-python23 diff --git a/build/pkgs/cysignals/spkg-check.in b/build/pkgs/cysignals/spkg-check.in new file mode 100644 index 00000000000..be297af0cdd --- /dev/null +++ b/build/pkgs/cysignals/spkg-check.in @@ -0,0 +1 @@ +cd src && $MAKE check-install PYTHON=sage-python23 diff --git a/build/pkgs/cysignals/spkg-install b/build/pkgs/cysignals/spkg-install deleted file mode 100644 index 88a6c01c68d..00000000000 --- a/build/pkgs/cysignals/spkg-install +++ /dev/null @@ -1,8 +0,0 @@ -cd src - -if [ "$SAGE_DEBUG" = yes ]; then - CYSIGNALS_CONFIGURE="--enable-debug $CYSIGNALS_CONFIGURE" -fi - -sdh_configure $CYSIGNALS_CONFIGURE -sdh_pip_install . diff --git a/build/pkgs/cysignals/spkg-install.in b/build/pkgs/cysignals/spkg-install.in new file mode 100644 index 00000000000..0894611a27e --- /dev/null +++ b/build/pkgs/cysignals/spkg-install.in @@ -0,0 +1,11 @@ +cd src + +# #29473: Override -Wp,-D_FORTIFY_SOURCE from Fedora's python3. +export CPPFLAGS="$CPPFLAGS -Wp,-U_FORTIFY_SOURCE" + +if [ "$SAGE_DEBUG" = yes ]; then + CYSIGNALS_CONFIGURE="--enable-debug $CYSIGNALS_CONFIGURE" +fi + +sdh_configure $CYSIGNALS_CONFIGURE +sdh_pip_install . diff --git a/build/pkgs/cython/SPKG.rst b/build/pkgs/cython/SPKG.rst new file mode 100644 index 00000000000..8c5b201c86c --- /dev/null +++ b/build/pkgs/cython/SPKG.rst @@ -0,0 +1,36 @@ +Cython +====== + +Description +----------- + +Cython is a language that makes writing C extensions for the Python +language as easy as Python itself. Cython is based on the well-known +Pyrex, but supports more cutting edge functionality and optimizations. + +The Cython language is very close to the Python language, but Cython +additio- nally supports calling C functions and declaring C types on +variables and class attributes. This allows the compiler to generate +very efficient C code from Cython code. + +This makes Cython the ideal language for wrapping for external C +libraries, and for fast C modules that speed up the execution of Python +code. + +Website: http://www.cython.org/ + +License +------- + +Apache License, Version 2.0 + + +Upstream Contact +---------------- + +- cython-devel@python.org + +Dependencies +------------ + +- Python diff --git a/build/pkgs/cython/SPKG.txt b/build/pkgs/cython/SPKG.txt deleted file mode 100644 index 6baffa31c41..00000000000 --- a/build/pkgs/cython/SPKG.txt +++ /dev/null @@ -1,27 +0,0 @@ -= Cython = - -== Description == - -Cython is a language that makes writing C extensions for the Python language as -easy as Python itself. Cython is based on the well-known Pyrex, but supports -more cutting edge functionality and optimizations. - -The Cython language is very close to the Python language, but Cython additio- -nally supports calling C functions and declaring C types on variables and class -attributes. This allows the compiler to generate very efficient C code from -Cython code. - -This makes Cython the ideal language for wrapping for external C libraries, and -for fast C modules that speed up the execution of Python code. - -Website: http://www.cython.org/ - -== License == - -Apache License, Version 2.0 - -== Upstream Contact == - * cython-devel@python.org - -== Dependencies == - * Python diff --git a/build/pkgs/cython/checksums.ini b/build/pkgs/cython/checksums.ini index 7e9b0490f41..759e496b3b2 100644 --- a/build/pkgs/cython/checksums.ini +++ b/build/pkgs/cython/checksums.ini @@ -1,4 +1,5 @@ tarball=Cython-VERSION.tar.gz -sha1=1c0c6cb9ebb875e8769863e1720683b24a755a7c -md5=f212574687a52706ff53738a64f91398 -cksum=1612385007 +sha1=3aafce4489a7bc7a48c843cdfb8dac4677fdac50 +md5=12c5e45af71dcc6dff28cdcbcbef6f39 +cksum=3658613797 +upstream_url=https://pypi.io/packages/source/C/Cython/Cython-VERSION.tar.gz diff --git a/build/pkgs/cython/dependencies b/build/pkgs/cython/dependencies index 2573414ce8a..15df0c4d6d8 100644 --- a/build/pkgs/cython/dependencies +++ b/build/pkgs/cython/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/cython/package-version.txt b/build/pkgs/cython/package-version.txt index fe14ade44a2..09ccc27c724 100644 --- a/build/pkgs/cython/package-version.txt +++ b/build/pkgs/cython/package-version.txt @@ -1 +1 @@ -0.29.12.p0 +0.29.21 diff --git a/build/pkgs/cython/spkg-check b/build/pkgs/cython/spkg-check.in similarity index 100% rename from build/pkgs/cython/spkg-check rename to build/pkgs/cython/spkg-check.in diff --git a/build/pkgs/cython/spkg-install b/build/pkgs/cython/spkg-install.in similarity index 100% rename from build/pkgs/cython/spkg-install rename to build/pkgs/cython/spkg-install.in diff --git a/build/pkgs/d3js/SPKG.rst b/build/pkgs/d3js/SPKG.rst new file mode 100644 index 00000000000..739f46f8f28 --- /dev/null +++ b/build/pkgs/d3js/SPKG.rst @@ -0,0 +1,37 @@ +d3js +==== + +Description +----------- + +D3.js is a JavaScript library for manipulating documents based on data. +The file d3.min.js will be placed into the ${SAGE_SHARE}/d3js/ +directory. + +License +------- + +BSD 3-Clause License + + +Upstream Contact +---------------- + +- Author: Mike Bostock (http://bost.ocks.org/mike/) +- Home page: http://d3js.org/ + +Dependencies +------------ + +None. + + +Special Update/Build Instructions +--------------------------------- + +Two kind of archives can be downloaded from d3.js website: one with all +source code and tests that weights 2,9M (both in zip and tar.gz formats) +and one with the final javascript scripts which weights 121K (zip format +only). Since testing requires node.js that is not shipped with Sage, we +currenlty ship the final js only. Hence we have to transform it from zip +to tar.gz format. Running sage-src should do all the repackaging job. diff --git a/build/pkgs/d3js/SPKG.txt b/build/pkgs/d3js/SPKG.txt deleted file mode 100644 index d7a80a3bc91..00000000000 --- a/build/pkgs/d3js/SPKG.txt +++ /dev/null @@ -1,29 +0,0 @@ -= d3js = - -== Description == - -D3.js is a JavaScript library for manipulating documents based on data. -The file d3.min.js will be placed into the ${SAGE_SHARE}/d3js/ directory. - -== License == - -BSD 3-Clause License - -== Upstream Contact == - -Author: Mike Bostock (http://bost.ocks.org/mike/) -Home page: http://d3js.org/ - -== Dependencies == - -None. - -== Special Update/Build Instructions == - -Two kind of archives can be downloaded from d3.js website: one with all source -code and tests that weights 2,9M (both in zip and tar.gz formats) and one with -the final javascript scripts which weights 121K (zip format only). Since -testing requires node.js that is not shipped with Sage, we currenlty ship the -final js only. Hence we have to transform it from zip to tar.gz format. -Running sage-src should do all the repackaging job. - diff --git a/build/pkgs/d3js/spkg-install b/build/pkgs/d3js/spkg-install deleted file mode 100644 index a1fe93fe433..00000000000 --- a/build/pkgs/d3js/spkg-install +++ /dev/null @@ -1,13 +0,0 @@ -if [ -z "$SAGE_SHARE" ]; then - echo >&2 "SAGE_SHARE undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -TARGET="${SAGE_SHARE}/d3js" -if [ ! -d "${TARGET}" ]; then - mkdir "${TARGET}" -fi - -cp 'src/d3.min.js' "${TARGET}/" - diff --git a/build/pkgs/d3js/spkg-install.in b/build/pkgs/d3js/spkg-install.in new file mode 100644 index 00000000000..853a5305f07 --- /dev/null +++ b/build/pkgs/d3js/spkg-install.in @@ -0,0 +1,7 @@ +TARGET="${SAGE_SHARE}/d3js" +if [ ! -d "${TARGET}" ]; then + mkdir "${TARGET}" +fi + +cp 'src/d3.min.js' "${TARGET}/" + diff --git a/build/pkgs/d3js/spkg-src b/build/pkgs/d3js/spkg-src index d8c8e66062e..134c8f0bc5d 100755 --- a/build/pkgs/d3js/spkg-src +++ b/build/pkgs/d3js/spkg-src @@ -36,6 +36,6 @@ mv "d3js-${ZIP_VERSION}.tar.gz" "${SAGE_ROOT}/upstream/" # update package info cd .. echo "${ZIP_VERSION}" > 'package-version.txt' -sage -sh 'sage-fix-pkg-checksums' +sage --package fix-checksum diff --git a/build/pkgs/database_cremona_ellcurve/SPKG.rst b/build/pkgs/database_cremona_ellcurve/SPKG.rst new file mode 100644 index 00000000000..6a2a75ecfbf --- /dev/null +++ b/build/pkgs/database_cremona_ellcurve/SPKG.rst @@ -0,0 +1,51 @@ +database_cremona_ellcurve +========================= + +Description +----------- + +John Cremona's database of elliptic curves + +See https://github.com/JohnCremona/ecdata + +This is an optional package, not included by default. + +License +------- + +Public Domain + +Dependencies +------------ + +None + +Patches +~~~~~~~ + +- None + + +Upstream Contact +---------------- + +- Author: John Cremona +- Email: john.cremona@gmail.com +- Website: http://homepages.warwick.ac.uk/staff/J.E.Cremona/ + + +Update Instructions +------------------- + +Get an up-to-date copy of the git repository ecdata from +https://github.com/JohnCremona/ecdata. + +If the cremona database has already been installed, remove +``SAGE_DATA/cremona/cremona.db``. Then run + +The build script expects to find the files in subfolders allcurves, +allgens, degphi and allbsd of the ecdata folder. It extracts them and +builds the new cremona.db file from the contents. + +Finally, copy ``SAGE_DATA/cremona/cremona.db`` to the src directory of +the spkg. diff --git a/build/pkgs/database_cremona_ellcurve/SPKG.txt b/build/pkgs/database_cremona_ellcurve/SPKG.txt deleted file mode 100644 index 88b2ba0f6af..00000000000 --- a/build/pkgs/database_cremona_ellcurve/SPKG.txt +++ /dev/null @@ -1,44 +0,0 @@ -= database_cremona_ellcurve = - -== Description == -John Cremona's database of elliptic curves - -See https://github.com/JohnCremona/ecdata - -This is an optional package, not included by default. - -== License == -Public Domain - -== Dependencies == -None - -=== Patches === - * None - -== Upstream Contact == - - * Author: John Cremona - * Email: john.cremona@gmail.com - * Website: http://homepages.warwick.ac.uk/staff/J.E.Cremona/ - -== Update Instructions == - -Get an up-to-date copy of the git repository ecdata from -https://github.com/JohnCremona/ecdata. - -If the cremona database has already been installed, remove -`SAGE_DATA/cremona/cremona.db`. Then run - -{{{ -sage: C = sage.databases.cremona.LargeCremonaDatabase('cremona',False, True) -sage: C._init_from_ftpdata('path/to/ecdata/',0) -}}} - -The build script expects to find the files in subfolders allcurves, -allgens, degphi and allbsd of the ecdata folder. It extracts them and -builds the new cremona.db file from the contents. - -Finally, copy `SAGE_DATA/cremona/cremona.db` to the src directory of -the spkg. - diff --git a/build/pkgs/database_cremona_ellcurve/spkg-install b/build/pkgs/database_cremona_ellcurve/spkg-install.in similarity index 100% rename from build/pkgs/database_cremona_ellcurve/spkg-install rename to build/pkgs/database_cremona_ellcurve/spkg-install.in diff --git a/build/pkgs/database_jones_numfield/SPKG.rst b/build/pkgs/database_jones_numfield/SPKG.rst new file mode 100644 index 00000000000..b19f9a99235 --- /dev/null +++ b/build/pkgs/database_jones_numfield/SPKG.rst @@ -0,0 +1,30 @@ +database_jones_numfield +======================= + +Description +----------- + +This is a table of number fields with bounded ramification and degree +at most 6. + +License +------- + +GPLv2+ + + +Upstream Contact +---------------- + +sage-devel@googlegroups.com + +Dependencies +------------ + +None + + +Special Update/Build Instructions +--------------------------------- + +Created by taking the original old-style spkg and removing crud from it. diff --git a/build/pkgs/database_jones_numfield/SPKG.txt b/build/pkgs/database_jones_numfield/SPKG.txt deleted file mode 100644 index 93d41c86cd3..00000000000 --- a/build/pkgs/database_jones_numfield/SPKG.txt +++ /dev/null @@ -1,21 +0,0 @@ -= database_jones_numfield = - -== Description == - -This is a table of number fields with bounded ramification and degree ≤6. - -== License == - -GPLv2+ - -== Upstream Contact == - -sage-devel@googlegroups.com - -== Dependencies == - -None - -== Special Update/Build Instructions == - -Created by taking the original old-style spkg and removing crud from it. diff --git a/build/pkgs/database_jones_numfield/spkg-install b/build/pkgs/database_jones_numfield/spkg-install.in similarity index 100% rename from build/pkgs/database_jones_numfield/spkg-install rename to build/pkgs/database_jones_numfield/spkg-install.in diff --git a/build/pkgs/database_kohel/SPKG.rst b/build/pkgs/database_kohel/SPKG.rst new file mode 100644 index 00000000000..2208efb7a9d --- /dev/null +++ b/build/pkgs/database_kohel/SPKG.rst @@ -0,0 +1,14 @@ + +Kohel database +============== + +Description +----------- + +Database of modular and Hilbert polynomials. + + +Upstream Contact +---------------- + +- David Kohel diff --git a/build/pkgs/database_kohel/SPKG.txt b/build/pkgs/database_kohel/SPKG.txt deleted file mode 100644 index b98875f31d7..00000000000 --- a/build/pkgs/database_kohel/SPKG.txt +++ /dev/null @@ -1,14 +0,0 @@ -= Kohel database = - -== Description == - -Database of modular and Hilbert polynomials. - -== Upstream Contact == - - * David Kohel - -== Changelog == - - * new style package 20160724 (V. Delecroix) - * initial version 20060803 (D. Kohel and W. Stein) diff --git a/build/pkgs/database_kohel/spkg-install b/build/pkgs/database_kohel/spkg-install deleted file mode 100644 index bbf35a62154..00000000000 --- a/build/pkgs/database_kohel/spkg-install +++ /dev/null @@ -1,31 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -TARGET="${SAGE_SHARE}/kohel/" -rm -rf ${TARGET} - -mkdir ${TARGET} -if [ $? -ne 0 ] -then - echo "Error creating repertory ${TARGET}" - exit 1 -fi - -mv PolMod ${TARGET} -if [ $? -ne 0 ] -then - echo "Error moving PolMod to ${TARGET}" - exit 1 -fi - -mv PolHeeg ${TARGET} -if [ $? -ne 0 ] -then - echo "Error moving PolHeeg to ${TARGET}" - exit 1 -fi diff --git a/build/pkgs/database_kohel/spkg-install.in b/build/pkgs/database_kohel/spkg-install.in new file mode 100644 index 00000000000..5743e1b2a03 --- /dev/null +++ b/build/pkgs/database_kohel/spkg-install.in @@ -0,0 +1,25 @@ +cd src + +TARGET="${SAGE_SHARE}/kohel/" +rm -rf ${TARGET} + +mkdir ${TARGET} +if [ $? -ne 0 ] +then + echo "Error creating repertory ${TARGET}" + exit 1 +fi + +mv PolMod ${TARGET} +if [ $? -ne 0 ] +then + echo "Error moving PolMod to ${TARGET}" + exit 1 +fi + +mv PolHeeg ${TARGET} +if [ $? -ne 0 ] +then + echo "Error moving PolHeeg to ${TARGET}" + exit 1 +fi diff --git a/build/pkgs/database_mutation_class/SPKG.rst b/build/pkgs/database_mutation_class/SPKG.rst new file mode 100644 index 00000000000..fbf5ff84fc8 --- /dev/null +++ b/build/pkgs/database_mutation_class/SPKG.rst @@ -0,0 +1,33 @@ + +Mutation class database +======================= + +Description +----------- + +Contains a database of all exceptional mutation classes of quivers. + +Every file in the database is of the form +``mutation_classes_n.dig6`` for some ``n`` and + +- contains a ``cPickle.dump`` of a dictionary where +- the keys are tuples representing irreducible exceptional quiver + mutation types of rank ``n``, and +- the values are all quivers in the given mutation class stored in + canonical form as ``(dig6,edges)`` where +- ``dig6`` is the dig6 data of the given ``DiGraph``, and +- ``edges`` are the non-simply-laced edges thereof. +- is obtained by running the function + + ``sage.combinat.cluster_algebra_quiver.quiver_mutation_type._save_data_dig6(n, types='Exceptional', verbose=False)`` + + +SPKG Maintainers +---------------- + +- C. Stump + +Dependencies +------------ + +- None diff --git a/build/pkgs/database_mutation_class/SPKG.txt b/build/pkgs/database_mutation_class/SPKG.txt deleted file mode 100644 index ae3facd5d3d..00000000000 --- a/build/pkgs/database_mutation_class/SPKG.txt +++ /dev/null @@ -1,23 +0,0 @@ -= Mutation class database = - -== Description == - - Contains a database of all exceptional mutation classes of quivers. - - Every file in the database is of the form ``mutation_classes_n.dig6`` for some ``n`` and - - contains a ``cPickle.dump`` of a dictionary where - - the keys are tuples representing irreducible exceptional quiver mutation types of rank ``n``, and - - the values are all quivers in the given mutation class stored in canonical form as ``(dig6,edges)`` where - - ``dig6`` is the dig6 data of the given ``DiGraph``, and - - ``edges`` are the non-simply-laced edges thereof. - - is obtained by running the function - ``sage.combinat.cluster_algebra_quiver.quiver_mutation_type._save_data_dig6(n, types='Exceptional', verbose=False)`` - -== SPKG Maintainers == - - * C. Stump - -== Dependencies == - - * None - diff --git a/build/pkgs/database_mutation_class/spkg-install b/build/pkgs/database_mutation_class/spkg-install.in similarity index 100% rename from build/pkgs/database_mutation_class/spkg-install rename to build/pkgs/database_mutation_class/spkg-install.in diff --git a/build/pkgs/database_odlyzko_zeta/SPKG.rst b/build/pkgs/database_odlyzko_zeta/SPKG.rst new file mode 100644 index 00000000000..d0865cd907c --- /dev/null +++ b/build/pkgs/database_odlyzko_zeta/SPKG.rst @@ -0,0 +1,16 @@ + +Zeros of the Riemann zeta function +================================== + +Description +----------- + +Table of zeros of the Riemann zeta function by Andrew Odlyzko. + +This package contains the file 'zeros6' with the first 2,001,052 zeros +of the Riemann zeta function, accurate to within 4*10^(-9). + +Dependencies +------------ + +- Sage library diff --git a/build/pkgs/database_odlyzko_zeta/SPKG.txt b/build/pkgs/database_odlyzko_zeta/SPKG.txt deleted file mode 100644 index 9103d0bd468..00000000000 --- a/build/pkgs/database_odlyzko_zeta/SPKG.txt +++ /dev/null @@ -1,12 +0,0 @@ -= Zeros of the Riemann zeta function = - -== Description == - -Table of zeros of the Riemann zeta function by Andrew Odlyzko. - -This package contains the file 'zeros6' with the first 2,001,052 zeros -of the Riemann zeta function, accurate to within 4*10^(-9). - -== Dependencies == - - * Sage library diff --git a/build/pkgs/database_odlyzko_zeta/spkg-install b/build/pkgs/database_odlyzko_zeta/spkg-install.in similarity index 100% rename from build/pkgs/database_odlyzko_zeta/spkg-install rename to build/pkgs/database_odlyzko_zeta/spkg-install.in diff --git a/build/pkgs/database_stein_watkins/SPKG.rst b/build/pkgs/database_stein_watkins/SPKG.rst new file mode 100644 index 00000000000..6cc091b8ec8 --- /dev/null +++ b/build/pkgs/database_stein_watkins/SPKG.rst @@ -0,0 +1,26 @@ +database_stein_watkins +====================== + +Description +----------- + +The Stein-Watkins database of elliptic curves (full version) + +See http://modular.math.washington.edu/papers/stein-watkins/ + +This is an optional (huge) package, not included by default. + +License +------- + +Public Domain + +Dependencies +------------ + +None + +Patches +~~~~~~~ + +None diff --git a/build/pkgs/database_stein_watkins/SPKG.txt b/build/pkgs/database_stein_watkins/SPKG.txt deleted file mode 100644 index ee6ecb4349f..00000000000 --- a/build/pkgs/database_stein_watkins/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= database_stein_watkins = - -== Description == -The Stein-Watkins database of elliptic curves (full version) - -See http://modular.math.washington.edu/papers/stein-watkins/ - -This is an optional (huge) package, not included by default. - -== License == -Public Domain - -== Dependencies == -None - -=== Patches === - * None diff --git a/build/pkgs/database_stein_watkins/spkg-install b/build/pkgs/database_stein_watkins/spkg-install.in similarity index 100% rename from build/pkgs/database_stein_watkins/spkg-install rename to build/pkgs/database_stein_watkins/spkg-install.in diff --git a/build/pkgs/database_stein_watkins_mini/SPKG.rst b/build/pkgs/database_stein_watkins_mini/SPKG.rst new file mode 100644 index 00000000000..0147cca7e59 --- /dev/null +++ b/build/pkgs/database_stein_watkins_mini/SPKG.rst @@ -0,0 +1,26 @@ +database_stein_watkins_mini +=========================== + +Description +----------- + +The Stein-Watkins database of elliptic curves (small version) + +See http://modular.math.washington.edu/papers/stein-watkins/ + +This is an optional package, not included by default. + +License +------- + +Public Domain + +Dependencies +------------ + +None + +Patches +~~~~~~~ + +None diff --git a/build/pkgs/database_stein_watkins_mini/SPKG.txt b/build/pkgs/database_stein_watkins_mini/SPKG.txt deleted file mode 100644 index 41c575c9a9e..00000000000 --- a/build/pkgs/database_stein_watkins_mini/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= database_stein_watkins_mini = - -== Description == -The Stein-Watkins database of elliptic curves (small version) - -See http://modular.math.washington.edu/papers/stein-watkins/ - -This is an optional package, not included by default. - -== License == -Public Domain - -== Dependencies == -None - -=== Patches === - * None diff --git a/build/pkgs/database_stein_watkins_mini/spkg-install b/build/pkgs/database_stein_watkins_mini/spkg-install deleted file mode 100644 index 4d0c0e13d25..00000000000 --- a/build/pkgs/database_stein_watkins_mini/spkg-install +++ /dev/null @@ -1,2 +0,0 @@ -# Use spkg-install file of database_stein_watkins -. "$SAGE_ROOT/build/pkgs/database_stein_watkins/spkg-install" diff --git a/build/pkgs/database_stein_watkins_mini/spkg-install.in b/build/pkgs/database_stein_watkins_mini/spkg-install.in new file mode 120000 index 00000000000..d7005d17fc4 --- /dev/null +++ b/build/pkgs/database_stein_watkins_mini/spkg-install.in @@ -0,0 +1 @@ +../database_stein_watkins/spkg-install.in \ No newline at end of file diff --git a/build/pkgs/database_symbolic_data/SPKG.rst b/build/pkgs/database_symbolic_data/SPKG.rst new file mode 100644 index 00000000000..ba21d67d5f2 --- /dev/null +++ b/build/pkgs/database_symbolic_data/SPKG.rst @@ -0,0 +1,46 @@ +database_symbolic_data +====================== + +Description +----------- + +The SymbolicData project is set out + +- to develop concepts and tools for profiling, testing and benchmarking + Computer Algebra Software + (CAS) and + +- to collect and interlink relevant data and activities from different + Computer Algebra Communities. + +SymbolicData is an + +- inter-community project that has its roots in the activities of + different Computer Algebra Communities and +- aims at interlinking these activities using modern Semantic Web + concepts. + +Tools and data are designed to be used both + +- on a local site for special testing and profiling purposes +- and to manage a central repository at www.symbolicdata.org. + +License +------- + +GNU General Public License + + +Upstream Contact +---------------- + +- Andreas Nareike + +Dependencies +------------ + + +Special Update/Build Instructions +--------------------------------- + +List patches that need to be applied and what they do diff --git a/build/pkgs/database_symbolic_data/SPKG.txt b/build/pkgs/database_symbolic_data/SPKG.txt deleted file mode 100644 index cc59f6d5193..00000000000 --- a/build/pkgs/database_symbolic_data/SPKG.txt +++ /dev/null @@ -1,41 +0,0 @@ -= database_symbolic_data = - -== Description == - -The SymbolicData project is set out - -* to develop concepts and tools for profiling, testing and benchmarking Computer Algebra Software - (CAS) and -* to collect and interlink relevant data and activities from different Computer Algebra Communities. - -SymbolicData is an - -* inter-community project that has its roots in the activities of different Computer Algebra Communities and -* aims at interlinking these activities using modern Semantic Web concepts. - -Tools and data are designed to be used both - -* on a local site for special testing and profiling purposes -* and to manage a central repository at www.symbolicdata.org. - -== License == - -GNU General Public License - -== Upstream Contact == - -* Andreas Nareike - -== Dependencies == - -== Special Update/Build Instructions == - -List patches that need to be applied and what they do - -== Changelog == - -=== database_symbolic_data-20070206 (Martin Albrecht, 8 Oct 2013) === - - * SPKG.txt added - * adapted to new git workflow - diff --git a/build/pkgs/database_symbolic_data/spkg-install b/build/pkgs/database_symbolic_data/spkg-install.in similarity index 100% rename from build/pkgs/database_symbolic_data/spkg-install rename to build/pkgs/database_symbolic_data/spkg-install.in diff --git a/build/pkgs/dateutil/SPKG.rst b/build/pkgs/dateutil/SPKG.rst new file mode 100644 index 00000000000..6bfb61a0b6b --- /dev/null +++ b/build/pkgs/dateutil/SPKG.rst @@ -0,0 +1,26 @@ +dateutil +======== + +Description +----------- + +The dateutil module provides powerful extensions to the standard +datetime module. + +License +------- + +Simplified BSD License + + +Upstream Contact +---------------- + +Author: Gustavo Niemeyer Home page: +http://labix.org/python-dateutil + +Dependencies +------------ + +- Python +- Six diff --git a/build/pkgs/dateutil/SPKG.txt b/build/pkgs/dateutil/SPKG.txt deleted file mode 100644 index c8b3d5d8779..00000000000 --- a/build/pkgs/dateutil/SPKG.txt +++ /dev/null @@ -1,27 +0,0 @@ -= dateutil = - -== Description == - -The dateutil module provides powerful extensions to the standard -datetime module. - -== License == - -Simplified BSD License - -== Upstream Contact == - -Author: Gustavo Niemeyer -Home page: http://labix.org/python-dateutil - -== Dependencies == - -* Python -* Six - -== Changelog == - -=== dateutil-2.2 (John H. Palmieri, 20 December 2013) === - - * Trac #14993: initial release. - diff --git a/build/pkgs/dateutil/checksums.ini b/build/pkgs/dateutil/checksums.ini index 47c234fc011..e08ee4550d4 100644 --- a/build/pkgs/dateutil/checksums.ini +++ b/build/pkgs/dateutil/checksums.ini @@ -1,4 +1,4 @@ tarball=python-dateutil-VERSION.tar.gz -sha1=a53ebfeecc784ea5781ba504ddf7f4ad13738213 -md5=05ffc6d2cc85a7fd93bb245807f715ef -cksum=1879066915 +sha1=bd26127e57f83a10f656b62c46524c15aeb844dd +md5=f2a1d4b680b297b367a974664ca3a4f6 +cksum=675236266 diff --git a/build/pkgs/dateutil/dependencies b/build/pkgs/dateutil/dependencies index 9a1ab0b0247..4db01013112 100644 --- a/build/pkgs/dateutil/dependencies +++ b/build/pkgs/dateutil/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) six | setuptools pip +$(PYTHON) six | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/dateutil/package-version.txt b/build/pkgs/dateutil/package-version.txt index aedc15bb0c6..dbe59006547 100644 --- a/build/pkgs/dateutil/package-version.txt +++ b/build/pkgs/dateutil/package-version.txt @@ -1 +1 @@ -2.5.3 +2.8.1 diff --git a/build/pkgs/dateutil/spkg-install b/build/pkgs/dateutil/spkg-install deleted file mode 100644 index 22327001b3d..00000000000 --- a/build/pkgs/dateutil/spkg-install +++ /dev/null @@ -1,13 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . -if [ $? -ne 0 ]; then - echo "Error installing dateutil ... exiting" - exit 1 -fi diff --git a/build/pkgs/ipykernel/spkg-install b/build/pkgs/dateutil/spkg-install.in similarity index 100% rename from build/pkgs/ipykernel/spkg-install rename to build/pkgs/dateutil/spkg-install.in diff --git a/build/pkgs/debian.txt b/build/pkgs/debian.txt index b8eed4c91cd..01a3fe61a3a 100644 --- a/build/pkgs/debian.txt +++ b/build/pkgs/debian.txt @@ -25,3 +25,5 @@ gcc # configure: error: in `/sage': # configure: error: C++ preprocessor "/lib/cpp" fails sanity check g++ +# Needed if we download some packages from a https upstream URL +ca-certificates diff --git a/build/pkgs/decorator/SPKG.rst b/build/pkgs/decorator/SPKG.rst new file mode 100644 index 00000000000..cbc1b166e6c --- /dev/null +++ b/build/pkgs/decorator/SPKG.rst @@ -0,0 +1,7 @@ +decorator +========= + +Description +----------- + +Better living through Python with decorators diff --git a/build/pkgs/decorator/SPKG.txt b/build/pkgs/decorator/SPKG.txt deleted file mode 100644 index 52aa0271d0a..00000000000 --- a/build/pkgs/decorator/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= decorator = - -== Description == - -Better living through Python with decorators diff --git a/build/pkgs/decorator/dependencies b/build/pkgs/decorator/dependencies index 2573414ce8a..15df0c4d6d8 100644 --- a/build/pkgs/decorator/dependencies +++ b/build/pkgs/decorator/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/decorator/spkg-install b/build/pkgs/decorator/spkg-install.in similarity index 100% rename from build/pkgs/decorator/spkg-install rename to build/pkgs/decorator/spkg-install.in diff --git a/build/pkgs/deformation/SPKG.rst b/build/pkgs/deformation/SPKG.rst new file mode 100644 index 00000000000..f8cc5e2a22d --- /dev/null +++ b/build/pkgs/deformation/SPKG.rst @@ -0,0 +1,19 @@ +deformation +=========== + +Description +----------- + +Deformation is a C library for counting points on hypersurfaces using +the deformation method, developed by Sebastian Pancratz. + +License +------- + +GLPv3 + + +Upstream Contact +---------------- + +- Sebastian Pancratz: sebastian.pancratz@gmail.com diff --git a/build/pkgs/deformation/SPKG.txt b/build/pkgs/deformation/SPKG.txt deleted file mode 100644 index 4381842c04f..00000000000 --- a/build/pkgs/deformation/SPKG.txt +++ /dev/null @@ -1,14 +0,0 @@ -= deformation = - -== Description == - -Deformation is a C library for counting points on hypersurfaces -using the deformation method, developed by Sebastian Pancratz. - -== License == - -GLPv3 - -== Upstream Contact == - -* Sebastian Pancratz: sebastian.pancratz@gmail.com diff --git a/build/pkgs/curl/spkg-check b/build/pkgs/deformation/spkg-check.in similarity index 100% rename from build/pkgs/curl/spkg-check rename to build/pkgs/deformation/spkg-check.in diff --git a/build/pkgs/deformation/spkg-install b/build/pkgs/deformation/spkg-install.in similarity index 100% rename from build/pkgs/deformation/spkg-install rename to build/pkgs/deformation/spkg-install.in diff --git a/build/pkgs/defusedxml/SPKG.rst b/build/pkgs/defusedxml/SPKG.rst new file mode 100644 index 00000000000..e39d1435e33 --- /dev/null +++ b/build/pkgs/defusedxml/SPKG.rst @@ -0,0 +1,31 @@ +defusedxml +========== + +Description +----------- + +defusedxml addresses vulnerabilities of XML parsers and XML libraries. + +It became a dependency of nbconvert starting with nbconvert 5.4. + +License +------- + +Python Software Foundation License (PSFL) + + +Upstream Contact +---------------- + +https://pypi.org/project/defusedxml/ + +Dependencies +------------ + +- pip + + +Special Update/Build Instructions +--------------------------------- + +None. diff --git a/build/pkgs/defusedxml/SPKG.txt b/build/pkgs/defusedxml/SPKG.txt deleted file mode 100644 index c3c8d65701d..00000000000 --- a/build/pkgs/defusedxml/SPKG.txt +++ /dev/null @@ -1,23 +0,0 @@ -= defusedxml = - -== Description == - -defusedxml addresses vulnerabilities of XML parsers and XML libraries. - -It became a dependency of nbconvert starting with nbconvert 5.4. - -== License == - -Python Software Foundation License (PSFL) - -== Upstream Contact == - -https://pypi.org/project/defusedxml/ - -== Dependencies == - -* pip - -== Special Update/Build Instructions == - -None. diff --git a/build/pkgs/defusedxml/dependencies b/build/pkgs/defusedxml/dependencies index 639891a22b2..15df0c4d6d8 100644 --- a/build/pkgs/defusedxml/dependencies +++ b/build/pkgs/defusedxml/dependencies @@ -1 +1,5 @@ -$(PYTHON) | pip \ No newline at end of file +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/defusedxml/spkg-install b/build/pkgs/defusedxml/spkg-install.in similarity index 100% rename from build/pkgs/defusedxml/spkg-install rename to build/pkgs/defusedxml/spkg-install.in diff --git a/build/pkgs/docutils/SPKG.rst b/build/pkgs/docutils/SPKG.rst new file mode 100644 index 00000000000..0b3d2f4ab66 --- /dev/null +++ b/build/pkgs/docutils/SPKG.rst @@ -0,0 +1,32 @@ +docutils +======== + +Description +----------- + +Docutils is a modular system for processing documentation into useful +formats, such as HTML, XML, and LaTeX. For input Docutils supports +reStructuredText, an easy-to-read, what-you-see-is-what-you-get +plaintext markup syntax. + +License +------- + +Modified BSD + + +Upstream Contact +---------------- + +Author: David Goodger Home Page: http://docutils.sourceforge.net/ + +Dependencies +------------ + +None + + +Special Update/Build Instructions +--------------------------------- + +None diff --git a/build/pkgs/docutils/SPKG.txt b/build/pkgs/docutils/SPKG.txt deleted file mode 100644 index 9b6eaa57634..00000000000 --- a/build/pkgs/docutils/SPKG.txt +++ /dev/null @@ -1,38 +0,0 @@ -= docutils = - -== Description == - -Docutils is a modular system for processing documentation into useful -formats, such as HTML, XML, and LaTeX. For input Docutils supports -reStructuredText, an easy-to-read, what-you-see-is-what-you-get -plaintext markup syntax. - -== License == - -Modified BSD - -== Upstream Contact == - -Author: David Goodger -Home Page: http://docutils.sourceforge.net/ - -== Dependencies == - -None - -== Special Update/Build Instructions == - -None - -== Changelog == - -=== docutils-0.7.p0 (Jeroen Demeyer, 8 August 2011) === - * Trac #11660: Change permissions of files inside /src to world-readable - -=== docutils-0.7 (Pablo De Napoli, 24 October 2010) === - * Upgrade to latest upstream stable release (trac #10166) - -=== docutils-0.5.p0 (undocumented, 25 July 2010) === - -=== docutils-0.5 (Mike Hansen, 15 September 2008) === - * Initial version. diff --git a/build/pkgs/docutils/dependencies b/build/pkgs/docutils/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/docutils/dependencies +++ b/build/pkgs/docutils/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/docutils/spkg-install b/build/pkgs/docutils/spkg-install deleted file mode 100644 index 01dd6c28914..00000000000 --- a/build/pkgs/docutils/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -#Install new version -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing docutils." - exit 1 -fi diff --git a/build/pkgs/ipywidgets/spkg-install b/build/pkgs/docutils/spkg-install.in similarity index 100% rename from build/pkgs/ipywidgets/spkg-install rename to build/pkgs/docutils/spkg-install.in diff --git a/build/pkgs/dot2tex/SPKG.rst b/build/pkgs/dot2tex/SPKG.rst new file mode 100644 index 00000000000..2ba3d5aa3a7 --- /dev/null +++ b/build/pkgs/dot2tex/SPKG.rst @@ -0,0 +1,54 @@ +dot2tex +======= + +Description +----------- + +dot2tex is a Python module, whose purpose is to give graphs generated by +Graphviz a more LaTeX friendly look and feel. This is accomplished by +converting xdot output from Graphviz to a series of PSTricks or PGF/TikZ +commands. + +See https://github.com/kjellmf/dot2tex/ + +License +------- + +- MIT + + +Upstream Contact +---------------- + +- Kjell Magne Fauske, km@fauskes.net + +Dependencies +------------ + +graphviz (www.graphviz.org) should be installed and in the path (for +example via the graphviz spkg). + +preview, a LaTeX package for extracting parts of a document. + +Self-tests dependencies: + +- graphviz +- texlive-latex-base +- texlive-pictures +- texlive-pstricks + +Patches +------- + +- remove_test_semicolon.patch: + + Remove the failing semicolon test for the open dot2tex + issue #5 - https://github.com/kjellmf/dot2tex/issues/5 + + +Special Update/Build Instructions +--------------------------------- + +Make sure corresponding optional doctests still pass: + + sage -t --long --optional=dot2tex,graphviz,sage src/ diff --git a/build/pkgs/dot2tex/SPKG.txt b/build/pkgs/dot2tex/SPKG.txt deleted file mode 100644 index 4cbf60564e5..00000000000 --- a/build/pkgs/dot2tex/SPKG.txt +++ /dev/null @@ -1,42 +0,0 @@ -= dot2tex = - -== Description == - -dot2tex is a Python module, whose purpose is to give graphs generated -by Graphviz a more LaTeX friendly look and feel. This is accomplished -by converting xdot output from Graphviz to a series of PSTricks or -PGF/TikZ commands. - -See https://github.com/kjellmf/dot2tex/ - -== License == - * MIT - -== Upstream Contact == - * Kjell Magne Fauske, km@fauskes.net - -== Dependencies == - -graphviz (www.graphviz.org) should be installed and in the path (for -example via the graphviz spkg). - -preview, a LaTeX package for extracting parts of a document. - -Self-tests dependencies: - * graphviz - * texlive-latex-base - * texlive-pictures - * texlive-pstricks - -== Patches == - - * remove_test_semicolon.patch: - Remove the failing semicolon test for the open dot2tex - issue #5 - https://github.com/kjellmf/dot2tex/issues/5 - -== Special Update/Build Instructions == - -Make sure corresponding optional doctests still pass: - - sage -t --long --optional=dot2tex,graphviz,sage src/ - diff --git a/build/pkgs/dot2tex/dependencies b/build/pkgs/dot2tex/dependencies index 5e8075520b4..e88742b6c74 100644 --- a/build/pkgs/dot2tex/dependencies +++ b/build/pkgs/dot2tex/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip pyparsing +$(PYTHON) | $(PYTHON_TOOLCHAIN) pyparsing ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/dot2tex/spkg-check b/build/pkgs/dot2tex/spkg-check deleted file mode 100644 index ed41c595ec4..00000000000 --- a/build/pkgs/dot2tex/spkg-check +++ /dev/null @@ -1,25 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src/tests/ - -# Currently (as of version 2.9.0), there is one Python test file: -# - test_dot2tex.py - -passed=true -for file in test_*.py; do - echo "############################################################" - echo "spkg-check: Running $file..." - echo "############################################################" - sage-python23 $file || passed=false -done -echo -if $passed; then - echo "dot2tex's test suite passed successfully." -else - echo >&2 "Error(s) running dot2tex's test suite." - exit 1 -fi diff --git a/build/pkgs/dot2tex/spkg-check.in b/build/pkgs/dot2tex/spkg-check.in new file mode 100644 index 00000000000..099d96a3587 --- /dev/null +++ b/build/pkgs/dot2tex/spkg-check.in @@ -0,0 +1,16 @@ +cd src/tests/ + +# Currently (as of version 2.9.0), there is one Python test file: +# - test_dot2tex.py + +passed=true +for file in test_*.py; do + echo "############################################################" + echo "spkg-check: Running $file..." + echo "############################################################" + sage-python23 $file || passed=false +done +echo +if ! $passed; then + exit 1 +fi diff --git a/build/pkgs/dot2tex/spkg-install b/build/pkgs/dot2tex/spkg-install deleted file mode 100644 index 2a379a2c31f..00000000000 --- a/build/pkgs/dot2tex/spkg-install +++ /dev/null @@ -1,22 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -# Check that Graphviz is installed by running dot2tex from the source -# directory. -sage-python23 -c "import sys; from dot2tex.dotparsing import find_graphviz; sys.exit(0 if find_graphviz() else 1)" -if [ $? -ne 0 ]; then - echo >&2 "Error: dot2tex requires Graphviz but Graphviz is not installed. You can download Graphviz at https://www.graphviz.org/download/" - exit 1 -fi - -sdh_pip_install . - -if [ $? -ne 0 ]; then - exit 1 -fi - diff --git a/build/pkgs/dot2tex/spkg-install.in b/build/pkgs/dot2tex/spkg-install.in new file mode 100644 index 00000000000..8ef346eb3b3 --- /dev/null +++ b/build/pkgs/dot2tex/spkg-install.in @@ -0,0 +1,11 @@ +cd src + +# Check that Graphviz is installed by running dot2tex from the source +# directory. +sage-python23 -c "import sys; from dot2tex.dotparsing import find_graphviz; sys.exit(0 if find_graphviz() else 1)" +if [ $? -ne 0 ]; then + echo >&2 "Error: dot2tex requires Graphviz but Graphviz is not installed. You can download Graphviz at https://www.graphviz.org/download/" + exit 1 +fi + +sdh_pip_install . diff --git a/build/pkgs/e_antic/SPKG.rst b/build/pkgs/e_antic/SPKG.rst new file mode 100644 index 00000000000..e6e3ffc29c3 --- /dev/null +++ b/build/pkgs/e_antic/SPKG.rst @@ -0,0 +1,29 @@ + +e-antic +======= + +Description +----------- + +e-antic is a C library for exact computations with real embedded number +field maintained by Vincent Delecroix. + +Website: https://github.com/videlec/e-antic + +License +------- + +e-antic is licensed GPL v3. + + +Upstream Contact +---------------- + +- https://github.com/videlec/e-antic + +Dependencies +------------ + +- GMP/MPIR +- FLINT +- ARB diff --git a/build/pkgs/e_antic/SPKG.txt b/build/pkgs/e_antic/SPKG.txt deleted file mode 100644 index e13b4c97c94..00000000000 --- a/build/pkgs/e_antic/SPKG.txt +++ /dev/null @@ -1,22 +0,0 @@ -= e-antic = - -== Description == - -e-antic is a C library for exact computations with real embedded number field -maintained by Vincent Delecroix. - -Website: https://github.com/videlec/e-antic - -== License == - -e-antic is licensed GPL v3. - -== Upstream Contact == - - * https://github.com/videlec/e-antic - -== Dependencies == - - * GMP/MPIR - * FLINT - * ARB diff --git a/build/pkgs/e_antic/checksums.ini b/build/pkgs/e_antic/checksums.ini index e5a4ec4724f..4ef18e1d257 100644 --- a/build/pkgs/e_antic/checksums.ini +++ b/build/pkgs/e_antic/checksums.ini @@ -1,4 +1,5 @@ tarball=e-antic-VERSION.tar.gz -sha1=2a443ab9babf5373a434acd90923a793cd950f10 -md5=c4ffc38b285cd6e81c555e48dbbb529e -cksum=1444226844 +sha1=e3f54ab708e34c65d5704a560caba3f89a065785 +md5=7af45afdb754bb87b11abf08cc876c26 +cksum=1677181853 +upstream_url=https://www.labri.fr/perso/vdelecro/e-antic/e-antic-VERSION.tar.gz diff --git a/build/pkgs/e_antic/package-version.txt b/build/pkgs/e_antic/package-version.txt index b1e80bb2480..11808190d4b 100644 --- a/build/pkgs/e_antic/package-version.txt +++ b/build/pkgs/e_antic/package-version.txt @@ -1 +1 @@ -0.1.3 +0.1.7 diff --git a/build/pkgs/e_antic/spkg-check b/build/pkgs/e_antic/spkg-check deleted file mode 100644 index d2e30d2e90c..00000000000 --- a/build/pkgs/e_antic/spkg-check +++ /dev/null @@ -1,8 +0,0 @@ -############################################################################### -# -# e-antic Sage check script -# -############################################################################### - -cd src -sdh_make check diff --git a/build/pkgs/e_antic/spkg-check.in b/build/pkgs/e_antic/spkg-check.in new file mode 100644 index 00000000000..3e3d7dc286d --- /dev/null +++ b/build/pkgs/e_antic/spkg-check.in @@ -0,0 +1,8 @@ +############################################################################### +# +# e-antic Sage check script +# +############################################################################### + +cd src +sdh_make_check diff --git a/build/pkgs/e_antic/spkg-install b/build/pkgs/e_antic/spkg-install.in similarity index 100% rename from build/pkgs/e_antic/spkg-install rename to build/pkgs/e_antic/spkg-install.in diff --git a/build/pkgs/ecl/SPKG.rst b/build/pkgs/ecl/SPKG.rst new file mode 100644 index 00000000000..646abfc6ecf --- /dev/null +++ b/build/pkgs/ecl/SPKG.rst @@ -0,0 +1,70 @@ +ECL +=== + +Description +----------- + +ECL is an implementation of the Common Lisp language as defined by the +ANSI X3J13 specification. The most relevant features: + +- A bytecodes compiler and interpreter. +- Compiles Lisp also with any C/C++ compiler. +- It can build standalone executables and libraries. +- ASDF, Sockets, Gray streams, MOP, and other useful components. +- Extremely portable. +- A reasonable license. + +ECL supports the operating systems Linux, FreeBSD, NetBSD, OpenBSD, +Solaris and Windows, running on top of the Intel, Sparc, Alpha and +PowerPC processors. Porting to other architectures should be rather +easy. + +Website: http://ecls.sourceforge.net/ + +License +------- + +- LGPL V2+ or compatible - for details see + + http://ecls.sourceforge.net/license.html + + +Upstream Contact +---------------- + +- the ECL mailing list - see http://ecls.sourceforge.net/resources.html + +Dependencies +------------ + +- mpir +- boehm_gc + + +Special Update/Build Instructions +--------------------------------- + +- As autotools need to be run after most of the patches are applied, + we do all the patching in spkg-source. + +- Deleting the following directories saves space: without doing + this, the tarball can grow from under 3 megabytes to more than 7 + megabytes. Deleting these files is done automatically by the + ``spkg-src`` script. + +- The directory msvc +- The directory src/gc-unstable +- The directory src/gmp +- The directory src/libffi +- Note: for the time being, ECL is built single threaded library as it + seems to interact badly with the pexpect interface and Sage's signal + handling when built multithreaded. + +- Do NOT quote SAGE_LOCAL when setting CPPFLAGS and/or LDFLAGS, + in spkg-install as this caused the build to break. See + http://trac.sagemath.org/sage_trac/ticket/10187#comment:117 + +- TODO: Add the ECL test suite, and an spkg-check file to run it. +- TODO: Make ECL use Sage's Boehm GC on MacOS X as well (but perhaps + put some changes from ECL's into Sage's Boehm GC), then remove + the src/src/gc directory, too. diff --git a/build/pkgs/ecl/SPKG.txt b/build/pkgs/ecl/SPKG.txt deleted file mode 100644 index be945bb2319..00000000000 --- a/build/pkgs/ecl/SPKG.txt +++ /dev/null @@ -1,56 +0,0 @@ -= ECL = - -== Description == - -ECL is an implementation of the Common Lisp language as defined by the -ANSI X3J13 specification. The most relevant features: - - * A bytecodes compiler and interpreter. - * Compiles Lisp also with any C/C++ compiler. - * It can build standalone executables and libraries. - * ASDF, Sockets, Gray streams, MOP, and other useful components. - * Extremely portable. - * A reasonable license. - -ECL supports the operating systems Linux, FreeBSD, NetBSD, OpenBSD, -Solaris and Windows, running on top of the Intel, Sparc, Alpha and -PowerPC processors. Porting to other architectures should be rather -easy. - -Website: http://ecls.sourceforge.net/ - -== License == - - * LGPL V2+ or compatible - for details see - http://ecls.sourceforge.net/license.html - -== Upstream Contact == - - * the ECL mailing list - see http://ecls.sourceforge.net/resources.html - -== Dependencies == - - * mpir - * boehm_gc - -== Special Update/Build Instructions == - * As autotools need to be run after most of the patches are applied, - we do all the patching in spkg-source. - * Deleting the following directories saves space: without doing - this, the tarball can grow from under 3 megabytes to more than 7 - megabytes. Deleting these files is done automatically by the - `spkg-src` script. - - The directory msvc - - The directory src/gc-unstable - - The directory src/gmp - - The directory src/libffi - * Note: for the time being, ECL is built single threaded library as it - seems to interact badly with the pexpect interface and Sage's signal - handling when built multithreaded. - * Do NOT quote SAGE_LOCAL when setting CPPFLAGS and/or LDFLAGS, - in spkg-install as this caused the build to break. See - http://trac.sagemath.org/sage_trac/ticket/10187#comment:117 - * TODO: Add the ECL test suite, and an spkg-check file to run it. - * TODO: Make ECL use Sage's Boehm GC on MacOS X as well (but perhaps - put some changes from ECL's into Sage's Boehm GC), then remove - the src/src/gc directory, too. diff --git a/build/pkgs/ecl/checksums.ini b/build/pkgs/ecl/checksums.ini index 5e56dd5cdda..9d0155a893c 100644 --- a/build/pkgs/ecl/checksums.ini +++ b/build/pkgs/ecl/checksums.ini @@ -1,4 +1,5 @@ -tarball=ecl-VERSION.tar.bz2 -sha1=66f99db852b23660668e8c1d5304ec842558d1a2 -md5=14740f65bb971127bb10caaff855361e -cksum=1956945543 +tarball=ecl-VERSION.tgz +sha1=96daa970ae31821c44d9e8ee344f62106d6d79a0 +md5=93ba832d2ec609d49132a7076e5c14ae +cksum=3753868648 +upstream_url=https://common-lisp.net/project/ecl/static/files/release/ecl-VERSION.tgz diff --git a/build/pkgs/ecl/package-version.txt b/build/pkgs/ecl/package-version.txt index 0be84e06f77..809c8bfa98f 100644 --- a/build/pkgs/ecl/package-version.txt +++ b/build/pkgs/ecl/package-version.txt @@ -1 +1 @@ -16.1.2.p5 +20.4.24.p1 diff --git a/build/pkgs/ecl/patches/0001-unicode-fix-ecl_string_case-for-non-ascii-characters.patch b/build/pkgs/ecl/patches/0001-unicode-fix-ecl_string_case-for-non-ascii-characters.patch new file mode 100644 index 00000000000..56befbe9c1f --- /dev/null +++ b/build/pkgs/ecl/patches/0001-unicode-fix-ecl_string_case-for-non-ascii-characters.patch @@ -0,0 +1,47 @@ +From 29239fa1b38f55b9a263b9582a43bae18cb5980d Mon Sep 17 00:00:00 2001 +From: Marius Gerbershagen +Date: Wed, 6 May 2020 20:58:20 +0200 +Subject: [PATCH 1/3] unicode: fix ecl_string_case for non-ascii characters + +Problem reported and fix provided by Vladimir Sedach on the ecl-devel +mailing list. +--- + src/c/character.d | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/src/c/character.d b/src/c/character.d +index a69b6e4b..c3ef328a 100644 +--- a/src/c/character.d ++++ b/src/c/character.d +@@ -99,6 +99,7 @@ cl_both_case_p(cl_object c) + int + ecl_string_case(cl_object s) + { ++ /* Returns 1 if string is all uppercase, -1 if all lowercase, and 0 if mixed case */ + int upcase; + cl_index i; + ecl_base_char *text; +@@ -106,16 +107,16 @@ ecl_string_case(cl_object s) + switch (ecl_t_of(s)) { + #ifdef ECL_UNICODE + case t_string: +- s = si_coerce_to_base_string(s); + #endif + case t_base_string: +- text = (ecl_base_char*)s->base_string.self; + for (i = 0, upcase = 0; i < s->base_string.dim; i++) { +- if (ecl_upper_case_p(text[i])) { ++ ecl_character c = ecl_char(s, i); ++ ++ if (ecl_upper_case_p(c)) { + if (upcase < 0) + return 0; + upcase = +1; +- } else if (ecl_lower_case_p(text[i])) { ++ } else if (ecl_lower_case_p(c)) { + if (upcase > 0) + return 0; + upcase = -1; +-- +2.26.2 + diff --git a/build/pkgs/ecl/patches/0002-cosmetic-fix-some-compiler-warnings.patch b/build/pkgs/ecl/patches/0002-cosmetic-fix-some-compiler-warnings.patch new file mode 100644 index 00000000000..9ebcb224707 --- /dev/null +++ b/build/pkgs/ecl/patches/0002-cosmetic-fix-some-compiler-warnings.patch @@ -0,0 +1,941 @@ +From 1a835fa016fc65e7d19beaa416afc574bd79f7e6 Mon Sep 17 00:00:00 2001 +From: Marius Gerbershagen +Date: Sun, 26 Apr 2020 18:45:40 +0200 +Subject: [PATCH 2/3] cosmetic: fix some compiler warnings + +--- + contrib/bytecmp/bytecmp.lsp | 2 +- + src/c/alloc_2.d | 4 ++++ + src/c/clos/cache.d | 4 ++-- + src/c/compiler.d | 12 ++++++------ + src/c/disassembler.d | 2 -- + src/c/ffi/mmap.d | 2 +- + src/c/interpreter.d | 1 - + src/c/number.d | 2 +- + src/c/numbers/number_equalp.d | 1 - + src/c/pathname.d | 2 ++ + src/c/printer/float_to_digits.d | 2 -- + src/c/printer/write_symbol.d | 22 ++++++++++++---------- + src/c/printer/write_ugly.d | 2 +- + src/c/sequence.d | 2 +- + src/c/threads/queue.d | 2 ++ + src/c/unixfsys.d | 4 +++- + src/clos/combin.lsp | 1 + + src/clos/generic.lsp | 2 +- + src/clos/kernel.lsp | 4 ++-- + src/clos/method.lsp | 3 +++ + src/clos/print.lsp | 3 +++ + src/clos/std-accessors.lsp | 1 + + src/cmp/cmpc-machine.lsp | 4 ---- + src/cmp/cmpenv-declaim.lsp | 1 + + src/cmp/cmpenv-declare.lsp | 6 +++--- + src/cmp/cmpenv-fun.lsp | 2 +- + src/cmp/cmpinline.lsp | 2 +- + src/cmp/cmplam.lsp | 1 + + src/cmp/cmplet.lsp | 2 +- + src/cmp/cmploc.lsp | 1 + + src/cmp/cmpmain.lsp | 4 ++++ + src/cmp/cmpmulti.lsp | 2 +- + src/cmp/cmpname.lsp | 2 +- + src/cmp/cmpnum.lsp | 2 ++ + src/cmp/cmpopt-printer.lsp | 6 +++--- + src/cmp/cmpopt-sequence.lsp | 3 +++ + src/cmp/cmpopt.lsp | 4 ++-- + src/cmp/cmpprop.lsp | 2 ++ + src/cmp/cmptop.lsp | 1 + + src/cmp/cmptype-assert.lsp | 6 +++--- + src/cmp/cmpvar.lsp | 15 ++++++++------- + src/cmp/proclamations.lsp | 2 +- + src/h/bytecodes.h | 1 + + src/lsp/defstruct.lsp | 4 ++-- + src/lsp/ffi.lsp | 6 ++++-- + src/lsp/setf.lsp | 1 - + 46 files changed, 94 insertions(+), 66 deletions(-) + +diff --git a/contrib/bytecmp/bytecmp.lsp b/contrib/bytecmp/bytecmp.lsp +index c71b7d8c..1a233b4d 100755 +--- a/contrib/bytecmp/bytecmp.lsp ++++ b/contrib/bytecmp/bytecmp.lsp +@@ -81,7 +81,7 @@ + (defun bc-compile-file-pathname (name &key (output-file name) (type :fasl) + verbose print c-file h-file data-file + shared-data-file system-p load external-format) +- (declare (ignore load c-file h-file data-file shared-data-file system-p verbose print)) ++ (declare (ignore load c-file h-file data-file shared-data-file system-p verbose print external-format)) + (let ((extension "fasc")) + (case type + ((:fasl :fas) (setf extension "fasc")) +diff --git a/src/c/alloc_2.d b/src/c/alloc_2.d +index dbeb1ead..a2223334 100644 +--- a/src/c/alloc_2.d ++++ b/src/c/alloc_2.d +@@ -191,11 +191,13 @@ static struct ecl_type_information { + size_t t; + } type_info[t_end]; + ++#ifdef GBC_BOEHM_PRECISE + static void + error_wrong_tag(cl_type t) + { + ecl_internal_error("Collector called with invalid tag number."); + } ++#endif + + cl_index + ecl_object_byte_size(cl_type t) +@@ -764,6 +766,7 @@ extern void (*GC_push_other_roots)(); + static void (*old_GC_push_other_roots)(); + static void stacks_scanner(); + ++#ifdef GBC_BOEHM_PRECISE + static cl_index + to_bitmap(void *x, void *y) + { +@@ -773,6 +776,7 @@ to_bitmap(void *x, void *y) + n /= sizeof(void*); + return 1 << n; + } ++#endif + + void + init_alloc(void) +diff --git a/src/c/clos/cache.d b/src/c/clos/cache.d +index 0f66f2db..9f8a59fd 100644 +--- a/src/c/clos/cache.d ++++ b/src/c/clos/cache.d +@@ -36,6 +36,7 @@ empty_cache(ecl_cache_ptr cache) + #endif + } + ++#ifndef ECL_THREADS + static void + clear_one_from_cache(ecl_cache_ptr cache, cl_object target) + { +@@ -51,8 +52,7 @@ clear_one_from_cache(ecl_cache_ptr cache, cl_object target) + } + } + } +- +-#ifdef ECL_THREADS ++#else + static void + clear_list_from_cache(ecl_cache_ptr cache) + { +diff --git a/src/c/compiler.d b/src/c/compiler.d +index e67cd2b2..a4d02806 100644 +--- a/src/c/compiler.d ++++ b/src/c/compiler.d +@@ -847,7 +847,7 @@ c_block(cl_env_ptr env, cl_object body, int old_flags) { + struct cl_compiler_env old_env; + cl_object name = pop(&body); + cl_object block_record; +- cl_index labelz, pc, loc, constants; ++ cl_index labelz, pc, constants; + int flags; + + if (!ECL_SYMBOLP(name)) +@@ -858,7 +858,7 @@ c_block(cl_env_ptr env, cl_object body, int old_flags) { + pc = current_pc(env); + + flags = maybe_values_or_reg0(old_flags); +- loc = c_register_block(env, name); ++ c_register_block(env, name); + block_record = ECL_CONS_CAR(env->c_env->variables); + if (Null(name)) { + asm_op(env, OP_DO); +@@ -1063,7 +1063,7 @@ c_case(cl_env_ptr env, cl_object clause, int flags) { + + static int + c_catch(cl_env_ptr env, cl_object args, int flags) { +- cl_index labelz, loc; ++ cl_index labelz; + cl_object old_env; + + /* Compile evaluation of tag */ +@@ -1071,7 +1071,7 @@ c_catch(cl_env_ptr env, cl_object args, int flags) { + + /* Compile binding of tag */ + old_env = env->c_env->variables; +- loc = c_register_block(env, ecl_make_fixnum(0)); ++ c_register_block(env, ecl_make_fixnum(0)); + asm_op(env, OP_CATCH); + + /* Compile jump point */ +@@ -3039,7 +3039,7 @@ c_default(cl_env_ptr env, cl_object var, cl_object stmt, cl_object flag, cl_obje + cl_object + ecl_make_lambda(cl_env_ptr env, cl_object name, cl_object lambda) { + cl_object reqs, opts, rest, key, keys, auxs, allow_other_keys; +- cl_object specials, doc, decl, body, output; ++ cl_object specials, decl, body, output; + cl_index handle; + struct cl_compiler_env *old_c_env, new_c_env; + +@@ -3057,7 +3057,7 @@ ecl_make_lambda(cl_env_ptr env, cl_object name, cl_object lambda) { + keys = env->values[4]; + allow_other_keys = env->values[5]; + auxs = env->values[6]; +- doc = env->values[7]; ++ /* doc = env->values[7]; unused */; + specials = env->values[8]; + decl = env->values[9]; + body = env->values[10]; +diff --git a/src/c/disassembler.d b/src/c/disassembler.d +index b134c56c..46c78259 100644 +--- a/src/c/disassembler.d ++++ b/src/c/disassembler.d +@@ -53,13 +53,11 @@ print_oparg_arg(const char *s, cl_fixnum n, cl_object x) { + static void + disassemble_lambda(cl_object bytecodes) { + const cl_env_ptr env = ecl_process_env(); +- cl_object *data; + cl_opcode *vector; + + ecl_bds_bind(env, @'*print-pretty*', ECL_NIL); + + /* Print required arguments */ +- data = bytecodes->bytecodes.data->vector.self.t; + cl_print(1,bytecodes->bytecodes.data); + + /* Name of LAMBDA */ +diff --git a/src/c/ffi/mmap.d b/src/c/ffi/mmap.d +index ee1f49b6..23ed9e4b 100644 +--- a/src/c/ffi/mmap.d ++++ b/src/c/ffi/mmap.d +@@ -53,7 +53,7 @@ + @':element-type', element_type, + @':if-exists', if_exists, + @':if-does-not-exist', if_does_not_exist, +- @':external-format', @':default', ++ @':external-format', external_format, + @':cstream', ECL_NIL); + fd = ecl_to_int(si_file_stream_fd(stream)); + if (Null(length)) +diff --git a/src/c/interpreter.d b/src/c/interpreter.d +index 22b78af6..34fd2a1c 100644 +--- a/src/c/interpreter.d ++++ b/src/c/interpreter.d +@@ -314,7 +314,6 @@ ecl_interpret(cl_object frame, cl_object env, cl_object bytecodes) + frame_aux.stack = frame_aux.base = 0; + frame_aux.size = 0; + frame_aux.env = the_env; +- BEGIN: + BEGIN_SWITCH { + CASE(OP_NOP); { + reg0 = ECL_NIL; +diff --git a/src/c/number.d b/src/c/number.d +index ba28cfe5..c6511af6 100644 +--- a/src/c/number.d ++++ b/src/c/number.d +@@ -611,7 +611,7 @@ si_complex_float(cl_object r, cl_object i) + { + cl_type tr = ecl_t_of(r); + cl_type ti = ecl_t_of(i); +- cl_object result; ++ cl_object result = OBJNULL; + switch (tr) { + case t_singlefloat: + if (ti != tr) { ecl_type_error(@'si::complex-float',"imag part", i, @'single-float'); } +diff --git a/src/c/numbers/number_equalp.d b/src/c/numbers/number_equalp.d +index 678f509e..2add2691 100644 +--- a/src/c/numbers/number_equalp.d ++++ b/src/c/numbers/number_equalp.d +@@ -36,7 +36,6 @@ + int + ecl_number_equalp(cl_object x, cl_object y) + { +- double dx; + /* INV: (= fixnum bignum) => 0 */ + /* INV: (= fixnum ratio) => 0 */ + /* INV: (= bignum ratio) => 0 */ +diff --git a/src/c/pathname.d b/src/c/pathname.d +index 3617ce93..0507e606 100644 +--- a/src/c/pathname.d ++++ b/src/c/pathname.d +@@ -659,7 +659,9 @@ ecl_parse_namestring(cl_object s, cl_index start, cl_index end, cl_index *ep, + if (!ecl_stringp(device)) { + return ECL_NIL; + } ++#if defined(ECL_MS_WINDOWS_HOST) + maybe_parse_host: ++#endif + /* Files have no effective device. */ + if (@string-equal(2, device, @':file') == ECL_T) + device = ECL_NIL; +diff --git a/src/c/printer/float_to_digits.d b/src/c/printer/float_to_digits.d +index 2d9fdd8a..384dceb6 100644 +--- a/src/c/printer/float_to_digits.d ++++ b/src/c/printer/float_to_digits.d +@@ -161,10 +161,8 @@ generate(cl_object digits, float_approx *approx) + static void + change_precision(float_approx *approx, cl_object position, cl_object relativep) + { +- cl_fixnum pos; + if (Null(position)) + return; +- pos = ecl_fixnum(position); + if (!Null(relativep)) { + cl_object k = ecl_make_fixnum(0); + cl_object l = ecl_make_fixnum(1); +diff --git a/src/c/printer/write_symbol.d b/src/c/printer/write_symbol.d +index 1e1d4e2b..a39bab97 100644 +--- a/src/c/printer/write_symbol.d ++++ b/src/c/printer/write_symbol.d +@@ -102,13 +102,15 @@ needs_to_be_escaped(cl_object s, cl_object readtable, cl_object print_case) + return 0; + } + +-#define buffer_write_char(c, buffer, stream, buffer_ndx, buffer_size) \ +- ecl_char_set(buffer, buffer_ndx++, c); \ +- if (buffer_ndx >= buffer_size) { \ +- si_fill_pointer_set(buffer, ecl_make_fixnum(buffer_size)); \ +- si_do_write_sequence(buffer, stream, ecl_make_fixnum(0), ECL_NIL);\ +- buffer_ndx = 0; \ ++static inline void ++buffer_write_char(char c, cl_object buffer, cl_object stream, cl_index *buffer_ndx, cl_index buffer_size) { ++ ecl_char_set(buffer, (*buffer_ndx)++, c); ++ if (*buffer_ndx >= buffer_size) { ++ si_fill_pointer_set(buffer, ecl_make_fixnum(buffer_size)); ++ si_do_write_sequence(buffer, stream, ecl_make_fixnum(0), ECL_NIL); ++ *buffer_ndx = 0; + } ++} + + static void + write_symbol_string(cl_object s, int action, cl_object print_case, +@@ -124,13 +126,13 @@ write_symbol_string(cl_object s, int action, cl_object print_case, + cl_index buffer_size = ecl_fixnum(cl_array_total_size(buffer)); + cl_index buffer_ndx = 0; + if (escape) +- buffer_write_char('|', buffer, stream, buffer_ndx, buffer_size); ++ buffer_write_char('|', buffer, stream, &buffer_ndx, buffer_size); + capitalize = 1; + for (i = 0; i < s->base_string.fillp; i++) { + ecl_character c = ecl_char(s, i); + if (escape) { + if (c == '|' || c == '\\') { +- buffer_write_char('\\', buffer, stream, buffer_ndx, buffer_size); ++ buffer_write_char('\\', buffer, stream, &buffer_ndx, buffer_size); + } + } else if (action != ecl_case_preserve) { + if (ecl_upper_case_p(c)) { +@@ -155,10 +157,10 @@ write_symbol_string(cl_object s, int action, cl_object print_case, + capitalize = !ecl_alphanumericp(c); + } + } +- buffer_write_char(c, buffer, stream, buffer_ndx, buffer_size); ++ buffer_write_char(c, buffer, stream, &buffer_ndx, buffer_size); + } + if (escape) +- buffer_write_char('|', buffer, stream, buffer_ndx, buffer_size); ++ buffer_write_char('|', buffer, stream, &buffer_ndx, buffer_size); + si_fill_pointer_set(buffer, ecl_make_fixnum(buffer_ndx)); + si_do_write_sequence(buffer, stream, ecl_make_fixnum(0), ECL_NIL); + si_put_buffer_string(buffer); +diff --git a/src/c/printer/write_ugly.d b/src/c/printer/write_ugly.d +index ae07a388..d99672ee 100644 +--- a/src/c/printer/write_ugly.d ++++ b/src/c/printer/write_ugly.d +@@ -120,7 +120,7 @@ write_float(cl_object f, cl_object stream) + static void /* XXX: do not cons new floats here! */ + write_complex_float(cl_object f, cl_object stream) + { +- cl_object real, imag; ++ cl_object real = OBJNULL, imag = OBJNULL; + switch (ecl_t_of(f)) { + case t_csfloat: + real = ecl_make_single_float(crealf(ecl_csfloat(f))); +diff --git a/src/c/sequence.d b/src/c/sequence.d +index a39d2760..fa77b7c9 100644 +--- a/src/c/sequence.d ++++ b/src/c/sequence.d +@@ -189,7 +189,7 @@ ecl_copy_seq(cl_object sequence) + return ecl_subseq(sequence, 0, MOST_POSITIVE_FIXNUM); + } + +-@(defun subseq (sequence start &optional end &aux x) ++@(defun subseq (sequence start &optional end) + cl_index_pair p; + @ + p = ecl_sequence_start_end(@[subseq], sequence, start, end); +diff --git a/src/c/threads/queue.d b/src/c/threads/queue.d +index 727e5c32..81ee4337 100755 +--- a/src/c/threads/queue.d ++++ b/src/c/threads/queue.d +@@ -84,6 +84,7 @@ wait_queue_delete(cl_env_ptr the_env, cl_object q, cl_object item) + * THREAD SCHEDULER & WAITING + */ + ++#if !defined(HAVE_SIGPROCMASK) + static cl_object + bignum_set_time(cl_object bignum, struct ecl_timeval *time) + { +@@ -194,6 +195,7 @@ ecl_wait_on_timed(cl_env_ptr env, cl_object (*condition)(cl_env_ptr, cl_object), + ecl_bds_unwind1(the_env); + return output; + } ++#endif + + /********************************************************************** + * BLOCKING WAIT QUEUE ALGORITHM +diff --git a/src/c/unixfsys.d b/src/c/unixfsys.d +index 9e4ecbab..3d169db3 100644 +--- a/src/c/unixfsys.d ++++ b/src/c/unixfsys.d +@@ -351,7 +351,7 @@ file_truename(cl_object pathname, cl_object filename, int flags) + * the other hand, if the link is broken – return file + * truename "as is". */ + struct stat filestatus; +- if (safe_stat(filename->base_string.self, &filestatus) < 0) { ++ if (safe_stat((char*) filename->base_string.self, &filestatus) < 0) { + @(return pathname kind); + } + filename = si_readlink(filename); +@@ -560,7 +560,9 @@ ecl_file_len(int f) + } + #endif + } ++#if defined(ECL_MS_WINDOWS_HOST) + FAILURE_CLOBBER: ++#endif + ecl_enable_interrupts(); + { + cl_object c_error = _ecl_strerror(errno); +diff --git a/src/clos/combin.lsp b/src/clos/combin.lsp +index b4cead1d..fccb5e4d 100644 +--- a/src/clos/combin.lsp ++++ b/src/clos/combin.lsp +@@ -211,6 +211,7 @@ + o)) + + (defun find-method-combination (gf method-combination-type-name method-combination-options) ++ (declare (ignore gf)) + (make-method-combination method-combination-type-name + (search-method-combination method-combination-type-name) + method-combination-options +diff --git a/src/clos/generic.lsp b/src/clos/generic.lsp +index dba4b531..9993ebf4 100644 +--- a/src/clos/generic.lsp ++++ b/src/clos/generic.lsp +@@ -181,7 +181,7 @@ + + (defmethod shared-initialize ((gfun standard-generic-function) slot-names + &rest initargs) +- (declare (ignore initargs slot-names)) ++ (declare (ignore slot-names)) + (call-next-method) + (when (generic-function-methods gfun) + (compute-g-f-spec-list gfun)) +diff --git a/src/clos/kernel.lsp b/src/clos/kernel.lsp +index a0b9b2ed..31594f9d 100644 +--- a/src/clos/kernel.lsp ++++ b/src/clos/kernel.lsp +@@ -355,7 +355,7 @@ + (with-early-accessors (+standard-generic-function-slots+ + +eql-specializer-slots+ + +standard-method-slots+) +- (flet ((nupdate-spec-how-list (spec-how-list specializers gf) ++ (flet ((nupdate-spec-how-list (spec-how-list specializers) + ;; update the spec-how of the gfun + ;; computing the or of the previous value and the new one + (setf spec-how-list (or spec-how-list +@@ -379,7 +379,7 @@ + (a-p-o (generic-function-argument-precedence-order gf))) + (dolist (method (generic-function-methods gf)) + (setf spec-how-list +- (nupdate-spec-how-list spec-how-list (method-specializers method) gf))) ++ (nupdate-spec-how-list spec-how-list (method-specializers method)))) + (setf (generic-function-spec-list gf) + (loop for type in spec-how-list + for i from 0 +diff --git a/src/clos/method.lsp b/src/clos/method.lsp +index 3e607b5e..2866e0a8 100644 +--- a/src/clos/method.lsp ++++ b/src/clos/method.lsp +@@ -102,6 +102,7 @@ + (when (eq (first method-lambda) 'lambda) + (multiple-value-bind (declarations body documentation) + (si::find-declarations (cddr method-lambda)) ++ (declare (ignore documentation)) + (let (block) + (when (and (null (rest body)) + (listp (setf block (first body))) +@@ -177,6 +178,7 @@ + (values method-lambda declarations documentation)))) + + (defun make-method-lambda (gf method method-lambda env) ++ (declare (ignore method gf)) + (multiple-value-bind (call-next-method-p next-method-p-p in-closure-p) + (walk-method-lambda method-lambda env) + (values `(lambda (.combined-method-args. *next-methods*) +@@ -190,6 +192,7 @@ + (defun add-call-next-method-closure (method-lambda) + (multiple-value-bind (declarations real-body documentation) + (si::find-declarations (cddr method-lambda)) ++ (declare (ignore documentation)) + `(lambda ,(second method-lambda) + ,@declarations + (let* ((.closed-combined-method-args. +diff --git a/src/clos/print.lsp b/src/clos/print.lsp +index ea392516..afa7d83b 100644 +--- a/src/clos/print.lsp ++++ b/src/clos/print.lsp +@@ -112,12 +112,15 @@ + (no-make-load-form object))))) + + (defmethod make-load-form ((object standard-object) &optional environment) ++ (declare (ignore environment)) + (no-make-load-form object)) + + (defmethod make-load-form ((object structure-object) &optional environment) ++ (declare (ignore environment)) + (no-make-load-form object)) + + (defmethod make-load-form ((object condition) &optional environment) ++ (declare (ignore environment)) + (no-make-load-form object)) + + (defun no-make-load-form (object) +diff --git a/src/clos/std-accessors.lsp b/src/clos/std-accessors.lsp +index fd8431f0..ff83156e 100644 +--- a/src/clos/std-accessors.lsp ++++ b/src/clos/std-accessors.lsp +@@ -23,6 +23,7 @@ + ;;; + + (defun safe-slot-definition-location (slotd &optional default) ++ (declare (ignore default)) + (cond ((listp slotd) + (error "List instead of a slot definition object")) + (t +diff --git a/src/cmp/cmpc-machine.lsp b/src/cmp/cmpc-machine.lsp +index 95e9d024..eff6d6fb 100644 +--- a/src/cmp/cmpc-machine.lsp ++++ b/src/cmp/cmpc-machine.lsp +@@ -130,10 +130,6 @@ + :from-lisp from-lisp + :from-lisp-unsafe from-lisp-unsafe)))) + +-(defun make-rep-type-hash (all-c-types) +- (let ((table (make-hash-table :size 128 :test 'eq))) +- table)) +- + (defun default-machine () + (let* ((all-c-types (append +this-machine-c-types+ +all-machines-c-types+)) + (table (make-hash-table :size 128 :test 'eq)) +diff --git a/src/cmp/cmpenv-declaim.lsp b/src/cmp/cmpenv-declaim.lsp +index 4428915d..c93efbe0 100644 +--- a/src/cmp/cmpenv-declaim.lsp ++++ b/src/cmp/cmpenv-declaim.lsp +@@ -36,6 +36,7 @@ + env)) + (multiple-value-bind (body specials types ignored others doc all) + (c1body `((DECLARE ,@args)) nil) ++ (declare (ignore body doc all)) + (when ignored + (cmpwarn-style "IGNORE/IGNORABLE declarations in DECLAIM are ignored")) + (reduce #'add-one-declaration others +diff --git a/src/cmp/cmpenv-declare.lsp b/src/cmp/cmpenv-declare.lsp +index 2996e739..25de42bc 100644 +--- a/src/cmp/cmpenv-declare.lsp ++++ b/src/cmp/cmpenv-declare.lsp +@@ -33,10 +33,10 @@ + (defun validate-alien-declaration (names-list error) + (dolist (new-declaration names-list) + (unless (symbolp new-declaration) +- (cmperr "The declaration ~s is not a symbol" new-declaration)) ++ (funcall error "The declaration ~s is not a symbol" new-declaration)) + (when (type-name-p new-declaration) +- (cmperr "Symbol name ~S cannot be both the name of a type and of a declaration" +- new-declaration)))) ++ (funcall error "Symbol name ~S cannot be both the name of a type and of a declaration" ++ new-declaration)))) + + (defun alien-declaration-p (name &optional (env *cmp-env*)) + (and (symbolp name) +diff --git a/src/cmp/cmpenv-fun.lsp b/src/cmp/cmpenv-fun.lsp +index 7bc1e9a8..681602d6 100644 +--- a/src/cmp/cmpenv-fun.lsp ++++ b/src/cmp/cmpenv-fun.lsp +@@ -91,7 +91,7 @@ + (values nil nil)))) + + (defun get-local-return-type (fun &optional (env *cmp-env*)) +- (let ((x (cmp-env-search-ftype (fun-name fun)))) ++ (let ((x (cmp-env-search-ftype (fun-name fun) env))) + (if x + (values (second x) t) + (values nil nil)))) +diff --git a/src/cmp/cmpinline.lsp b/src/cmp/cmpinline.lsp +index a44a0a10..b0ff9596 100644 +--- a/src/cmp/cmpinline.lsp ++++ b/src/cmp/cmpinline.lsp +@@ -182,7 +182,7 @@ + (wt-nl-open-brace) + (incf *inline-blocks*)) + +-(defun close-inline-blocks (&optional new-line) ++(defun close-inline-blocks () + (loop for i of-type fixnum from 0 below *inline-blocks* + do (wt-nl-close-brace))) + +diff --git a/src/cmp/cmplam.lsp b/src/cmp/cmplam.lsp +index 29f02ed6..c6e313ff 100644 +--- a/src/cmp/cmplam.lsp ++++ b/src/cmp/cmplam.lsp +@@ -335,6 +335,7 @@ The function thus belongs to the type of functions that ecl_make_cfun accepts." + (maxarg call-arguments-limit)) + (destructuring-bind (requireds optionals rest key-flag keywords a-o-k) + (c1form-arg 0 lambda) ++ (declare (ignore keywords)) + (setf minarg (length requireds)) + (when (and (null rest) (not key-flag) (not a-o-k)) + (setf maxarg (+ minarg (/ (length optionals) 3))))) +diff --git a/src/cmp/cmplet.lsp b/src/cmp/cmplet.lsp +index 08694a51..cdd789cd 100644 +--- a/src/cmp/cmplet.lsp ++++ b/src/cmp/cmplet.lsp +@@ -311,7 +311,7 @@ + (when env (pop-debug-lexical-env)))) + (c2expr body)) + +- (close-inline-blocks :line)) ++ (close-inline-blocks)) + + (defun discarded (var form body &aux last) + (labels ((last-form (x &aux (args (c1form-args x))) +diff --git a/src/cmp/cmploc.lsp b/src/cmp/cmploc.lsp +index b2d2115c..c6ec0a66 100644 +--- a/src/cmp/cmploc.lsp ++++ b/src/cmp/cmploc.lsp +@@ -218,6 +218,7 @@ + ;;; + + (defun set-unknown-loc (loc) ++ (declare (ignore loc)) + (unknown-location 'set-loc *destination*)) + + (defun set-loc (loc &aux fd) +diff --git a/src/cmp/cmpmain.lsp b/src/cmp/cmpmain.lsp +index a9858ef5..dd2a4be4 100755 +--- a/src/cmp/cmpmain.lsp ++++ b/src/cmp/cmpmain.lsp +@@ -48,6 +48,8 @@ the environment variable TMPDIR to a different value." template)) + verbose print c-file h-file data-file + system-p load external-format source-truename + source-offset) ++ (declare (ignore verbose print c-file h-file data-file load ++ external-format source-truename source-offset)) + (let* ((format '()) + (extension '())) + (unless type-supplied-p +@@ -145,6 +147,7 @@ the environment variable TMPDIR to a different value." template)) + (defun linker-cc (o-pathname object-files &key + (type :program) + (ld-flags (split-program-options *ld-flags*))) ++ (declare (ignore type)) + (safe-run-program + *ld* + `("-o" ,(brief-namestring o-pathname) +@@ -995,6 +998,7 @@ from the C language code. NIL means \"do not create the file\"." + *safety* *space* *speed* *debug*)) + + (defmacro with-compilation-unit (options &rest body) ++ (declare (ignore options)) + `(progn ,@body)) + + (ext:package-lock "CL" t) +diff --git a/src/cmp/cmpmulti.lsp b/src/cmp/cmpmulti.lsp +index f8cb77c8..a27c6693 100644 +--- a/src/cmp/cmpmulti.lsp ++++ b/src/cmp/cmpmulti.lsp +@@ -166,7 +166,7 @@ + (declare (si::c-local)) + (if (plusp i) (values-loc i) 'VALUE0)) + +-(defun do-m-v-setq (vars form use-bind &aux min-values max-values) ++(defun do-m-v-setq (vars form use-bind) + ;; This routine moves values from the multiple-value stack into the + ;; variables VARS. The amount of values is not known (or at least we only + ;; know that there is some number between MIN-VALUES and MAX-VALUES). If +diff --git a/src/cmp/cmpname.lsp b/src/cmp/cmpname.lsp +index dbd34091..4774816a 100644 +--- a/src/cmp/cmpname.lsp ++++ b/src/cmp/cmpname.lsp +@@ -161,7 +161,7 @@ initialization from the C code which wants to use it." + c) + (t + #\p))) +- (disambiguation (c) ++ (disambiguation (kind) + (case kind + ((:object :c) "") + ((:fasl :fas) "fas_") +diff --git a/src/cmp/cmpnum.lsp b/src/cmp/cmpnum.lsp +index 78ae3260..fab4e0b6 100644 +--- a/src/cmp/cmpnum.lsp ++++ b/src/cmp/cmpnum.lsp +@@ -254,11 +254,13 @@ + (def-type-propagator acos (fname op1-type) + (multiple-value-bind (output-type op1-type) + (ensure-nonrational-type op1-type) ++ (declare (ignore output-type)) + (values (list op1-type) 'NUMBER))) + + (def-type-propagator atan (fname op1-type &optional (op2-type t op2-p)) + (multiple-value-bind (float-t1 t1) + (ensure-nonrational-type op1-type) ++ (declare (ignore float-t1)) + (if op2-p + (multiple-value-bind (result t1 t2) + (maximum-number-type t1 op2-type :only-real t) +diff --git a/src/cmp/cmpopt-printer.lsp b/src/cmp/cmpopt-printer.lsp +index 015ac1e2..fb140c51 100644 +--- a/src/cmp/cmpopt-printer.lsp ++++ b/src/cmp/cmpopt-printer.lsp +@@ -65,19 +65,19 @@ + "ecl_princ(#0,#1)" + :one-liner t))) + +-(define-compiler-macro terpri (&optional stream &environment env) ++(define-compiler-macro terpri (&optional stream) + `(ffi:c-inline (,stream) + (:object) :object + "ecl_terpri(#0)" + :one-liner t)) + +-(define-compiler-macro print (value &optional stream &environment env) ++(define-compiler-macro print (value &optional stream) + `(ffi:c-inline (,value ,stream) + (:object :object) :object + "ecl_print(#0,#1)" + :one-liner t)) + +-(define-compiler-macro prin1 (value &optional stream &environment env) ++(define-compiler-macro prin1 (value &optional stream) + `(ffi:c-inline (,value ,stream) + (:object :object) :object + "ecl_prin1(#0,#1)" +diff --git a/src/cmp/cmpopt-sequence.lsp b/src/cmp/cmpopt-sequence.lsp +index 7443f317..e07ed90f 100644 +--- a/src/cmp/cmpopt-sequence.lsp ++++ b/src/cmp/cmpopt-sequence.lsp +@@ -220,6 +220,7 @@ + (return ,%sublist))))))) + + (define-compiler-macro member (&whole whole value list &rest sequence-args) ++ (declare (value list sequence-args)) + (if (policy-inline-sequence-functions) + (or (apply #'expand-member (rest whole)) + whole) +@@ -264,6 +265,7 @@ + (return ,%elt)))))))))) + + (define-compiler-macro assoc (&whole whole value list &rest sequence-args) ++ (declare (ignore value list sequence-args)) + (if (policy-inline-sequence-functions) + (or (apply #'expand-assoc (rest whole)) + whole) +@@ -287,6 +289,7 @@ + (return ,%elt)))))))) + + (define-compiler-macro find (&whole whole value sequence &rest sequence-args) ++ (declare (ignore value sequence sequence-args)) + (if (policy-inline-sequence-functions) + (or (apply #'expand-find (rest whole)) + whole) +diff --git a/src/cmp/cmpopt.lsp b/src/cmp/cmpopt.lsp +index e4de366f..b591d0cc 100644 +--- a/src/cmp/cmpopt.lsp ++++ b/src/cmp/cmpopt.lsp +@@ -160,6 +160,7 @@ + form)))) + + (define-compiler-macro typep (&whole form object type &optional e &environment env) ++ (declare (ignore e)) + (expand-typep form object type env)) + + ;;; +@@ -346,8 +347,7 @@ + (multiple-value-bind (constant-p float) + (constant-value-p float env) + (when (and constant-p (floatp float)) +- (let* ((aux (gentemp)) +- (float (type-of float)) ++ (let* ((float (type-of float)) + (c-type (lisp-type->rep-type float))) + `(let ((value ,value)) + (declare (:read-only value)) +diff --git a/src/cmp/cmpprop.lsp b/src/cmp/cmpprop.lsp +index b6e6f727..e9ba7c6f 100644 +--- a/src/cmp/cmpprop.lsp ++++ b/src/cmp/cmpprop.lsp +@@ -379,12 +379,14 @@ compute it. This version only handles the simplest cases." + elt-type))) + + (def-type-propagator si::row-major-aset (fname array-type index obj) ++ (declare (ignore index obj)) + (multiple-value-bind (elt-type array-type) + (type-from-array-elt array-type) + (values (list array-type 'si::index elt-type) + elt-type))) + + (def-type-propagator row-major-aref (fname array-type index) ++ (declare (ignore index)) + (multiple-value-bind (elt-type array-type) + (type-from-array-elt array-type) + (values (list array-type 'si::index) elt-type))) +diff --git a/src/cmp/cmptop.lsp b/src/cmp/cmptop.lsp +index 072329e4..e41f4f0a 100644 +--- a/src/cmp/cmptop.lsp ++++ b/src/cmp/cmptop.lsp +@@ -477,6 +477,7 @@ + args + (multiple-value-bind (function pprint doc-string) + (sys::expand-defmacro name lambda-list body) ++ (declare (ignore pprint doc-string)) + (let ((fn (cmp-eval function *cmp-env*))) + (cmp-env-register-global-macro name fn)) + (t1expr* (macroexpand `(DEFMACRO ,@args)))))) +diff --git a/src/cmp/cmptype-assert.lsp b/src/cmp/cmptype-assert.lsp +index d485d651..f979997d 100644 +--- a/src/cmp/cmptype-assert.lsp ++++ b/src/cmp/cmptype-assert.lsp +@@ -58,7 +58,7 @@ + FEtype_error_sequence(#0);") + (vector . "if (ecl_unlikely(!ECL_VECTORP(#0))) FEtype_error_vector(#0);"))) + +-(defun simple-type-assertion (value type env) ++(defun simple-type-assertion (value type) + (let ((simple-form (cdr (assoc type +simple-type-assertions+)))) + (if simple-form + `(ffi:c-inline (,value) (:object) :void ,simple-form +@@ -82,13 +82,13 @@ + (compulsory + ;; The check has to be produced, independent of the declared + ;; value of the variable (for instance, in LAMBDA arguments). +- (simple-type-assertion value type env)) ++ (simple-type-assertion value type)) + (t + ;; We may rely on the compiler to choose the appropriate + ;; branch once type propagation has happened. + `(ext:compiler-typecase ,value + (,type) +- (t ,(simple-type-assertion value type env)))))) ++ (t ,(simple-type-assertion value type)))))) + + (defun c1checked-value (args) + (let* ((type (pop args)) +diff --git a/src/cmp/cmpvar.lsp b/src/cmp/cmpvar.lsp +index d5009e35..590b3081 100644 +--- a/src/cmp/cmpvar.lsp ++++ b/src/cmp/cmpvar.lsp +@@ -74,13 +74,13 @@ + (mapcar #'first (var-read-nodes var))) + + (defun assert-var-ref-value (var) +- #+debug-compiler +- (unless (let ((ref (var-ref var))) +- (or (> ref (/ most-positive-fixnum 2)) +- (= (var-ref var) (+ (length (var-read-nodes var)) +- (length (var-set-nodes var)))))) +- (baboon :format-control "Number of references in VAR ~A unequal to references list" +- :format-arguments (list var)))) ++ (when *debug-compiler* ++ (unless (let ((ref (var-ref var))) ++ (or (> ref (/ most-positive-fixnum 2)) ++ (= (var-ref var) (+ (length (var-read-nodes var)) ++ (length (var-set-nodes var)))))) ++ (baboon :format-control "Number of references in VAR ~A unequal to references list" ++ :format-arguments (list var))))) + + (defun assert-var-not-ignored (var) + (when (let ((x (var-ignorable var))) (and x (minusp x))) +@@ -229,6 +229,7 @@ + (defun c1vref (name) + (multiple-value-bind (var cfb unw) + (cmp-env-search-var name) ++ (declare (ignore unw)) + (cond ((null var) + (c1make-global-variable name :warn t + :type (or (si:get-sysprop name 'CMP-TYPE) t))) +diff --git a/src/cmp/proclamations.lsp b/src/cmp/proclamations.lsp +index 7f9d607a..54839d2a 100644 +--- a/src/cmp/proclamations.lsp ++++ b/src/cmp/proclamations.lsp +@@ -1189,7 +1189,7 @@ + (proclamation si:open-unix-socket-stream (string) stream) + #+wants-sockets + (proclamation si:lookup-host-entry (t) (values (or null string) list list)) +-(proclamation si:copy-stream (stream stream wait) t) ++(proclamation si:copy-stream (stream stream gen-bool) t) + (proclamation si:make-encoding (t) hash-table) + (proclamation si:load-encoding (t) t) + +diff --git a/src/h/bytecodes.h b/src/h/bytecodes.h +index 8b106ea7..235a1a97 100644 +--- a/src/h/bytecodes.h ++++ b/src/h/bytecodes.h +@@ -152,6 +152,7 @@ typedef int16_t cl_opcode; + goto *(&&LBL_OP_NOP + offsets[GET_OPCODE(vector)]) + #else + #define BEGIN_SWITCH \ ++ BEGIN: \ + switch (GET_OPCODE(vector)) + #define THREAD_NEXT \ + goto BEGIN +diff --git a/src/lsp/defstruct.lsp b/src/lsp/defstruct.lsp +index 3b568b8d..f3848a25 100644 +--- a/src/lsp/defstruct.lsp ++++ b/src/lsp/defstruct.lsp +@@ -313,10 +313,10 @@ + (or (equal old-def new-def) + (destructuring-bind (old-slot-name old-init old-type old-read-only old-offset old-ac) + old-def +- (declare (ignore old-init old-read-only old-ac)) ++ (declare (ignore old-slot-name old-init old-read-only old-ac)) + (destructuring-bind (new-slot-name new-init new-type new-read-only new-offset new-ac) + new-def +- (declare (ignore new-init new-read-only new-ac)) ++ (declare (ignore new-slot-name new-init new-read-only new-ac)) + ;; Name EQL is not enforced because structures may be + ;; constructed by code generators and it is likely they + ;; will have gensymed names. -- jd 2019-05-22 +diff --git a/src/lsp/ffi.lsp b/src/lsp/ffi.lsp +index 7e2b12f3..017aaaa9 100644 +--- a/src/lsp/ffi.lsp ++++ b/src/lsp/ffi.lsp +@@ -834,7 +834,8 @@ reference the arguments of the function as \"#0\", \"#1\", etc. + + The interpreter ignores this form. ARG-TYPES are argument types of + the defined Lisp function and RESULT-TYPE is its return type." +- (let ((args (mapcar #'(lambda (x) (gensym)) arg-types))) ++ (let ((args (mapcar #'(lambda (x) (declare (ignore x)) (gensym)) ++ arg-types))) + `(defun ,name ,args + (c-inline ,args ,arg-types ,result-type + ,c-expression :one-liner t)))) +@@ -850,7 +851,8 @@ FUNCTION-NAME. + The interpreter ignores this form. ARG-TYPES are argument types of + the C function and RESULT-TYPE is its return type." + (let ((output-type :object) +- (args (mapcar #'(lambda (x) (gensym)) arg-types))) ++ (args (mapcar #'(lambda (x) (declare (ignore x)) (gensym)) ++ arg-types))) + (if (consp c-name) + (setf output-type (first c-name) + c-name (second c-name))) +diff --git a/src/lsp/setf.lsp b/src/lsp/setf.lsp +index 6575701b..6b9ffbb6 100644 +--- a/src/lsp/setf.lsp ++++ b/src/lsp/setf.lsp +@@ -31,7 +31,6 @@ + (push item vars)) + (push item all)) + (dotimes (i stores-no) +- (declare (ignore i)) + (push (gensym) stores)) + (let* ((all (nreverse all))) + (values (nreverse vars) +-- +2.26.2 + diff --git a/build/pkgs/ecl/patches/0003-printer-fix-printing-of-symbols-with-non-ascii-names.patch b/build/pkgs/ecl/patches/0003-printer-fix-printing-of-symbols-with-non-ascii-names.patch new file mode 100644 index 00000000000..3cbe157cfbc --- /dev/null +++ b/build/pkgs/ecl/patches/0003-printer-fix-printing-of-symbols-with-non-ascii-names.patch @@ -0,0 +1,29 @@ +From d0a454a36e1e552beb15d9b09e8d16658489f4d5 Mon Sep 17 00:00:00 2001 +From: Marius Gerbershagen +Date: Wed, 6 May 2020 21:03:18 +0200 +Subject: [PATCH 3/3] printer: fix printing of symbols with non-ascii names + +Bug was introduced in commit c6b4296bb886ad70b83c5cc0f472f6855783e2f9 +in converting buffer_write_char from a macro to an inline +function. Problem reported by Vladimir Sedach on the ecl-devel mailing +list. +--- + src/c/printer/write_symbol.d | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/c/printer/write_symbol.d b/src/c/printer/write_symbol.d +index a39bab97..49ef5d31 100644 +--- a/src/c/printer/write_symbol.d ++++ b/src/c/printer/write_symbol.d +@@ -103,7 +103,7 @@ needs_to_be_escaped(cl_object s, cl_object readtable, cl_object print_case) + } + + static inline void +-buffer_write_char(char c, cl_object buffer, cl_object stream, cl_index *buffer_ndx, cl_index buffer_size) { ++buffer_write_char(ecl_character c, cl_object buffer, cl_object stream, cl_index *buffer_ndx, cl_index buffer_size) { + ecl_char_set(buffer, (*buffer_ndx)++, c); + if (*buffer_ndx >= buffer_size) { + si_fill_pointer_set(buffer, ecl_make_fixnum(buffer_size)); +-- +2.26.2 + diff --git a/build/pkgs/ecl/patches/16.1.2-getcwd.patch b/build/pkgs/ecl/patches/16.1.2-getcwd.patch deleted file mode 100644 index 6e186a1d627..00000000000 --- a/build/pkgs/ecl/patches/16.1.2-getcwd.patch +++ /dev/null @@ -1,14 +0,0 @@ -Backport of fix in master ecl repo. See trac 20845. -diff --git a/src/c/unixfsys.d b/src/c/unixfsys.d -index d3dd4d1..287eded 100644 ---- a/src/c/unixfsys.d -+++ b/src/c/unixfsys.d -@@ -158,7 +158,7 @@ current_dir(void) { - output = ecl_alloc_adjustable_base_string(size); - ecl_disable_interrupts(); - ok = getcwd((char*)output->base_string.self, size); -- if (ok == NULL && errno != ENAMETOOLONG) { -+ if (ok == NULL && errno != ERANGE) { - perror("ext::getcwd error"); - ecl_internal_error("Can't work without CWD"); - } diff --git a/build/pkgs/ecl/patches/214.patch b/build/pkgs/ecl/patches/214.patch new file mode 100644 index 00000000000..b466b3fe685 --- /dev/null +++ b/build/pkgs/ecl/patches/214.patch @@ -0,0 +1,202 @@ +diff --git a/src/configure b/src/configure +index 103f4102be537c5521889ad14f7d4151245bd4b2..5c7cfc2b8b4822a7477c0ba2883993a8ef185d09 100755 +--- a/src/configure ++++ b/src/configure +@@ -3932,6 +3932,184 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' + ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' + ac_compiler_gnu=$ac_cv_c_compiler_gnu + # sets variable CC ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 ++$as_echo_n "checking for $CC option to accept ISO C99... " >&6; } ++if ${ac_cv_prog_cc_c99+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_cv_prog_cc_c99=no ++ac_save_CC=$CC ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++#include ++#include ++#include ++#include ++#include ++ ++// Check varargs macros. These examples are taken from C99 6.10.3.5. ++#define debug(...) fprintf (stderr, __VA_ARGS__) ++#define showlist(...) puts (#__VA_ARGS__) ++#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) ++static void ++test_varargs_macros (void) ++{ ++ int x = 1234; ++ int y = 5678; ++ debug ("Flag"); ++ debug ("X = %d\n", x); ++ showlist (The first, second, and third items.); ++ report (x>y, "x is %d but y is %d", x, y); ++} ++ ++// Check long long types. ++#define BIG64 18446744073709551615ull ++#define BIG32 4294967295ul ++#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) ++#if !BIG_OK ++ your preprocessor is broken; ++#endif ++#if BIG_OK ++#else ++ your preprocessor is broken; ++#endif ++static long long int bignum = -9223372036854775807LL; ++static unsigned long long int ubignum = BIG64; ++ ++struct incomplete_array ++{ ++ int datasize; ++ double data[]; ++}; ++ ++struct named_init { ++ int number; ++ const wchar_t *name; ++ double average; ++}; ++ ++typedef const char *ccp; ++ ++static inline int ++test_restrict (ccp restrict text) ++{ ++ // See if C++-style comments work. ++ // Iterate through items via the restricted pointer. ++ // Also check for declarations in for loops. ++ for (unsigned int i = 0; *(text+i) != '\0'; ++i) ++ continue; ++ return 0; ++} ++ ++// Check varargs and va_copy. ++static void ++test_varargs (const char *format, ...) ++{ ++ va_list args; ++ va_start (args, format); ++ va_list args_copy; ++ va_copy (args_copy, args); ++ ++ const char *str; ++ int number; ++ float fnumber; ++ ++ while (*format) ++ { ++ switch (*format++) ++ { ++ case 's': // string ++ str = va_arg (args_copy, const char *); ++ break; ++ case 'd': // int ++ number = va_arg (args_copy, int); ++ break; ++ case 'f': // float ++ fnumber = va_arg (args_copy, double); ++ break; ++ default: ++ break; ++ } ++ } ++ va_end (args_copy); ++ va_end (args); ++} ++ ++int ++main () ++{ ++ ++ // Check bool. ++ _Bool success = false; ++ ++ // Check restrict. ++ if (test_restrict ("String literal") == 0) ++ success = true; ++ char *restrict newvar = "Another string"; ++ ++ // Check varargs. ++ test_varargs ("s, d' f .", "string", 65, 34.234); ++ test_varargs_macros (); ++ ++ // Check flexible array members. ++ struct incomplete_array *ia = ++ malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ++ ia->datasize = 10; ++ for (int i = 0; i < ia->datasize; ++i) ++ ia->data[i] = i * 1.234; ++ ++ // Check named initializers. ++ struct named_init ni = { ++ .number = 34, ++ .name = L"Test wide string", ++ .average = 543.34343, ++ }; ++ ++ ni.number = 58; ++ ++ int dynamic_array[ni.number]; ++ dynamic_array[ni.number - 1] = 543; ++ ++ // work around unused variable warnings ++ return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' ++ || dynamic_array[ni.number - 1] != 543); ++ ++ ; ++ return 0; ++} ++_ACEOF ++for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 ++do ++ CC="$ac_save_CC $ac_arg" ++ if ac_fn_c_try_compile "$LINENO"; then : ++ ac_cv_prog_cc_c99=$ac_arg ++fi ++rm -f core conftest.err conftest.$ac_objext ++ test "x$ac_cv_prog_cc_c99" != "xno" && break ++done ++rm -f conftest.$ac_ext ++CC=$ac_save_CC ++ ++fi ++# AC_CACHE_VAL ++case "x$ac_cv_prog_cc_c99" in ++ x) ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 ++$as_echo "none needed" >&6; } ;; ++ xno) ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 ++$as_echo "unsupported" >&6; } ;; ++ *) ++ CC="$CC $ac_cv_prog_cc_c99" ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 ++$as_echo "$ac_cv_prog_cc_c99" >&6; } ;; ++esac ++if test "x$ac_cv_prog_cc_c99" != xno; then : ++ ++fi ++ ++ # checks that CC by default accepts C99 code, if not, ++ # tries adding -std=gnu-99, if this has no effect, throws an error. + ac_ext=cpp + ac_cpp='$CXXCPP $CPPFLAGS' + ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +diff --git a/src/configure.ac b/src/configure.ac +index 4e615408e7b336329b4228cb40ca820f12adacba..df7a90c84538a32ca45f2dbc4f398c4c51bec3ae 100644 +--- a/src/configure.ac ++++ b/src/configure.ac +@@ -334,6 +334,8 @@ AC_CANONICAL_HOST + dnl ===================================================================== + dnl Checks for programs + AC_PROG_CC # sets variable CC ++AC_PROG_CC_C99 # checks that CC by default accepts C99 code, if not, ++ # tries adding -std=gnu-99, if this has no effect, throws an error. + AC_PROG_CXX # sets variable CXX + AC_PROG_CPP # sets variable CPP + AC_PROG_RANLIB # sets variable RANLIB diff --git a/build/pkgs/ecl/patches/215.patch b/build/pkgs/ecl/patches/215.patch new file mode 100644 index 00000000000..a0f18f2b22f --- /dev/null +++ b/build/pkgs/ecl/patches/215.patch @@ -0,0 +1,175 @@ +diff --git a/src/aclocal.m4 b/src/aclocal.m4 +index 6adc5c63d7c60cf34ac7cee372158d617143191b..cf011efd016a20d5685c12bbac7d1d731eaa755e 100644 +--- a/src/aclocal.m4 ++++ b/src/aclocal.m4 +@@ -665,6 +665,21 @@ case "${ECL_STACK_DIR}" in + up|UP) AC_MSG_RESULT(no) ;; + *) AC_MSG_ERROR(Unable to determine stack growth direction) + esac]) ++ ++dnl ++dnl -------------------------------------------------------------- ++dnl Check if we can determine the stack size at runtime ++dnl ++AC_DEFUN(ECL_STACK_SIZE,[ ++AC_CHECK_HEADER([sys/resource.h], ++ [AC_DEFINE([HAVE_SYS_RESOURCE_H], [], [Define to 1 if you have the header file.]) ++ AC_CHECK_DECL([RLIMIT_STACK], ++ [AC_DEFINE([ECL_CAN_SET_STACK_SIZE], [], [Define to 1 if we can set the stack size at runtime.])], ++ [], ++ [#include ])], ++ [],[]) ++]) ++ + dnl + dnl ------------------------------------------------------------ + dnl Find out a setjmp() that does not save signals. It is called +diff --git a/src/c/stacks.d b/src/c/stacks.d +index d985766ba5e03b07d6a25e31e9880576250038aa..c0125ed2bc8ec9d1712e5e71f3c91edb1bc7c12a 100644 +--- a/src/c/stacks.d ++++ b/src/c/stacks.d +@@ -29,7 +29,7 @@ cs_set_size(cl_env_ptr env, cl_index new_size) + { + volatile char foo = 0; + cl_index margin = ecl_option_values[ECL_OPT_C_STACK_SAFETY_AREA]; +-#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_STACK) && !defined(NACL) ++#if defined(ECL_CAN_SET_STACK_SIZE) + { + struct rlimit rl; + +@@ -40,13 +40,22 @@ cs_set_size(cl_env_ptr env, cl_index new_size) + if (setrlimit(RLIMIT_STACK, &rl)) + ecl_internal_error("Can't set the size of the C stack"); + } ++ } else { ++ rl.rlim_cur = new_size; ++ } ++ if (rl.rlim_cur == 0 || rl.rlim_cur == RLIM_INFINITY || rl.rlim_cur > (cl_index)(-1)) { ++ /* Either getrlimit failed or returned nonsense, either way we ++ * don't know the stack size. Use a default of 1 MB and hope for ++ * the best. */ ++ new_size = 1048576; ++ } else { + new_size = rl.rlim_cur; ++ } + #ifdef ECL_DOWN_STACK +- env->cs_barrier = env->cs_org - new_size; ++ env->cs_barrier = env->cs_org - new_size; + #else +- env->cs_barrier = env->cs_org + new_size; ++ env->cs_barrier = env->cs_org + new_size; + #endif +- } + } + #endif + env->cs_limit_size = new_size - (2*margin); +@@ -64,7 +73,7 @@ cs_set_size(cl_env_ptr env, cl_index new_size) + } + #endif + else +- ecl_internal_error("Can't set the size of the C stack"); ++ ecl_internal_error("Can't set the size of the C stack: sanity check failed"); + env->cs_size = new_size; + } + +diff --git a/src/configure b/src/configure +index 103f4102be537c5521889ad14f7d4151245bd4b2..b2e7608887d1d0648c80578dea26a50ff9a945c0 100755 +--- a/src/configure ++++ b/src/configure +@@ -7125,7 +7125,7 @@ fi + done + + +-for ac_header in sys/resource.h sys/utsname.h float.h pwd.h dlfcn.h link.h \ ++for ac_header in sys/utsname.h float.h pwd.h dlfcn.h link.h \ + mach-o/dyld.h dirent.h sys/ioctl.h sys/select.h \ + sys/wait.h semaphore.h + do : +@@ -8345,6 +8345,24 @@ $as_echo "no" >&6; } ;; + *) as_fn_error $? "Unable to determine stack growth direction" "$LINENO" 5 + esac + ++ac_fn_c_check_header_mongrel "$LINENO" "sys/resource.h" "ac_cv_header_sys_resource_h" "$ac_includes_default" ++if test "x$ac_cv_header_sys_resource_h" = xyes; then : ++ ++$as_echo "#define HAVE_SYS_RESOURCE_H /**/" >>confdefs.h ++ ++ ac_fn_c_check_decl "$LINENO" "RLIMIT_STACK" "ac_cv_have_decl_RLIMIT_STACK" "#include ++" ++if test "x$ac_cv_have_decl_RLIMIT_STACK" = xyes; then : ++ ++$as_echo "#define ECL_CAN_SET_STACK_SIZE /**/" >>confdefs.h ++ ++fi ++ ++fi ++ ++ ++ ++ + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether closedir returns void" >&5 + $as_echo_n "checking whether closedir returns void... " >&6; } +@@ -8997,8 +9015,6 @@ main () + if (*(data + i) != *(data3 + i)) + return 14; + close (fd); +- free (data); +- free (data3); + return 0; + } + _ACEOF +diff --git a/src/configure.ac b/src/configure.ac +index 4e615408e7b336329b4228cb40ca820f12adacba..8103d88bc9b2abe24ebef83db4ea36355c947fc6 100644 +--- a/src/configure.ac ++++ b/src/configure.ac +@@ -658,7 +658,7 @@ AC_CHECK_HEADERS( [fcntl.h limits.h netdb.h netinet/in.h] \ + [sched.h] ) + dnl !!! end autoscan + +-AC_CHECK_HEADERS( [sys/resource.h sys/utsname.h float.h pwd.h dlfcn.h link.h] \ ++AC_CHECK_HEADERS( [sys/utsname.h float.h pwd.h dlfcn.h link.h] \ + [mach-o/dyld.h dirent.h sys/ioctl.h sys/select.h] \ + [sys/wait.h semaphore.h] ) + +@@ -711,8 +711,9 @@ ECL_SSE + ECL_COMPLEX_C99 + + dnl ----------------------------------------------------------------------- +-dnl Study the call conventions ++dnl Stack size and growth direction + ECL_STACK_DIRECTION ++ECL_STACK_SIZE + + dnl ===================================================================== + dnl Checks for library functions +diff --git a/src/ecl/configpre.h b/src/ecl/configpre.h +index 98f96bd653052014ef612cfcfcd87a08557979fd..aeda058e059d07d07d9c903f647e74f8dfe30c7a 100644 +--- a/src/ecl/configpre.h ++++ b/src/ecl/configpre.h +@@ -9,6 +9,9 @@ + /* ECL_AVOID_FPE_H */ + #undef ECL_AVOID_FPE_H + ++/* Define to 1 if we can set the stack size at runtime. */ ++#undef ECL_CAN_SET_STACK_SIZE ++ + /* Allow STREAM operations to work on arbitrary objects */ + #undef ECL_CLOS_STREAMS + +diff --git a/src/h/config-internal.h.in b/src/h/config-internal.h.in +index dd7a4f8ad0e86fb735333c4794665d4520fbf830..6b4438ad5953971952ac26328c5c2a3e0f898eff 100644 +--- a/src/h/config-internal.h.in ++++ b/src/h/config-internal.h.in +@@ -240,7 +240,10 @@ + #include "@ECL_LIBFFI_HEADER@" + #endif + +-#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_STACK) && !defined(NACL) ++/* Can we determine and set the stack size at runtime? */ ++#undef ECL_CAN_SET_STACK_SIZE ++ ++#if defined(ECL_CAN_SET_STACK_SIZE) + #define ECL_DEFAULT_C_STACK_SIZE 0 /* Use the stack size provided by the OS */ + #else + #define ECL_DEFAULT_C_STACK_SIZE @ECL_DEFAULT_C_STACK_SIZE@ diff --git a/build/pkgs/ecl/patches/216.patch b/build/pkgs/ecl/patches/216.patch new file mode 100644 index 00000000000..055a2636e7c --- /dev/null +++ b/build/pkgs/ecl/patches/216.patch @@ -0,0 +1,42 @@ +diff --git a/src/cmp/cmpos-run.lsp b/src/cmp/cmpos-run.lsp +index 418751c3d188c1ba6042d965fe1424eb6d4c7ca4..4008b3a6ad7bac846f50d1c4d03b45cf751a451e 100755 +--- a/src/cmp/cmpos-run.lsp ++++ b/src/cmp/cmpos-run.lsp +@@ -51,18 +51,25 @@ + (program (car program))) + (with-current-directory + ;; when compiling ECL itself, we only have low-level functions +- ;; available, otherwise we can use run-program and get proper +- ;; quoting of arguments +- #+ecl-min (multiple-value-bind (output-stream return-status pid) +- (si:run-program-inner program args :default nil) +- (setf output (collect-lines output-stream)) +- (multiple-value-setq (return-status result) +- (si:waitpid pid t))) +- #-ecl-min (multiple-value-bind (output-stream return-status process-obj) +- (ext:run-program program args :wait nil) +- (setf output (collect-lines output-stream)) +- (multiple-value-setq (return-status result) +- (ext:external-process-wait process-obj t))))) ++ ;; available ... ++ #+(and ecl-min (not cygwin)) ++ (multiple-value-bind (output-stream return-status pid) ++ (si:run-program-inner program args :default nil) ++ (setf output (collect-lines output-stream)) ++ (multiple-value-setq (return-status result) ++ (si:waitpid pid t))) ++ ;; ... otherwise we can use run-program and get proper ++ ;; quoting of arguments ... ++ #+(and (not ecl-min) (not cygwin)) ++ (multiple-value-bind (output-stream return-status process-obj) ++ (ext:run-program program args :wait nil) ++ (setf output (collect-lines output-stream)) ++ (multiple-value-setq (return-status result) ++ (ext:external-process-wait process-obj t))) ++ ;; ... unless we're running on cygwin which has problems with ++ ;; forking so we have to use si:system ++ #+cygwin ++ (setf result (si:system (format nil "~A~{ ~A~}" program args))))) + (cond ((null result) + (cerror "Continues anyway." + "Unable to execute:~%(EXT:RUN-PROGRAM ~S ~S)" diff --git a/build/pkgs/ecl/patches/ECL_WITH_LISP_FPE.patch b/build/pkgs/ecl/patches/ECL_WITH_LISP_FPE.patch new file mode 100644 index 00000000000..ce87f79ab58 --- /dev/null +++ b/build/pkgs/ecl/patches/ECL_WITH_LISP_FPE.patch @@ -0,0 +1,47 @@ +From 75877dd8f0d534552284ba4380ba65baa74f028f Mon Sep 17 00:00:00 2001 +From: Marius Gerbershagen +Date: Sun, 28 Jun 2020 11:02:15 +0200 +Subject: [PATCH] fpe: fix ECL_WITH_LISP_FPE macro + +We can't use ecl_process_env_unsafe() == NULL to check if ECL has +booted because the return value of ecl_process_env_unsafe is +unpredictable before ECL has booted. The reason is that +ecl_process_env_unsafe calls pthread_getspecific with an uninitialized +key stored in cl_env_key. But another call to pthread_setspecific +might have already registered a key which happens to be the same as +the not yet initialized cl_env_key, yielding a non-NULL value. +--- + src/h/impl/math_fenv.h | 17 ++++++++--------- + 1 file changed, 8 insertions(+), 9 deletions(-) + +diff --git a/src/h/impl/math_fenv.h b/src/h/impl/math_fenv.h +index 0a93c8e0a..9630f4c6c 100644 +--- a/src/h/impl/math_fenv.h ++++ b/src/h/impl/math_fenv.h +@@ -72,15 +72,14 @@ + + #if defined(HAVE_FENV_H) && !defined(ECL_AVOID_FPE_H) + # if defined(HAVE_FEENABLEEXCEPT) +-# define ECL_WITH_LISP_FPE_BEGIN do { \ +- fenv_t __fenv; \ +- fegetenv(&__fenv); \ +- cl_env_ptr __the_env = ecl_process_env_unsafe(); \ +- if (__the_env) { \ +- int bits = __the_env->trap_fpe_bits; \ +- fedisableexcept(FE_ALL_EXCEPT & ~bits); \ +- feenableexcept(FE_ALL_EXCEPT & bits); \ +- } \ ++# define ECL_WITH_LISP_FPE_BEGIN do { \ ++ fenv_t __fenv; \ ++ fegetenv(&__fenv); \ ++ if (ecl_get_option(ECL_OPT_BOOTED) > 0) { \ ++ int bits = ecl_process_env()->trap_fpe_bits; \ ++ fedisableexcept(FE_ALL_EXCEPT & ~bits); \ ++ feenableexcept(FE_ALL_EXCEPT & bits); \ ++ } \ + feclearexcept(FE_ALL_EXCEPT); + # else + # define ECL_WITH_LISP_FPE_BEGIN do { \ +-- +GitLab + diff --git a/build/pkgs/ecl/patches/cygwin-uname.patch b/build/pkgs/ecl/patches/cygwin-uname.patch deleted file mode 100644 index e0bd8bcc8f7..00000000000 --- a/build/pkgs/ecl/patches/cygwin-uname.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/src/lsp/config.lsp.in b/src/lsp/config.lsp.in -index e8bed39..957d7ba 100644 ---- a/src/lsp/config.lsp.in -+++ b/src/lsp/config.lsp.in -@@ -7,12 +7,12 @@ - ;; - (in-package "LISP") - --#+(and (not ecl-min) (not nacl) (not :mingw32) (not :msvc) (not :cygwin) uname) -+#+(and (not ecl-min) (not nacl) (not :mingw32) (not :msvc) uname) - (ffi:clines " - #include - ") - --#+(and (not ecl-min) (not nacl) (not :mingw32) (not :msvc) (not :cygwin) uname) -+#+(and (not ecl-min) (not nacl) (not :mingw32) (not :msvc) uname) - (defun uname () - (ffi:c-inline () () :object "{ - cl_object output; -@@ -28,7 +28,7 @@ - @(return) = output; - }" :one-liner nil)) - --#+(and ecl-min uname) -+#+ecl-min - (defun uname () - "A poor man's uname" - (list "@SOFTWARE_TYPE@" diff --git a/build/pkgs/ecl/patches/ffi_abi_libffi33.patch b/build/pkgs/ecl/patches/ffi_abi_libffi33.oldpatch similarity index 100% rename from build/pkgs/ecl/patches/ffi_abi_libffi33.patch rename to build/pkgs/ecl/patches/ffi_abi_libffi33.oldpatch diff --git a/build/pkgs/ecl/patches/fix-cc.patch b/build/pkgs/ecl/patches/fix-cc.patch deleted file mode 100644 index 5615a5e1c3b..00000000000 --- a/build/pkgs/ecl/patches/fix-cc.patch +++ /dev/null @@ -1,29 +0,0 @@ -From d23d974654fbef889aa1c110d6e2968e07b58930 Mon Sep 17 00:00:00 2001 -From: "Erik M. Bray" -Date: Thu, 17 Mar 2016 15:57:16 +0100 -Subject: [PATCH] Allow CC and other environment variables passed to - safe-run-program to be a command with arguments - ---- - src/cmp/cmpos-run.lsp | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/src/cmp/cmpos-run.lsp b/src/cmp/cmpos-run.lsp -index 49f6605..b824409 100755 ---- a/src/cmp/cmpos-run.lsp -+++ b/src/cmp/cmpos-run.lsp -@@ -54,7 +54,10 @@ - (cmpnote "Invoking external command:~% ~A ~{~A ~}" program args) - (multiple-value-bind (stream result process) - (let* ((*standard-output* ext:+process-standard-output+) -- (*error-output* ext:+process-error-output+)) -+ (*error-output* ext:+process-error-output+) -+ (program (split-program-options program)) -+ (args `(,@(cdr program) ,@args)) -+ (program (car program))) - (with-current-directory - #-(and cygwin (not ecl-min)) - (ext:run-program program args :input nil :output t :error t :wait t) --- -1.9.1 - diff --git a/build/pkgs/ecl/patches/flisten-bug.patch b/build/pkgs/ecl/patches/flisten-bug.patch deleted file mode 100644 index cec8c36a448..00000000000 --- a/build/pkgs/ecl/patches/flisten-bug.patch +++ /dev/null @@ -1,16 +0,0 @@ -Prevent busy loop during ECL interpreter shutdown on Cygwin. - -See https://trac.sagemath.org/ticket/22337 -diff --git a/src/c/file.d b/src/c/file.d -index 2285a60..aeed899 100755 ---- a/src/c/file.d -+++ b/src/c/file.d -@@ -5317,7 +5317,7 @@ static int - flisten(cl_object stream, FILE *fp) - { - int aux; -- if (feof(fp)) -+ if (feof(fp) || ferror(fp)) - return ECL_LISTEN_EOF; - #ifdef FILE_CNT - if (FILE_CNT(fp) > 0) diff --git a/build/pkgs/ecl/patches/format-directive-limit.patch b/build/pkgs/ecl/patches/format-directive-limit.patch deleted file mode 100644 index e9449267d06..00000000000 --- a/build/pkgs/ecl/patches/format-directive-limit.patch +++ /dev/null @@ -1,81 +0,0 @@ -Fix from upstream that happens to work around -https://trac.sagemath.org/ticket/23011 -diff --git a/src/lsp/format.lsp b/src/lsp/format.lsp -index 77ca799..53b887c 100644 ---- a/src/lsp/format.lsp -+++ b/src/lsp/format.lsp -@@ -307,11 +307,13 @@ - :start (format-directive-start struct) - :end (format-directive-end struct)))) - -+(defconstant +format-directive-limit+ (1+ (char-code #\~))) -+ - #+formatter - (defparameter *format-directive-expanders* -- (make-array char-code-limit :initial-element nil)) -+ (make-array +format-directive-limit+ :initial-element nil)) - (defparameter *format-directive-interpreters* -- (make-array char-code-limit :initial-element nil)) -+ (make-array +format-directive-limit+ :initial-element nil)) - - (defparameter *default-format-error-control-string* nil) - (defparameter *default-format-error-offset* nil) -@@ -550,24 +552,24 @@ - (write-string directive stream) - (interpret-directive-list stream (cdr directives) orig-args args)) - (#-ecl format-directive #+ecl vector -+ (multiple-value-bind -+ (new-directives new-args) -+ (let* ((code (char-code (format-directive-character directive))) -+ (function -+ (and (< code +format-directive-limit+) -+ (svref *format-directive-interpreters* code))) -+ (*default-format-error-offset* -+ (1- (format-directive-end directive)))) -+ (unless function -+ (error 'format-error -+ :complaint "Unknown format directive.")) - (multiple-value-bind - (new-directives new-args) -- (let ((function -- (svref *format-directive-interpreters* -- (char-code (format-directive-character -- directive)))) -- (*default-format-error-offset* -- (1- (format-directive-end directive)))) -- (unless function -- (error 'format-error -- :complaint "Unknown format directive.")) -- (multiple-value-bind -- (new-directives new-args) -- (funcall function stream directive -- (cdr directives) orig-args args) -- (values new-directives new-args))) -- (interpret-directive-list stream new-directives -- orig-args new-args))))) -+ (funcall function stream directive -+ (cdr directives) orig-args args) -+ (values new-directives new-args))) -+ (interpret-directive-list stream new-directives -+ orig-args new-args))))) - args)) - - -@@ -639,11 +641,12 @@ - (values `(write-string ,directive stream) - more-directives)) - (format-directive -- (let ((expander -- (aref *format-directive-expanders* -- (char-code (format-directive-character directive)))) -- (*default-format-error-offset* -- (1- (format-directive-end directive)))) -+ (let* ((code (char-code (format-directive-character directive))) -+ (expander -+ (and (< code +format-directive-limit+) -+ (svref *format-directive-expanders* code))) -+ (*default-format-error-offset* -+ (1- (format-directive-end directive)))) - (if expander - (funcall expander directive more-directives) - (error 'format-error diff --git a/build/pkgs/ecl/patches/skip_makeinfo_test.patch b/build/pkgs/ecl/patches/skip_makeinfo_test.patch new file mode 100644 index 00000000000..15387feac49 --- /dev/null +++ b/build/pkgs/ecl/patches/skip_makeinfo_test.patch @@ -0,0 +1,59 @@ +diff --git a/src/configure b/src/configure +index beca5e5b..103f4102 100755 +--- a/src/configure ++++ b/src/configure +@@ -5304,8 +5304,8 @@ fi + elif test "${enable_manual}" = "info"; then + as_fn_error $? "Unable to build the manual: install-info not found." "$LINENO" 5 + fi +- fi +- # Extract the first word of "makeinfo", so it can be a program name with args. ++ else ++ # Extract the first word of "makeinfo", so it can be a program name with args. + set dummy makeinfo; ac_word=$2 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 + $as_echo_n "checking for $ac_word... " >&6; } +@@ -5345,11 +5345,12 @@ $as_echo "no" >&6; } + fi + + +- if test "x${MAKEINFO}" = "x"; then +- if test "${enable_manual}" = "auto"; then +- enable_manual=no +- else +- as_fn_error $? "Unable to build the manual: makeinfo not found." "$LINENO" 5 ++ if test "x${MAKEINFO}" = "x"; then ++ if test "${enable_manual}" = "auto"; then ++ enable_manual=no ++ else ++ as_fn_error $? "Unable to build the manual: makeinfo not found." "$LINENO" 5 ++ fi + fi + fi + fi +diff --git a/src/configure.ac b/src/configure.ac +index 0184a182..4e615408 100644 +--- a/src/configure.ac ++++ b/src/configure.ac +@@ -359,13 +359,14 @@ if test "${enable_manual}" != "no"; then + elif test "${enable_manual}" = "info"; then + AC_MSG_ERROR([Unable to build the manual: install-info not found.]) + fi +- fi +- AC_PATH_PROG([MAKEINFO], [makeinfo], []) +- if test "x${MAKEINFO}" = "x"; then +- if test "${enable_manual}" = "auto"; then +- enable_manual=no +- else +- AC_MSG_ERROR([Unable to build the manual: makeinfo not found.]) ++ else ++ AC_PATH_PROG([MAKEINFO], [makeinfo], []) ++ if test "x${MAKEINFO}" = "x"; then ++ if test "${enable_manual}" = "auto"; then ++ enable_manual=no ++ else ++ AC_MSG_ERROR([Unable to build the manual: makeinfo not found.]) ++ fi + fi + fi + fi diff --git a/build/pkgs/ecl/patches/src/implib.patch b/build/pkgs/ecl/patches/src/implib.patch index 2d612fb8349..03137b9afcf 100644 --- a/build/pkgs/ecl/patches/src/implib.patch +++ b/build/pkgs/ecl/patches/src/implib.patch @@ -1,27 +1,28 @@ -diff -Naur a/src/Makefile.in b/src/Makefile.in ---- a/src/Makefile.in 2016-05-11 13:10:51.816673752 +1200 -+++ b/src/Makefile.in 2016-05-11 13:33:52.743812049 +1200 -@@ -184,10 +184,14 @@ +diff --git a/src/Makefile.in b/src/Makefile.in +index 18f0af21f..cc7edf043 100644 +--- a/src/Makefile.in ++++ b/src/Makefile.in +@@ -220,10 +220,14 @@ install: if test -s $$i ; then \ if echo $$i | grep dll; then \ $(INSTALL_LIBRARY) $$i $(DESTDIR)$(bindir); \ -- fi; \ -- $(INSTALL_LIBRARY) $$i $(DESTDIR)$(libdir); \ + else \ + $(INSTALL_LIBRARY) $$i $(DESTDIR)$(libdir); \ -+ fi \ + fi; \ +- $(INSTALL_LIBRARY) $$i $(DESTDIR)$(libdir); \ fi \ done + if [ "x@IMPLIB_NAME@" != "x" -a -f "@IMPLIB_NAME@" ]; then \ + $(INSTALL_LIBRARY) @IMPLIB_NAME@ $(DESTDIR)$(libdir); \ + fi - if [ "x@SONAME3@" != "x" -a -f "@SONAME3@" ]; then \ + if [ "x@SONAME3@" != "x" -a -f "@SONAME3@" -a "@SONAME@" != "@SONAME3@" ]; then \ ( $(INSTALL_LIBRARY) @SONAME3@ $(DESTDIR)$(libdir) && \ cd $(DESTDIR)$(libdir) && $(RM) -f @SONAME2@ @SONAME1@ @SONAME@ && \ -diff -Naur a/src/aclocal.m4 b/src/aclocal.m4 ---- a/src/aclocal.m4 2016-05-11 13:10:51.816673752 +1200 -+++ b/src/aclocal.m4 2016-05-11 13:27:44.983968151 +1200 -@@ -232,6 +232,8 @@ +diff --git a/src/aclocal.m4 b/src/aclocal.m4 +index 3f905e473..b29296ab9 100644 +--- a/src/aclocal.m4 ++++ b/src/aclocal.m4 +@@ -246,6 +246,8 @@ AC_SUBST(LIBPREFIX)dnl Name components of a statically linked library AC_SUBST(LIBEXT) AC_SUBST(SHAREDEXT)dnl Name components of a dynamically linked library AC_SUBST(SHAREDPREFIX) @@ -30,7 +31,7 @@ diff -Naur a/src/aclocal.m4 b/src/aclocal.m4 AC_SUBST(OBJEXT)dnl These are set by autoconf AC_SUBST(EXEEXT) AC_SUBST(INSTALL_TARGET)dnl Which type of installation: flat directory or unix like. -@@ -241,6 +243,8 @@ +@@ -257,6 +259,8 @@ ECL_GC_DIR=bdwgc ECL_LDRPATH='' SHAREDEXT='so' SHAREDPREFIX='lib' @@ -39,16 +40,16 @@ diff -Naur a/src/aclocal.m4 b/src/aclocal.m4 LIBPREFIX='lib' LIBEXT='a' PICFLAG='-fPIC' -@@ -252,6 +256,8 @@ - clibs='' +@@ -268,6 +272,8 @@ THREAD_OBJ="$THREAD_OBJ threads/process threads/queue threads/mutex threads/cond + clibs='-lm' SONAME='' SONAME_LDFLAGS='' +IMPLIB_NAME='' +IMPLIB_LDFLAGS='' case "${host_os}" in - linux-androideabi) + linux-android*) thehost='android' -@@ -369,10 +375,14 @@ +@@ -385,10 +391,14 @@ case "${host_os}" in shared='yes' THREAD_CFLAGS='-D_THREAD_SAFE' THREAD_LIBS='-lpthread' @@ -66,28 +67,27 @@ diff -Naur a/src/aclocal.m4 b/src/aclocal.m4 PICFLAG='' if test "x$host_cpu" = "xx86_64" ; then # Our GMP library is too old and does not support -@@ -387,11 +397,15 @@ +@@ -405,10 +415,14 @@ case "${host_os}" in enable_threads='yes' THREAD_CFLAGS='-D_THREAD_SAFE' THREAD_GC_FLAGS='--enable-threads=win32' -- SHARED_LDFLAGS='' -- BUNDLE_LDFLAGS='' -+ SHARED_LDFLAGS="-shared -Wl,--enable-auto-image-base ${LDFLAGS}" -+ BUNDLE_LDFLAGS="-shared -Wl,--enable-auto-image-base ${LDFLAGS}" +- SHARED_LDFLAGS="-Wl,--stack,${ECL_DEFAULT_C_STACK_SIZE}" +- BUNDLE_LDFLAGS="-Wl,--stack,${ECL_DEFAULT_C_STACK_SIZE}" ++ SHARED_LDFLAGS="-shared -Wl,--enable-auto-image-base ${LDFLAGS} -Wl,--stack,${ECL_DEFAULT_C_STACK_SIZE}" ++ BUNDLE_LDFLAGS="-shared -Wl,--enable-auto-image-base ${LDFLAGS} -Wl,--stack,${ECL_DEFAULT_C_STACK_SIZE}" SHAREDPREFIX='' SHAREDEXT='dll' -- PICFLAG='' + IMPLIB_PREFIX='lib' + IMPLIB_EXT='dll.a' + IMPLIB_NAME="${IMPLIB_PREFIX}ecl.${IMPLIB_EXT}" + IMPLIB_LDFLAGS="-Wl,--out-implib,${IMPLIB_NAME}" -+ PICFLAG='' + PICFLAG='' INSTALL_TARGET='flatinstall' TCPLIBS='-lws2_32' - ;; -diff -Naur a/src/compile.lsp.in b/src/compile.lsp.in ---- a/src/compile.lsp.in 2016-05-11 13:10:51.888673915 +1200 -+++ b/src/compile.lsp.in 2016-05-11 13:30:13.270308872 +1200 +diff --git a/src/compile.lsp.in b/src/compile.lsp.in +index 7d0a681cf..c01f94ec2 100755 +--- a/src/compile.lsp.in ++++ b/src/compile.lsp.in @@ -61,7 +61,7 @@ ;;; ;;; * Add include path to not yet installed headers, and remove include flag @@ -97,7 +97,7 @@ diff -Naur a/src/compile.lsp.in b/src/compile.lsp.in ;;; with an already installed copy of ECL. ;;; (setq c::*cc-flags* -@@ -143,7 +143,7 @@ +@@ -145,7 +145,7 @@ ;;; ;;; We do not need the -rpath flag for the library, nor -lecl. ;;; @@ -106,10 +106,11 @@ diff -Naur a/src/compile.lsp.in b/src/compile.lsp.in #+msvc "@SHARED_LDFLAGS@ @LDFLAGS@ @STATICLIBS@ @CLIBS@") (c::*cc-flags* (concatenate 'string "-DECL_API -I@true_builddir@/c " c::*cc-flags*)) (extra-args nil)) -diff -Naur a/src/configure.ac b/src/configure.ac ---- a/src/configure.ac 2016-05-11 13:10:51.889673917 +1200 -+++ b/src/configure.ac 2016-05-11 13:19:07.721791089 +1200 -@@ -600,6 +600,20 @@ +diff --git a/src/configure.ac b/src/configure.ac +index 0184a1820..0a834625b 100644 +--- a/src/configure.ac ++++ b/src/configure.ac +@@ -625,6 +625,20 @@ AC_SUBST(SONAME1) AC_SUBST(SONAME) AC_SUBST(SONAME_LDFLAGS) @@ -130,3 +131,6 @@ diff -Naur a/src/configure.ac b/src/configure.ac dnl Related to that, the package version number ECL_VERSION_NUMBER=$(($PACKAGE_MAJOR * 10000 + $PACKAGE_MINOR * 100 + $PACKAGE_LEAST)) AC_SUBST(ECL_VERSION_NUMBER) +-- +2.20.1 + diff --git a/build/pkgs/ecl/patches/windows-fixes.patch b/build/pkgs/ecl/patches/windows-fixes.patch deleted file mode 100644 index f3c366c770c..00000000000 --- a/build/pkgs/ecl/patches/windows-fixes.patch +++ /dev/null @@ -1,21 +0,0 @@ -commit ce828c400054af96f61b41daa56bd08c195ac749 -Author: Fabrizio Fabbri -Date: Wed Mar 2 12:43:36 2016 -0500 - - Win64 and Visual Studio compiler - - * Fix compilation error. - * Fix feature discover on cl compiler. - -diff --git a/src/cmp/cmpmain.lsp b/src/cmp/cmpmain.lsp -index 2eb4008..0b8bbfd 100755 ---- a/src/cmp/cmpmain.lsp -+++ b/src/cmp/cmpmain.lsp -@@ -234,6 +234,7 @@ the environment variable TMPDIR to a different value." template)) - extern \"C\" - #endif - -+ECL_DLLEXPORT - void ~A(cl_object cblock) - { - /* diff --git a/build/pkgs/ecl/patches/write_error.patch b/build/pkgs/ecl/patches/write_error.patch index 7f09fbe9a92..769187fc9ef 100644 --- a/build/pkgs/ecl/patches/write_error.patch +++ b/build/pkgs/ecl/patches/write_error.patch @@ -1,16 +1,17 @@ -diff -Naur ecl-16.1.2.orig/src/c/file.d ecl-16.1.2/src/c/file.d ---- ecl-16.1.2.orig/src/c/file.d 2016-05-11 13:10:51.867673867 +1200 -+++ ecl-16.1.2/src/c/file.d 2016-05-11 14:44:48.121907307 +1200 -@@ -3354,8 +3354,10 @@ - ecl_disable_interrupts(); - do { - out = fwrite(c, sizeof(char), n, IO_STREAM_FILE(strm)); -- } while (out < n && restartable_io_error(strm, "fwrite")); -- ecl_enable_interrupts(); -+ /* Ignore write errors to stderr to avoid an infinite loop */ -+ } while (out < n && (IO_STREAM_FILE(strm) != stderr) && restartable_io_error(strm, "fwrite")); -+ -+ ecl_enable_interrupts(); - return out; +diff --git a/src/c/file.d b/src/c/file.d +index 2d15d50f8..199f24c4f 100755 +--- a/src/c/file.d ++++ b/src/c/file.d +@@ -3536,7 +3536,8 @@ output_stream_write_byte8(cl_object strm, unsigned char *c, cl_index n) + ecl_disable_interrupts(); + do { + out = fwrite(c, sizeof(char), n, IO_STREAM_FILE(strm)); +- } while (out < n && restartable_io_error(strm, "fwrite")); ++ /* Ignore write errors to stderr to avoid an infinite loop */ ++ } while (out < n && (IO_STREAM_FILE(strm) != stderr) && restartable_io_error(strm, "fwrite")); + ecl_enable_interrupts(); + return out; } - +-- +2.20.1 + diff --git a/build/pkgs/ecl/spkg-install b/build/pkgs/ecl/spkg-install deleted file mode 100644 index 81b28acba57..00000000000 --- a/build/pkgs/ecl/spkg-install +++ /dev/null @@ -1,67 +0,0 @@ -cd src - -if [ "x$SAGE_DEBUG" = "xyes" ] ; then - CFLAGS="-g -O0 $CFLAGS" - CXXFLAGS="-g -O0 $CXXFLAGS" -else - CFLAGS="-g -O2 $CFLAGS" - CXXFLAGS="-g -O2 $CXXFLAGS" -fi - -if [ "$UNAME" = "CYGWIN" ]; then - # Some of ECL's sources rely on GNU-isms that are allowed by default on - # most glibcs, but not in newlib; https://trac.sagemath.org/ticket/25057 - CFLAGS="$CFLAGS -D_GNU_SOURCE" - CXXFLAGS="$CXXFLAGS -D_GNU_SOURCE" -fi - -export CFLAGS -export CXXFLAGS -export LDFLAGS - - -# These are all used by GNU to specify compilers. -echo "Using CC=$CC" -echo "Using CXX=$CXX" - -# Flags which may be set. -echo "The following environment variables will be exported" -echo "Using CFLAGS=$CFLAGS" -echo "Using CXXFLAGS=$CXXFLAGS" -echo "Using CPPFLAGS=$CPPFLAGS" -echo "Using LDFLAGS=$LDFLAGS" -echo "configure scripts and/or makefiles might override these later" -echo "" - - -# Use newer version of config.guess and config.sub (see Trac #19732) -cp "$SAGE_ROOT"/config/config.* src - -sdh_configure $SAGE_CONFIGURE_GMP --disable-threads \ - --enable-unicode=yes $ECL_CONFIGURE - -# Before running make we touch build/TAGS so its building process is never triggered -touch build/TAGS - -# Ensure that ECL will not ask interactive questions (for example, when -# pressing CTRL-C during the build) -exec include/ecl. -# This is important when the Sage install is moved, see Trac #14662. -cd "$SAGE_DESTDIR$SAGE_LOCAL/lib/" && rm -f ecl && ln -s ecl-* ecl -if [ $? -ne 0 ]; then - echo >&2 "Error - Failed to create symbolic link to ECL library" - echo >&2 "directory ... exiting" - exit 1 -fi -cd "$SAGE_DESTDIR$SAGE_LOCAL/lib/ecl" && rm -f ecl && ln -s ../../include/ecl ecl -if [ $? -ne 0 ]; then - echo >&2 "Error - Failed to create symbolic link to ECL include" - echo >&2 "directory ... exiting" - exit 1 -fi diff --git a/build/pkgs/ecl/spkg-install.in b/build/pkgs/ecl/spkg-install.in new file mode 100644 index 00000000000..4e2d7aa2763 --- /dev/null +++ b/build/pkgs/ecl/spkg-install.in @@ -0,0 +1,83 @@ +cd src + +if [ "x$SAGE_DEBUG" = "xyes" ] ; then + CFLAGS="-g -O0 $CFLAGS" + CXXFLAGS="-g -O0 $CXXFLAGS" +else + CFLAGS="-g -O2 $CFLAGS" + CXXFLAGS="-g -O2 $CXXFLAGS" +fi + +if [ "$UNAME" = "CYGWIN" ]; then + # Some of ECL's sources rely on GNU-isms that are allowed by default on + # most glibcs, but not in newlib; https://trac.sagemath.org/ticket/25057 + CFLAGS="$CFLAGS -D_GNU_SOURCE" + CXXFLAGS="$CXXFLAGS -D_GNU_SOURCE" +fi + +export CFLAGS +export CXXFLAGS +export LDFLAGS + + +# These are all used by GNU to specify compilers. +echo "Using CC=$CC" +echo "Using CXX=$CXX" + +# Flags which may be set. +echo "The following environment variables will be exported" +echo "Using CFLAGS=$CFLAGS" +echo "Using CXXFLAGS=$CXXFLAGS" +echo "Using CPPFLAGS=$CPPFLAGS" +echo "Using LDFLAGS=$LDFLAGS" +echo "configure scripts and/or makefiles might override these later" +echo "" + + +# Use newer version of config.guess and config.sub (see Trac #19732) +cp "$SAGE_ROOT"/config/config.* src + +if [ x"$SAGE_SPKG_INSTALL_DOCS" != xyes ] ; then + ECL_CONFIGURE="$ECL_CONFIGURE --enable-manual=no" +else + # ECL 2020 needs modern makeinfo + command -v texi2any >/dev/null 2>&1 + if [ $? -ne 0 ]; then # texi2any not found -> makeinfo too old, if present + ECL_CONFIGURE="$ECL_CONFIGURE --enable-manual=no" + else + if makeinfo -c foo 2>&1 | grep -q invalid; then + # makeinfo found but does not support all options that ecl + # likes to use + ECL_CONFIGURE="$ECL_CONFIGURE --enable-manual=no" + fi + fi +fi + +sdh_configure $SAGE_CONFIGURE_GMP --disable-threads \ + --enable-unicode=yes --with-defsystem $ECL_CONFIGURE + +# Before running make we touch build/TAGS so its building process is never triggered +touch build/TAGS + +# Ensure that ECL will not ask interactive questions (for example, when +# pressing CTRL-C during the build) +exec include/ecl. +# This is important when the Sage install is moved, see Trac #14662. +cd "$SAGE_DESTDIR$SAGE_LOCAL/lib/" && rm -f ecl && ln -s ecl-* ecl +if [ $? -ne 0 ]; then + echo >&2 "Error - Failed to create symbolic link to ECL library" + echo >&2 "directory ... exiting" + exit 1 +fi +cd "$SAGE_DESTDIR$SAGE_LOCAL/lib/ecl" && rm -f ecl && ln -s ../../include/ecl ecl +if [ $? -ne 0 ]; then + echo >&2 "Error - Failed to create symbolic link to ECL include" + echo >&2 "directory ... exiting" + exit 1 +fi diff --git a/build/pkgs/ecl/spkg-legacy-uninstall b/build/pkgs/ecl/spkg-legacy-uninstall.in similarity index 100% rename from build/pkgs/ecl/spkg-legacy-uninstall rename to build/pkgs/ecl/spkg-legacy-uninstall.in diff --git a/build/pkgs/ecl/spkg-src b/build/pkgs/ecl/spkg-src index 6333c7f3c8c..37ac03bf27b 100755 --- a/build/pkgs/ecl/spkg-src +++ b/build/pkgs/ecl/spkg-src @@ -53,5 +53,5 @@ autoreconf -ivf cd ../.. tar c ecl-$ECLVERSION | bzip2 -c >"$ECLTARBALL" -sage-fix-pkg-checksums "$ECLTARBALL" +sage --package fix-checksum "$ECLTARBALL" rm -rf ecl-* diff --git a/build/pkgs/eclib/SPKG.rst b/build/pkgs/eclib/SPKG.rst new file mode 100644 index 00000000000..9c24ab8cebc --- /dev/null +++ b/build/pkgs/eclib/SPKG.rst @@ -0,0 +1,38 @@ +eclib +===== + +Description +----------- + +mwrank is a program written in C++ for computing Mordell-Weil groups of +elliptic curves over Q via 2-descent. It is available as source code in +the eclib package, which may be distributed under the GNU General Public +License, version 2, or any later version. + +mwrank is now only distributed as part of eclib. eclib is also included +in Sage, and for most potential users the easiest way to run mwrank is +to install Sage (which also of course gives you much much more). I no +longer provide a source code distribution of mwrank by itself: use eclib +instead. + +License +------- + +eclib is licensed GPL v2+. + + +Upstream Contact +---------------- + +- Author: John Cremona +- Email: john.cremona@gmail.com +- Website: + http://homepages.warwick.ac.uk/staff/J.E.Cremona/mwrank/index.html +- Repository: https://github.com/JohnCremona/eclib + +Dependencies +------------ + +- PARI +- NTL +- FLINT diff --git a/build/pkgs/eclib/SPKG.txt b/build/pkgs/eclib/SPKG.txt deleted file mode 100644 index dfbd7424a9b..00000000000 --- a/build/pkgs/eclib/SPKG.txt +++ /dev/null @@ -1,31 +0,0 @@ -= eclib = - -== Description == - -mwrank is a program written in C++ for computing Mordell-Weil groups of -elliptic curves over Q via 2-descent. It is available as source code in -the eclib package, which may be distributed under the GNU General Public -License, version 2, or any later version. - -mwrank is now only distributed as part of eclib. eclib is also included -in Sage, and for most potential users the easiest way to run mwrank is -to install Sage (which also of course gives you much much more). I no -longer provide a source code distribution of mwrank by itself: use eclib -instead. - -== License == - -eclib is licensed GPL v2+. - -== Upstream Contact == - - * Author: John Cremona - * Email: john.cremona@gmail.com - * Website: http://homepages.warwick.ac.uk/staff/J.E.Cremona/mwrank/index.html - * Repository: https://github.com/JohnCremona/eclib - -== Dependencies == - - * PARI - * NTL - * FLINT diff --git a/build/pkgs/eclib/distros/debian.txt b/build/pkgs/eclib/distros/debian.txt index 23c991aabfa..e0a14faa07d 100644 --- a/build/pkgs/eclib/distros/debian.txt +++ b/build/pkgs/eclib/distros/debian.txt @@ -1 +1,3 @@ libec-dev +# provides the mwrank executable: +eclib-tools diff --git a/build/pkgs/eclib/distros/fedora.txt b/build/pkgs/eclib/distros/fedora.txt index 0f514d35416..7861ae6de2b 100644 --- a/build/pkgs/eclib/distros/fedora.txt +++ b/build/pkgs/eclib/distros/fedora.txt @@ -1 +1 @@ -eclib-devel +eclib eclib-devel diff --git a/build/pkgs/eclib/distros/gentoo.txt b/build/pkgs/eclib/distros/gentoo.txt new file mode 100644 index 00000000000..54cfd8df8ba --- /dev/null +++ b/build/pkgs/eclib/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/eclib[flint] diff --git a/build/pkgs/eclib/spkg-check b/build/pkgs/eclib/spkg-check deleted file mode 100644 index b22b96310db..00000000000 --- a/build/pkgs/eclib/spkg-check +++ /dev/null @@ -1,19 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -# We don't have to set up any environment variables here since the -# Makefiles already have them from 'configure'. (Hopefully.) - -cd src - -echo -echo "Now running eclib's test suite..." -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error: eclib's test suite failed to pass." - exit 1 -fi -echo "eclib's test suite passed without errors." diff --git a/build/pkgs/eclib/spkg-check.in b/build/pkgs/eclib/spkg-check.in new file mode 100644 index 00000000000..41570c5f42b --- /dev/null +++ b/build/pkgs/eclib/spkg-check.in @@ -0,0 +1,6 @@ +# We don't have to set up any environment variables here since the +# Makefiles already have them from 'configure'. (Hopefully.) + +cd src + +$MAKE check diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index 6f8e6973889..8cb8ed01ea7 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,15 +1,9 @@ SAGE_SPKG_CONFIGURE([eclib], [ - AC_REQUIRE([SAGE_SPKG_CONFIGURE_NTL]) - AC_REQUIRE([SAGE_SPKG_CONFIGURE_PARI]) - AC_MSG_CHECKING([installing ntl or pari? ]) - if test x$sage_spkg_install_ntl = xyes -o x$sage_spkg_install_pari = xyes; then - AC_MSG_RESULT([yes; install eclib as well]) - sage_spkg_install_eclib=yes - else + SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl header types.h appeared in v20180710 AC_CHECK_HEADER([eclib/types.h], [ AC_MSG_CHECKING([whether we can link and run a program using eclib]) - ECLIB_SAVED_LIBS=$LIBS + ECLIB_SAVED_LIBS="$LIBS" LIBS="$LIBS -lec" AC_RUN_IFELSE([ AC_LANG_PROGRAM([[#include ] @@ -20,8 +14,10 @@ SAGE_SPKG_CONFIGURE([eclib], [ )], [AC_MSG_RESULT([yes; use eclib from the system])], [ AC_MSG_RESULT([no; install eclib]) sage_spkg_install_eclib=yes - LIBS=$ECLIB_SAVED_LIBS + LIBS="$ECLIB_SAVED_LIBS" ]) ], [sage_spkg_install_eclib=yes]) - fi + AC_PATH_PROG([MWRANK], [mwrank]) + AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) + ]) ]) diff --git a/build/pkgs/eclib/spkg-install b/build/pkgs/eclib/spkg-install.in similarity index 100% rename from build/pkgs/eclib/spkg-install rename to build/pkgs/eclib/spkg-install.in diff --git a/build/pkgs/ecm/SPKG.rst b/build/pkgs/ecm/SPKG.rst new file mode 100644 index 00000000000..e1c84ba21d9 --- /dev/null +++ b/build/pkgs/ecm/SPKG.rst @@ -0,0 +1,64 @@ +ecm +=== + +Description +----------- + +GMP-ECM - Elliptic Curve Method for Integer Factorization + +Sources can be obtained from http://gforge.inria.fr/projects/ecm/ + +License +------- + +LGPL V3+ + + +Upstream Contact +---------------- + +- ecm-discuss@lists.gforge.inria.fr (requires subscription) + +Dependencies +------------ + +- GMP/MPIR (Note: Python is \*not\* required for ordinary builds.) +- GNU patch + + +Special Update/Build Instructions +--------------------------------- + +- GMP-ECM comes with a self-tuning feature; we could support + that as an option ($SAGE_TUNE_*=yes) in the future. + +- ECM currently does not (by itself) use the CC and CFLAGS settings + from 'gmp.h' since we pass (other) options in CFLAGS, and CC is set + by Sage and might got set by the user. We now at least partially fix + that + such that "optimized" code generation options ('-mcpu=...', + '-mtune=...') + are used by gcc. + Of course a user can also manually enable them by setting the + "global" + CFLAGS to e.g. '-march=native' on x86[_64] systems, or '-mcpu=...' + and + '-mtune=...' on other architectures where "native" isn't supported. + Note that this doesn't affect the packages' selection of processor- + specific optimized [assembly] code. + 'spkg-install' already reads the settings from Sage's and also a + system-wide GMP / MPIR now, but doesn't (yet) use all of them. + If SAGE_FAT_BINARY="yes", we should avoid too specific settings of + "-mcpu=...", and perhaps pass a more generic "--host=..." to + 'configure'. (MPIR honors '--enable-fat' to some extent, but this + option isn't used on anything other than x86 / x86_64.) + +- We currently work around a linker bug on MacOS X 10.5 PPC (with + GCC 4.2.1) which breaks 'configure' if debug symbols are enabled. + This \*might\* get fixed in later upstream releases. + +- We could save some space by removing the ``src/build.vc10/`` + directory which + isn't used in Sage. (It gets probably more worth in case also + directories / + files for later versions of Microsoft Visual C get added.) diff --git a/build/pkgs/ecm/SPKG.txt b/build/pkgs/ecm/SPKG.txt deleted file mode 100644 index 78c9dacbfa3..00000000000 --- a/build/pkgs/ecm/SPKG.txt +++ /dev/null @@ -1,47 +0,0 @@ -= ecm = - -== Description == - -GMP-ECM - Elliptic Curve Method for Integer Factorization - -Sources can be obtained from http://gforge.inria.fr/projects/ecm/ - -== License == - -LGPL V3+ - -== Upstream Contact == - - * ecm-discuss@lists.gforge.inria.fr (requires subscription) - -== Dependencies == - - * GMP/MPIR (Note: Python is *not* required for ordinary builds.) - * GNU patch - -== Special Update/Build Instructions == - - * GMP-ECM comes with a self-tuning feature; we could support - that as an option ($SAGE_TUNE_*=yes) in the future. - * ECM currently does not (by itself) use the CC and CFLAGS settings - from 'gmp.h' since we pass (other) options in CFLAGS, and CC is set - by Sage and might got set by the user. We now at least partially fix that - such that "optimized" code generation options ('-mcpu=...', '-mtune=...') - are used by gcc. - Of course a user can also manually enable them by setting the "global" - CFLAGS to e.g. '-march=native' on x86[_64] systems, or '-mcpu=...' and - '-mtune=...' on other architectures where "native" isn't supported. - Note that this doesn't affect the packages' selection of processor- - specific optimized [assembly] code. - 'spkg-install' already reads the settings from Sage's and also a - system-wide GMP / MPIR now, but doesn't (yet) use all of them. - If SAGE_FAT_BINARY="yes", we should avoid too specific settings of - "-mcpu=...", and perhaps pass a more generic "--host=..." to - 'configure'. (MPIR honors '--enable-fat' to some extent, but this - option isn't used on anything other than x86 / x86_64.) - * We currently work around a linker bug on MacOS X 10.5 PPC (with - GCC 4.2.1) which breaks 'configure' if debug symbols are enabled. - This *might* get fixed in later upstream releases. - * We could save some space by removing the `src/build.vc10/` directory which - isn't used in Sage. (It gets probably more worth in case also directories / - files for later versions of Microsoft Visual C get added.) diff --git a/build/pkgs/ecm/distros/fedora.txt b/build/pkgs/ecm/distros/fedora.txt index 7935ec7e17b..45e62aa8032 100644 --- a/build/pkgs/ecm/distros/fedora.txt +++ b/build/pkgs/ecm/distros/fedora.txt @@ -1 +1 @@ -gmp-ecm-devel +gmp-ecm gmp-ecm-devel diff --git a/build/pkgs/ecm/spkg-check b/build/pkgs/ecm/spkg-check deleted file mode 100644 index 2c1d62dca78..00000000000 --- a/build/pkgs/ecm/spkg-check +++ /dev/null @@ -1,25 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - exit 1 -fi - -# Note: Running the test suite should not involve (re)compilation, -# so we don't set CFLAGS et al. here. (Their settings are -# stored in the Makefiles created by 'configure' anyway.) - -cd src - -echo -echo "Now running GMP-ECM's test suite..." - -$MAKE check -# There are potential race conditions in the long check, -# running it serially. -$MAKE -j1 longcheck - -if [ $? -ne 0 ]; then - echo >&2 "Error: GMP-ECM's test suite failed." - exit 1 -fi - -echo "GMP-ECM's test suite passed." diff --git a/build/pkgs/ecm/spkg-check.in b/build/pkgs/ecm/spkg-check.in new file mode 100644 index 00000000000..3a50e8d5092 --- /dev/null +++ b/build/pkgs/ecm/spkg-check.in @@ -0,0 +1,10 @@ +# Note: Running the test suite should not involve (re)compilation, +# so we don't set CFLAGS et al. here. (Their settings are +# stored in the Makefiles created by 'configure' anyway.) + +cd src + +$MAKE check +# There are potential race conditions in the long check, +# running it serially. +$MAKE -j1 longcheck diff --git a/build/pkgs/ecm/spkg-install b/build/pkgs/ecm/spkg-install.in similarity index 100% rename from build/pkgs/ecm/spkg-install rename to build/pkgs/ecm/spkg-install.in diff --git a/build/pkgs/elliptic_curves/SPKG.rst b/build/pkgs/elliptic_curves/SPKG.rst new file mode 100644 index 00000000000..dc0064037e0 --- /dev/null +++ b/build/pkgs/elliptic_curves/SPKG.rst @@ -0,0 +1,35 @@ +elliptic_curves +=============== + +Description +----------- + +Includes two databases: + +- A small subset of John Cremona's database of elliptic curves up + to conductor 10000. + +- William Stein's database of interesting curves + + +Upstream Contact +---------------- + +cremona_mini +~~~~~~~~~~~~ + +- Author: John Cremona +- Email: john.cremona@gmail.com +- Website: http://johncremona.github.io/ecdata/ + +ellcurves +~~~~~~~~~ + +- Author: William Stein +- Email: wstein@gmail.com + +Dependencies +------------ + +- sqlite +- python diff --git a/build/pkgs/elliptic_curves/SPKG.txt b/build/pkgs/elliptic_curves/SPKG.txt deleted file mode 100644 index 12cb9e62ceb..00000000000 --- a/build/pkgs/elliptic_curves/SPKG.txt +++ /dev/null @@ -1,61 +0,0 @@ -= elliptic_curves = - -== Description == - -Includes two databases: - - * A small subset of John Cremona's database of elliptic curves up - to conductor 10000. - * William Stein's database of interesting curves - -== Upstream Contact == - -=== cremona_mini === - - * Author: John Cremona - * Email: john.cremona@gmail.com - * Website: http://johncremona.github.io/ecdata/ - -=== ellcurves === - - * Author: William Stein - * Email: wstein@gmail.com - -== Dependencies == - - * sqlite - * python - -== Changelog == - -=== elliptic_curves-0.8.1 (Frédéric Chapoton, 2019-03-01) === - * #20717: remove macOS hidden files - -=== elliptic_curves-0.8 (Simon Spicer, 2014-10-24) === - * #16773: add more examples of high rank curves - -=== elliptic_curves-0.7 (R. Andrew Ohana, 2012-05-17) === - * #13123: move SAGE_DATA to SAGE_LOCAL/share - -=== elliptic_curves-0.6 (R. Andrew Ohana, 2012-03-27) === - * #12763: fix permissions for the installed files - -=== elliptic_curves-0.5 (Keshav Kini, 2012-03-18) === - * #12694: make the spkg contain a src/ directory and track everything else - -=== elliptic_curves-0.4 (R. Andrew Ohana, 9 March 2012) === - * #12644: reduce the size of the spkg by half - * use os.path.join and file.xreadlines - -=== elliptic_curves-0.3 (R. Andrew Ohana, 9 September 2011) === - * Fixed a potential bug in spkg-install script - * cremona_mini install script now constructs the database from scratch - -=== elliptic_curves-0.2 (R. Andrew Ohana, 1 August 2011) === - * Updated cremona_mini to use a SQLite3 database - * Made SPKG follow guidelines - -=== elliptic_curves-0.1 (unknown, unknown) === - * previous version(s) - * lost to history - diff --git a/build/pkgs/elliptic_curves/spkg-install b/build/pkgs/elliptic_curves/spkg-install.in similarity index 100% rename from build/pkgs/elliptic_curves/spkg-install rename to build/pkgs/elliptic_curves/spkg-install.in diff --git a/build/pkgs/entrypoints/SPKG.rst b/build/pkgs/entrypoints/SPKG.rst new file mode 100644 index 00000000000..e33daf5c768 --- /dev/null +++ b/build/pkgs/entrypoints/SPKG.rst @@ -0,0 +1,23 @@ +entrypoints +=========== + +Description +----------- + +Discover and load entry points from installed packages. + + +Upstream Contact +---------------- + +https://github.com/takluyver/entrypoints + + +Special Update/Build Instructions +--------------------------------- + +Upstream does not provide a source tarball, so the tarball was taken +from github and renamed. + +The source tarball does not contain setup.py, so we put the setup +commands in spkg-install. diff --git a/build/pkgs/entrypoints/SPKG.txt b/build/pkgs/entrypoints/SPKG.txt deleted file mode 100644 index 89b2078a05a..00000000000 --- a/build/pkgs/entrypoints/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= entrypoints = - -== Description == - -Discover and load entry points from installed packages. - -== Upstream Contact == - -https://github.com/takluyver/entrypoints - -== Special Update/Build Instructions == - -Upstream does not provide a source tarball, so the tarball was taken -from github and renamed. - -The source tarball does not contain setup.py, so we put the setup -commands in spkg-install. diff --git a/build/pkgs/entrypoints/dependencies b/build/pkgs/entrypoints/dependencies index 70d522ac04d..15df0c4d6d8 100644 --- a/build/pkgs/entrypoints/dependencies +++ b/build/pkgs/entrypoints/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip configparser +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/entrypoints/setup.py b/build/pkgs/entrypoints/setup.py deleted file mode 100755 index 76bb5e925a2..00000000000 --- a/build/pkgs/entrypoints/setup.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env sage-python -# -# Upstream does not have a setup.py file, so we use this instead -# - -from entrypoints import __version__ as v - -from setuptools import setup -setup(name="entrypoints", version=v, py_modules=["entrypoints"]) diff --git a/build/pkgs/entrypoints/spkg-install b/build/pkgs/entrypoints/spkg-install deleted file mode 100644 index 1ef8c69d080..00000000000 --- a/build/pkgs/entrypoints/spkg-install +++ /dev/null @@ -1,6 +0,0 @@ -# -# Upstream does not have a setup.py file, so we supply one with Sage -# - -mv setup.py src -cd src && sdh_pip_install . diff --git a/build/pkgs/entrypoints/spkg-install.in b/build/pkgs/entrypoints/spkg-install.in new file mode 100644 index 00000000000..cb4ba894442 --- /dev/null +++ b/build/pkgs/entrypoints/spkg-install.in @@ -0,0 +1,8 @@ +cd src + +# Make sure that modern pip uses the generated setup.py +# that is distributed with the PyPI tarball, +# so we do not have to have flit. Trac #29803. +rm -f pyproject.toml + +sdh_pip_install . diff --git a/build/pkgs/enum34/SPKG.txt b/build/pkgs/enum34/SPKG.txt deleted file mode 100644 index 4deba1b8d21..00000000000 --- a/build/pkgs/enum34/SPKG.txt +++ /dev/null @@ -1,9 +0,0 @@ -= enum34 = - -== Description == - -Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4 - -An enumeration is a set of symbolic names (members) bound to unique, -constant values. Within an enumeration, the members can be compared by -identity, and the enumeration itself can be iterated over. diff --git a/build/pkgs/enum34/checksums.ini b/build/pkgs/enum34/checksums.ini deleted file mode 100644 index e43c2a8c833..00000000000 --- a/build/pkgs/enum34/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=enum34-VERSION.tar.gz -sha1=014ef5878333ff91099893d615192c8cd0b1525a -md5=5f13a0841a61f7fc295c514490d120d0 -cksum=3196958404 diff --git a/build/pkgs/enum34/dependencies b/build/pkgs/enum34/dependencies deleted file mode 100644 index d5dab729e18..00000000000 --- a/build/pkgs/enum34/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | pip - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/enum34/package-version.txt b/build/pkgs/enum34/package-version.txt deleted file mode 100644 index 0664a8fd291..00000000000 --- a/build/pkgs/enum34/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -1.1.6 diff --git a/build/pkgs/enum34/spkg-install b/build/pkgs/enum34/spkg-install deleted file mode 100644 index 934d72d76be..00000000000 --- a/build/pkgs/enum34/spkg-install +++ /dev/null @@ -1,6 +0,0 @@ -if [ "$SAGE_PYTHON3" = "yes" ]; then - echo "Not installing enum34: it is only needed for Python version 3.3 or older." - exit 0 -fi - -cd src && sdh_pip_install . diff --git a/build/pkgs/fedora.txt b/build/pkgs/fedora.txt index b309301207e..a545c11f0cd 100644 --- a/build/pkgs/fedora.txt +++ b/build/pkgs/fedora.txt @@ -30,3 +30,5 @@ gcc-c++ # The need for which comes [...] from MPIR's configure script findutils which +# Needed for pcre configure, see https://trac.sagemath.org/ticket/29129: +diffutils diff --git a/build/pkgs/fflas_ffpack/SPKG.rst b/build/pkgs/fflas_ffpack/SPKG.rst new file mode 100644 index 00000000000..8373e0f5cfa --- /dev/null +++ b/build/pkgs/fflas_ffpack/SPKG.rst @@ -0,0 +1,38 @@ +FFLAS/FFPACK +============ + +Description +----------- + +FFLAS-FFPACK is a LGPL-2.1+ source code library for dense linear algebra +over word-size finite fields. + +http://linalg.org/projects/fflas-ffpack + +License +------- + +LGPL V2.1 or later + + +SPKG Repository +--------------- + + https://bitbucket.org/malb/fflas-ffpack-spkg + + +Upstream Contact +---------------- + +- + +Dependencies +------------ + +- Givaro +- ATLAS (non-OSX)/The Accelerate FrameWork (on OSX) + +Patches +------- + +- bash.patch: fix shebang line to "#!/usr/bin/env bash" diff --git a/build/pkgs/fflas_ffpack/SPKG.txt b/build/pkgs/fflas_ffpack/SPKG.txt deleted file mode 100644 index ef918623bcd..00000000000 --- a/build/pkgs/fflas_ffpack/SPKG.txt +++ /dev/null @@ -1,40 +0,0 @@ -= FFLAS/FFPACK = - -== Description == - -FFLAS-FFPACK is a LGPL-2.1+ source code library for dense linear -algebra over word-size finite fields. - -http://linalg.org/projects/fflas-ffpack - -== License == - -LGPL V2.1 or later - -== SPKG Repository == - - https://bitbucket.org/malb/fflas-ffpack-spkg - -== Upstream Contact == - - * - -== Dependencies == - - * Givaro - * ATLAS (non-OSX)/The Accelerate FrameWork (on OSX) - -== Patches == - - * bash.patch: fix shebang line to "#!/usr/bin/env bash" - -== Changelog == - -=== fflas_ffpack-2.2.2 (Clement Pernet, 30 July 2016) === - * #13463: add bash.patch, use standard template for SPKG.txt. - -=== fflas_ffpack-1.6.0.p0 (Jeroen Demeyer, 5 February 2013) === - * #13463: add bash.patch, use standard template for SPKG.txt. - -=== fflas_ffpack-1.6.0 (Martin Albrecht, 7 June 2012) === - * #12883 new upstream release diff --git a/build/pkgs/fflas_ffpack/distros/arch.txt b/build/pkgs/fflas_ffpack/distros/arch.txt new file mode 100644 index 00000000000..683f84a42ca --- /dev/null +++ b/build/pkgs/fflas_ffpack/distros/arch.txt @@ -0,0 +1 @@ +fflas-ffpack diff --git a/build/pkgs/fflas_ffpack/distros/conda.txt b/build/pkgs/fflas_ffpack/distros/conda.txt new file mode 100644 index 00000000000..683f84a42ca --- /dev/null +++ b/build/pkgs/fflas_ffpack/distros/conda.txt @@ -0,0 +1 @@ +fflas-ffpack diff --git a/build/pkgs/fflas_ffpack/distros/debian.txt b/build/pkgs/fflas_ffpack/distros/debian.txt new file mode 100644 index 00000000000..683f84a42ca --- /dev/null +++ b/build/pkgs/fflas_ffpack/distros/debian.txt @@ -0,0 +1 @@ +fflas-ffpack diff --git a/build/pkgs/fflas_ffpack/distros/fedora.txt b/build/pkgs/fflas_ffpack/distros/fedora.txt new file mode 100644 index 00000000000..68a5bf50f14 --- /dev/null +++ b/build/pkgs/fflas_ffpack/distros/fedora.txt @@ -0,0 +1 @@ +fflas-ffpack-devel diff --git a/build/pkgs/fflas_ffpack/distros/gentoo.txt b/build/pkgs/fflas_ffpack/distros/gentoo.txt new file mode 100644 index 00000000000..021d8770229 --- /dev/null +++ b/build/pkgs/fflas_ffpack/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/fflas-ffpack diff --git a/build/pkgs/fflas_ffpack/distros/opensuse.txt b/build/pkgs/fflas_ffpack/distros/opensuse.txt new file mode 100644 index 00000000000..683f84a42ca --- /dev/null +++ b/build/pkgs/fflas_ffpack/distros/opensuse.txt @@ -0,0 +1 @@ +fflas-ffpack diff --git a/build/pkgs/fflas_ffpack/patches/fix-ksh-pkgconfig.patch b/build/pkgs/fflas_ffpack/patches/fix-ksh-pkgconfig.patch new file mode 100644 index 00000000000..fdaed2eebee --- /dev/null +++ b/build/pkgs/fflas_ffpack/patches/fix-ksh-pkgconfig.patch @@ -0,0 +1,28 @@ +From 33a5ec4977f36ce3a24c9ee824d9dd053b8cea04 Mon Sep 17 00:00:00 2001 +From: Dima Pasechnik +Date: Fri, 8 May 2020 15:55:27 +0100 +Subject: [PATCH 1/1] remove 1st and last file in .pc file + +this causes problem if building with ksh, as they remain, causing a broken .pc file. +--- + fflas-ffpack.pc.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/fflas-ffpack.pc.in b/fflas-ffpack.pc.in +index a2618d6..e34a744 100644 +--- a/fflas-ffpack.pc.in ++++ b/fflas-ffpack.pc.in +@@ -1,4 +1,3 @@ +-/------------------ fflas-ffpack.pc ------------------------ + prefix=@prefix@ + exec_prefix=@prefix@ + libdir=@prefix@/lib +@@ -11,4 +10,3 @@ Version: @VERSION@ + Requires: givaro >= 4.0.3 + Libs: @PARLIBS@ @PRECOMPILE_LIBS@ @BLAS_LIBS@ + Cflags: -I@includedir@ @BLAS_CFLAGS@ @PARFLAGS@ @PRECOMPILE_FLAGS@ @REQUIRED_FLAGS@ +-\------------------------------------------------------- +\ No newline at end of file +-- +2.26.2 + diff --git a/build/pkgs/fflas_ffpack/spkg-check b/build/pkgs/fflas_ffpack/spkg-check deleted file mode 100644 index c3374f64ad6..00000000000 --- a/build/pkgs/fflas_ffpack/spkg-check +++ /dev/null @@ -1,7 +0,0 @@ -cd src -sdh_make check - -if [ $? -ne 0 ]; then - cat tests/test-suite.log - exit 1 -fi diff --git a/build/pkgs/fflas_ffpack/spkg-check.in b/build/pkgs/fflas_ffpack/spkg-check.in new file mode 100644 index 00000000000..4b547d8d9da --- /dev/null +++ b/build/pkgs/fflas_ffpack/spkg-check.in @@ -0,0 +1,9 @@ +cd src +# Do not use sdh_make or sdh_make_check here because we are doing our own error +# handling below +${MAKE:-make} check + +if [ $? -ne 0 ]; then + cat tests/test-suite.log + exit 1 +fi diff --git a/build/pkgs/fflas_ffpack/spkg-configure.m4 b/build/pkgs/fflas_ffpack/spkg-configure.m4 new file mode 100644 index 00000000000..f85295a7767 --- /dev/null +++ b/build/pkgs/fflas_ffpack/spkg-configure.m4 @@ -0,0 +1,30 @@ +SAGE_SPKG_CONFIGURE([fflas_ffpack], [ + # fflas-lapack uses whatever multi-precision library givaro uses, + # either gmp or mpir. + SAGE_SPKG_DEPCHECK([atlas givaro gmp mpir openblas], [ + # If our dependencies come from the system, then we can use + # the system fflas-ffpack, too. Use pkg-config to find a + # recentish version, if there is one. + PKG_CHECK_MODULES([FFLAS_FFPACK], + [fflas-ffpack >= 2.4.0], + [sage_spkg_install_fflas_ffpack=no], + [sage_spkg_install_fflas_ffpack=yes]) + ]) +],[],[],[ + # Run this AFTER the check above, unconditionally, so that in particular + # it gets run if the user passes --without-system-fflas-ffpack to the + # ./configure script. + AS_IF([test "x$sage_spkg_install_fflas_ffpack" = "xyes"],[ + dnl https://github.com/linbox-team/fflas-ffpack/blob/master/macros/instr_set.m4 + dnl discovers these flags from the processor but fails to check whether + dnl compiler (and assembler) actually support these instruction sets. + + AX_CHECK_COMPILE_FLAG([-mavx512f -mavx512vl -mavx512dq], [], [ + AS_VAR_APPEND([SAGE_CONFIGURE_FFLAS_FFPACK], [" --disable-avx512f --disable-avx512vl --disable-avx512dq"]) + ]) + m4_foreach([ISFLAG], [fma, fma4], [ + AX_CHECK_COMPILE_FLAG([-m]ISFLAG, [], [AS_VAR_APPEND]([SAGE_CONFIGURE_FFLAS_FFPACK], [" --disable-]ISFLAG[ "])) + ]) + AC_SUBST([SAGE_CONFIGURE_FFLAS_FFPACK]) + ]) +]) diff --git a/build/pkgs/fflas_ffpack/spkg-install b/build/pkgs/fflas_ffpack/spkg-install deleted file mode 100644 index 791a7401099..00000000000 --- a/build/pkgs/fflas_ffpack/spkg-install +++ /dev/null @@ -1,43 +0,0 @@ -cd src - -if [ "$LINBOX_BLAS" != "" ]; then - echo "Using environment variable LINBOX_BLAS=$LINBOX_BLAS" -else - LINBOX_BLAS="$(pkg-config --libs blas cblas)" - BLAS_CFLAGS="$(pkg-config --cflags blas cblas)" - if [ "$BLAS_CFLAGS" != "" ]; then - LINBOX_BLAS_CFLAGS="--with-blas-cflags=$BLAS_CFLAGS" - else - LINBOX_BLAS_CFLAGS="" - fi -fi - -echo "*************************************************" -echo "Using --with-blas-libs='$LINBOX_BLAS' '$LINBOX_BLAS_CFLAGS'" -echo "*************************************************" - -# If SAGE_FAT_BINARY is set, disable all processor-specific optimizations -if [ "$SAGE_FAT_BINARY" = yes ]; then - FFLAS_FFPACK_CONFIGURE="--disable-sse --disable-sse2 --disable-sse3 --disable-ssse3 --disable-sse41 --disable-sse42 --disable-fma --disable-fma4 --disable-avx --disable-avx2 --disable-avx512f --disable-avx512dq --disable-avx512vl $FFLAS_FFPACK_CONFIGURE" -fi - -# Need to use 'bash' for configure, see -# https://trac.sagemath.org/ticket/23451 -if [ -z "$CONFIG_SHELL" ]; then - export CONFIG_SHELL=`command -v bash` -fi - -# We disable openmp because of build failures, see -# http://trac.sagemath.org/ticket/17635#comment:67 -sdh_configure --with-default="$SAGE_LOCAL" --with-blas-libs="$LINBOX_BLAS" \ - "$LINBOX_BLAS_CFLAGS" --disable-static \ - --enable-precompilation $FFLAS_FFPACK_CONFIGURE -sdh_make - -$MAKE autotune -if [ $? -ne 0 ]; then - echo >&2 "Error tuning fflas-ffpack" - exit 1 -fi - -sdh_make_install diff --git a/build/pkgs/fflas_ffpack/spkg-install.in b/build/pkgs/fflas_ffpack/spkg-install.in new file mode 100644 index 00000000000..55684dd8a5f --- /dev/null +++ b/build/pkgs/fflas_ffpack/spkg-install.in @@ -0,0 +1,45 @@ +cd src + +if [ "$LINBOX_BLAS" != "" ]; then + echo "Using environment variable LINBOX_BLAS=$LINBOX_BLAS" +else + LINBOX_BLAS="$(pkg-config --libs blas cblas)" + BLAS_CFLAGS="$(pkg-config --cflags blas cblas)" + if [ "$BLAS_CFLAGS" != "" ]; then + LINBOX_BLAS_CFLAGS="--with-blas-cflags=$BLAS_CFLAGS" + else + LINBOX_BLAS_CFLAGS="" + fi +fi + +echo "*************************************************" +echo "Using --with-blas-libs='$LINBOX_BLAS' '$LINBOX_BLAS_CFLAGS'" +echo "*************************************************" + +FFLAS_FFPACK_CONFIGURE="$SAGE_CONFIGURE_FFLAS_FFPACK $FFLAS_FFPACK_CONFIGURE" + +# If SAGE_FAT_BINARY is set, disable all processor-specific optimizations +if [ "$SAGE_FAT_BINARY" = yes ]; then + FFLAS_FFPACK_CONFIGURE="--disable-sse --disable-sse2 --disable-sse3 --disable-ssse3 --disable-sse41 --disable-sse42 --disable-fma --disable-fma4 --disable-avx --disable-avx2 --disable-avx512f --disable-avx512dq --disable-avx512vl $FFLAS_FFPACK_CONFIGURE" +fi + +# Need to use 'bash' for configure, see +# https://trac.sagemath.org/ticket/23451 +if [ -z "$CONFIG_SHELL" ]; then + export CONFIG_SHELL=`command -v bash` +fi + +# We disable openmp because of build failures, see +# http://trac.sagemath.org/ticket/17635#comment:67 +sdh_configure --with-default="$SAGE_LOCAL" --with-blas-libs="$LINBOX_BLAS" \ + "$LINBOX_BLAS_CFLAGS" --disable-static \ + --enable-precompilation $FFLAS_FFPACK_CONFIGURE +sdh_make + +$MAKE autotune +if [ $? -ne 0 ]; then + echo >&2 "Error tuning fflas-ffpack" + exit 1 +fi + +sdh_make_install diff --git a/build/pkgs/flask/SPKG.txt b/build/pkgs/flask/SPKG.txt deleted file mode 100644 index cf46f1da69e..00000000000 --- a/build/pkgs/flask/SPKG.txt +++ /dev/null @@ -1,8 +0,0 @@ -= Flask = - -== Description == - -A microframework based on Werkzeug, Jinja2 and good intentions - -Flask is a microframework for Python based on Werkzeug, Jinja 2 and good -intentions. And before you ask: It’s BSD licensed! diff --git a/build/pkgs/flask/checksums.ini b/build/pkgs/flask/checksums.ini deleted file mode 100644 index f9167d09e4a..00000000000 --- a/build/pkgs/flask/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=Flask-VERSION.tar.gz -sha1=d3d078262b053f4438e2ed3fd6f9b923c2c92172 -md5=378670fe456957eb3c27ddaef60b2b24 -cksum=2901487846 diff --git a/build/pkgs/flask/dependencies b/build/pkgs/flask/dependencies deleted file mode 100644 index 4c9f9037ab5..00000000000 --- a/build/pkgs/flask/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | pip werkzeug jinja2 itsdangerous - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask/package-version.txt b/build/pkgs/flask/package-version.txt deleted file mode 100644 index 1bb0c37e72a..00000000000 --- a/build/pkgs/flask/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -0.10.1.p0 diff --git a/build/pkgs/flask_autoindex/SPKG.txt b/build/pkgs/flask_autoindex/SPKG.txt deleted file mode 100644 index 6ef8325067a..00000000000 --- a/build/pkgs/flask_autoindex/SPKG.txt +++ /dev/null @@ -1,9 +0,0 @@ -= Flask-AutoIndex = - -== Description == - -The mod_autoindex for Flask - -Flask-AutoIndex generates an index page for your Flask application -automatically. The result just like mod_autoindex, but the look is more -awesome! diff --git a/build/pkgs/flask_autoindex/checksums.ini b/build/pkgs/flask_autoindex/checksums.ini deleted file mode 100644 index c9e98fd8be0..00000000000 --- a/build/pkgs/flask_autoindex/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=Flask_AutoIndex-VERSION.tar.gz -sha1=7727fe9afd4132ca638281624b089b2d7a1a90b4 -md5=7e3aa3e9ffdd4be1bc3ec95d0400e46e -cksum=1126596374 diff --git a/build/pkgs/flask_autoindex/dependencies b/build/pkgs/flask_autoindex/dependencies deleted file mode 100644 index 4c9fdd39c72..00000000000 --- a/build/pkgs/flask_autoindex/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | pip flask_silk future - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_autoindex/package-version.txt b/build/pkgs/flask_autoindex/package-version.txt deleted file mode 100644 index ddf260918b2..00000000000 --- a/build/pkgs/flask_autoindex/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -0.6.p0 diff --git a/build/pkgs/flask_babel/SPKG.txt b/build/pkgs/flask_babel/SPKG.txt deleted file mode 100644 index f96f6411918..00000000000 --- a/build/pkgs/flask_babel/SPKG.txt +++ /dev/null @@ -1,6 +0,0 @@ -= Flask-Babel = - -== Description == - -Adds i18n/l10n support to Flask applications with the help of the Babel -library. diff --git a/build/pkgs/flask_babel/checksums.ini b/build/pkgs/flask_babel/checksums.ini deleted file mode 100644 index 49652906818..00000000000 --- a/build/pkgs/flask_babel/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=Flask_Babel-VERSION.tar.gz -sha1=977d3b152f876e06c215f6bb72616b4ce138fa49 -md5=4762e0392303f464d53cbebedfb87ded -cksum=2286190911 diff --git a/build/pkgs/flask_babel/dependencies b/build/pkgs/flask_babel/dependencies deleted file mode 100644 index 8906930cf79..00000000000 --- a/build/pkgs/flask_babel/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | pip flask speaklater babel - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_babel/package-version.txt b/build/pkgs/flask_babel/package-version.txt deleted file mode 100644 index cdcd38abab9..00000000000 --- a/build/pkgs/flask_babel/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -0.9.p0 diff --git a/build/pkgs/flask_oldsessions/SPKG.txt b/build/pkgs/flask_oldsessions/SPKG.txt deleted file mode 100644 index 2af9b2ebb23..00000000000 --- a/build/pkgs/flask_oldsessions/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= Flask-OldSessions = - -== Description == - -Provides a session class that works like the one in Flask before 0.10. diff --git a/build/pkgs/flask_oldsessions/checksums.ini b/build/pkgs/flask_oldsessions/checksums.ini deleted file mode 100644 index c5fc0993ff0..00000000000 --- a/build/pkgs/flask_oldsessions/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=Flask_OldSessions-VERSION.tar.gz -sha1=1c0bbcd79b4fc626da2fd34ce9b59b2d43f6d81e -md5=3d731d343d5380bb9f502742ad62df50 -cksum=709943870 diff --git a/build/pkgs/flask_oldsessions/dependencies b/build/pkgs/flask_oldsessions/dependencies deleted file mode 100644 index d5dab729e18..00000000000 --- a/build/pkgs/flask_oldsessions/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | pip - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_oldsessions/package-version.txt b/build/pkgs/flask_oldsessions/package-version.txt deleted file mode 100644 index 400afd7090f..00000000000 --- a/build/pkgs/flask_oldsessions/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -0.10.p0 diff --git a/build/pkgs/flask_openid/SPKG.txt b/build/pkgs/flask_openid/SPKG.txt deleted file mode 100644 index 77aa5b1c152..00000000000 --- a/build/pkgs/flask_openid/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= Flask-OpenID = - -== Description == - -OpenID support for Flask diff --git a/build/pkgs/flask_openid/checksums.ini b/build/pkgs/flask_openid/checksums.ini deleted file mode 100644 index a17be5fa13a..00000000000 --- a/build/pkgs/flask_openid/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=Flask_OpenID-VERSION.tar.gz -sha1=18d39e03417cd2b577cd5c1f5c2ac117493f3fef -md5=a40c63df701ec634450d03490ddfb6c1 -cksum=995771756 diff --git a/build/pkgs/flask_openid/dependencies b/build/pkgs/flask_openid/dependencies deleted file mode 100644 index 5367d7a02a6..00000000000 --- a/build/pkgs/flask_openid/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | pip flask python_openid - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_openid/package-version.txt b/build/pkgs/flask_openid/package-version.txt deleted file mode 100644 index 81c03d9313a..00000000000 --- a/build/pkgs/flask_openid/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -1.2.5.p0 diff --git a/build/pkgs/flask_silk/SPKG.txt b/build/pkgs/flask_silk/SPKG.txt deleted file mode 100644 index 92d3009a886..00000000000 --- a/build/pkgs/flask_silk/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= Flask-Silk = - -== Description == - -Adds silk icons to your Flask application or blueprint, or extension. diff --git a/build/pkgs/flask_silk/checksums.ini b/build/pkgs/flask_silk/checksums.ini deleted file mode 100644 index a54c85cdfd6..00000000000 --- a/build/pkgs/flask_silk/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=Flask_Silk-VERSION.tar.gz -sha1=cef42b469c9ebb69a766d0cd33ad27480800d518 -md5=aca545a94063dc4acd21779ea5dde330 -cksum=1557295080 diff --git a/build/pkgs/flask_silk/dependencies b/build/pkgs/flask_silk/dependencies deleted file mode 100644 index 05fe89830e0..00000000000 --- a/build/pkgs/flask_silk/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | pip flask - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_silk/package-version.txt b/build/pkgs/flask_silk/package-version.txt deleted file mode 100644 index cebc1db828b..00000000000 --- a/build/pkgs/flask_silk/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -0.2.p0 diff --git a/build/pkgs/flint/SPKG.rst b/build/pkgs/flint/SPKG.rst new file mode 100644 index 00000000000..c62bc607dd8 --- /dev/null +++ b/build/pkgs/flint/SPKG.rst @@ -0,0 +1,30 @@ +flint +===== + +Description +----------- + +FLINT is a C library for doing number theory, maintained by William +Hart. + +Website: www.flintlib.org + +License +------- + +FLINT is licensed GPL v2+. + + +Upstream Contact +---------------- + +- flint-devel Gougle Group + (http://groups.google.co.uk/group/flint-devel) +- William Hart + +Dependencies +------------ + +- MPIR +- MPFR +- NTL diff --git a/build/pkgs/flint/SPKG.txt b/build/pkgs/flint/SPKG.txt deleted file mode 100644 index 935850e3888..00000000000 --- a/build/pkgs/flint/SPKG.txt +++ /dev/null @@ -1,22 +0,0 @@ -= flint = - -== Description == - -FLINT is a C library for doing number theory, maintained by William Hart. - -Website: www.flintlib.org - -== License == - -FLINT is licensed GPL v2+. - -== Upstream Contact == - - * flint-devel Gougle Group (http://groups.google.co.uk/group/flint-devel) - * William Hart - -== Dependencies == - - * MPIR - * MPFR - * NTL diff --git a/build/pkgs/flint/distros/cygwin.txt b/build/pkgs/flint/distros/cygwin.txt new file mode 100644 index 00000000000..cf5b84dbc05 --- /dev/null +++ b/build/pkgs/flint/distros/cygwin.txt @@ -0,0 +1 @@ +libflint-devel diff --git a/build/pkgs/flint/distros/fedora.txt b/build/pkgs/flint/distros/fedora.txt new file mode 100644 index 00000000000..8df1db2cd08 --- /dev/null +++ b/build/pkgs/flint/distros/fedora.txt @@ -0,0 +1 @@ +flint flint-devel diff --git a/build/pkgs/flint/distros/gentoo.txt b/build/pkgs/flint/distros/gentoo.txt new file mode 100644 index 00000000000..096cd325f61 --- /dev/null +++ b/build/pkgs/flint/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/flint[ntl] diff --git a/build/pkgs/flint/distros/homebrew.txt b/build/pkgs/flint/distros/homebrew.txt new file mode 100644 index 00000000000..9d130adb880 --- /dev/null +++ b/build/pkgs/flint/distros/homebrew.txt @@ -0,0 +1,3 @@ +## This package depends on ntl, the homebrew package of which we cannot use +## because it is built with NTL_THREADS. See https://trac.sagemath.org/ticket/29339 +# sagemath/science/flint diff --git a/build/pkgs/flint/spkg-check b/build/pkgs/flint/spkg-check deleted file mode 100644 index 58950480739..00000000000 --- a/build/pkgs/flint/spkg-check +++ /dev/null @@ -1,19 +0,0 @@ -############################################################################### -# -# FLINT Sage check script -# -############################################################################### - -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src -$MAKE check AT= QUIET_CC= QUIET_CXX= QUIET_AR= - -if [ $? -ne 0 ]; then - echo >&2 "Error: FLINT failed to pass its test suite." - exit 1 -fi diff --git a/build/pkgs/flint/spkg-check.in b/build/pkgs/flint/spkg-check.in new file mode 100644 index 00000000000..01456ef4e24 --- /dev/null +++ b/build/pkgs/flint/spkg-check.in @@ -0,0 +1,8 @@ +############################################################################### +# +# FLINT Sage check script +# +############################################################################### + +cd src +$MAKE check AT= QUIET_CC= QUIET_CXX= QUIET_AR= diff --git a/build/pkgs/flint/spkg-install b/build/pkgs/flint/spkg-install deleted file mode 100644 index 276d0da6db3..00000000000 --- a/build/pkgs/flint/spkg-install +++ /dev/null @@ -1,30 +0,0 @@ -############################################################################### -# -# FLINT Sage install script -# -############################################################################### -if [ "$SAGE_DEBUG" = "yes" ]; then - echo "Building a debug version of FLINT." - CFLAGS="-O0 -g $CFLAGS"; export CFLAGS - FLINT_TUNE=" $FLINT_TUNE"; export FLINT_TUNE - FLINT_CONFIGURE="--enable-assert $FLINT_CONFIGURE" -fi - -cd src - -echo "Configuring FLINT." -./configure \ - --disable-static \ - --prefix="$SAGE_LOCAL" \ - $SAGE_CONFIGURE_GMP \ - $SAGE_CONFIGURE_MPFR \ - --with-ntl="$SAGE_NTL_PREFIX" \ - $FLINT_CONFIGURE || sdh_die "Error: Failed to configure FLINT." - -sdh_make verbose - -echo "Deleting old FLINT files." -rm -f $SAGE_LOCAL/lib/libflint* -rm -rf $SAGE_LOCAL/include/flint - -sdh_make_install diff --git a/build/pkgs/flint/spkg-install.in b/build/pkgs/flint/spkg-install.in new file mode 100644 index 00000000000..c648fe3ca01 --- /dev/null +++ b/build/pkgs/flint/spkg-install.in @@ -0,0 +1,37 @@ +############################################################################### +# +# FLINT Sage install script +# +############################################################################### +if [ "$SAGE_DEBUG" = "yes" ]; then + echo "Building a debug version of FLINT." + CFLAGS="-O0 -g $CFLAGS"; export CFLAGS + FLINT_TUNE=" $FLINT_TUNE"; export FLINT_TUNE + FLINT_CONFIGURE="--enable-assert $FLINT_CONFIGURE" +fi + +cd src + +echo "Configuring FLINT." +# Trac #29607: We must always supply --with-gmp, --with-mpfr, +# --with-ntl because otherwise FLINT's configure script uses +# /usr/local, which is always wrong. +# This is why we do not use $SAGE_CONFIGURE_GMP etc. here. +# The value $SAGE_LOCAL is always a safe choice even if the library +# is coming from the system and is found using what is in +# LIBRARY_PATH or LDFLAGS etc. +./configure \ + --disable-static \ + --prefix="$SAGE_LOCAL" \ + --with-gmp="$SAGE_LOCAL" \ + --with-mpfr="$SAGE_LOCAL" \ + --with-ntl="$SAGE_LOCAL" \ + $FLINT_CONFIGURE || sdh_die "Error: Failed to configure FLINT." + +sdh_make verbose + +echo "Deleting old FLINT files." +rm -f $SAGE_LOCAL/lib/libflint* +rm -rf $SAGE_LOCAL/include/flint + +sdh_make_install diff --git a/build/pkgs/flintqs/SPKG.rst b/build/pkgs/flintqs/SPKG.rst new file mode 100644 index 00000000000..1387cbd9624 --- /dev/null +++ b/build/pkgs/flintqs/SPKG.rst @@ -0,0 +1,14 @@ +FlintQS +======= + +Description +----------- + +This is William Hart's GPL'd highly optimized multi-polynomial quadratic +sieve for integer factorization: + + http://www.friedspace.com/QS/ + +See also http://www.maths.warwick.ac.uk/~masfaw/preprint.html + +See also the repository: https://github.com/sagemath/FlintQS diff --git a/build/pkgs/flintqs/SPKG.txt b/build/pkgs/flintqs/SPKG.txt deleted file mode 100644 index 037997b5595..00000000000 --- a/build/pkgs/flintqs/SPKG.txt +++ /dev/null @@ -1,9 +0,0 @@ -This is William Hart's GPL'd highly optimized multi-polynomial -quadratic sieve for integer factorization: - - http://www.friedspace.com/QS/ - -See also http://www.maths.warwick.ac.uk/~masfaw/preprint.html - -See also the repository: https://github.com/sagemath/FlintQS - diff --git a/build/pkgs/flintqs/distros/conda.txt b/build/pkgs/flintqs/distros/conda.txt new file mode 100644 index 00000000000..87de35f2830 --- /dev/null +++ b/build/pkgs/flintqs/distros/conda.txt @@ -0,0 +1 @@ +flintqs diff --git a/build/pkgs/flintqs/distros/debian.txt b/build/pkgs/flintqs/distros/debian.txt new file mode 100644 index 00000000000..87de35f2830 --- /dev/null +++ b/build/pkgs/flintqs/distros/debian.txt @@ -0,0 +1 @@ +flintqs diff --git a/build/pkgs/flintqs/distros/gentoo.txt b/build/pkgs/flintqs/distros/gentoo.txt new file mode 100644 index 00000000000..2b06251e947 --- /dev/null +++ b/build/pkgs/flintqs/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/flintqs diff --git a/build/pkgs/flintqs/spkg-configure.m4 b/build/pkgs/flintqs/spkg-configure.m4 new file mode 100644 index 00000000000..8b50a817dee --- /dev/null +++ b/build/pkgs/flintqs/spkg-configure.m4 @@ -0,0 +1,17 @@ +SAGE_SPKG_CONFIGURE([flintqs], [ + # The QuadraticSieve program is the only interface to FlintQS that + # sagelib uses. As a result, we don't need to call SAGE_SPKG_DEPCHECK + # here because there's no possibility for a library conflict. + AC_CHECK_PROG(HAVE_QUADRATICSIEVE, QuadraticSieve, yes, no) + + # If we try to just do the obvious thing and swap the return value + # of AC_CHECK_PROG, then ./configure outputs + # + # checking for QuadraticSieve... no + # + # when QuadraticSieve is found... which is not great. + # + AS_IF([test "x$HAVE_QUADRATICSIEVE" = "xyes"], + [sage_spkg_install_flintqs=no], + [sage_spkg_install_flintqs=yes]) +]) diff --git a/build/pkgs/flintqs/spkg-install b/build/pkgs/flintqs/spkg-install.in similarity index 100% rename from build/pkgs/flintqs/spkg-install rename to build/pkgs/flintqs/spkg-install.in diff --git a/build/pkgs/fplll/SPKG.rst b/build/pkgs/fplll/SPKG.rst new file mode 100644 index 00000000000..f31484c927a --- /dev/null +++ b/build/pkgs/fplll/SPKG.rst @@ -0,0 +1,29 @@ +fplll +===== + +Description +----------- + +fplll contains implementations of several lattice algorithms. The +implementation relies on floating-point orthogonalization, and LLL is +central to the code, hence the name. + +Website: https://github.com/fplll/fplll + +License +------- + +- LGPL V2.1+ + + +Upstream Contact +---------------- + +- Martin Albrecht +- Mailing List https://groups.google.com/forum/#!forum/fplll-devel + +Dependencies +------------ + +- gmp +- mpfr diff --git a/build/pkgs/fplll/SPKG.txt b/build/pkgs/fplll/SPKG.txt deleted file mode 100644 index 5d806daf910..00000000000 --- a/build/pkgs/fplll/SPKG.txt +++ /dev/null @@ -1,21 +0,0 @@ -= fplll = - -== Description == - -fplll contains implementations of several lattice algorithms. The -implementation relies on floating-point orthogonalization, and LLL is -central to the code, hence the name. - -Website: https://github.com/fplll/fplll - -== License == - * LGPL V2.1+ - -== Upstream Contact == - - * Martin Albrecht - * Mailing List https://groups.google.com/forum/#!forum/fplll-devel - -== Dependencies == - * gmp - * mpfr diff --git a/build/pkgs/fplll/checksums.ini b/build/pkgs/fplll/checksums.ini index 371a327766d..af7b3d7f52c 100644 --- a/build/pkgs/fplll/checksums.ini +++ b/build/pkgs/fplll/checksums.ini @@ -1,4 +1,5 @@ tarball=fplll-VERSION.tar.gz -sha1=67b70f4dcbb835025abce879b9acb4500e2f1d2c -md5=e72082af9084c5b2d427977c9b79a602 -cksum=65319984 +sha1=3ab5e269fdf73b6f933b24db656e5c55d83aa690 +md5=31f0f5d2045802d0beb8945dd0fa07f0 +cksum=2692708834 +upstream_url=https://github.com/fplll/fplll/releases/download/VERSION/fplll-VERSION.tar.gz \ No newline at end of file diff --git a/build/pkgs/fplll/debian.txt b/build/pkgs/fplll/debian.txt new file mode 100644 index 00000000000..7def04a6329 --- /dev/null +++ b/build/pkgs/fplll/debian.txt @@ -0,0 +1,2 @@ +libfplll-dev +fplll-tools diff --git a/build/pkgs/fplll/distros/fedora.txt b/build/pkgs/fplll/distros/fedora.txt new file mode 100644 index 00000000000..ccf2e14dfee --- /dev/null +++ b/build/pkgs/fplll/distros/fedora.txt @@ -0,0 +1 @@ +libfplll libfplll-devel diff --git a/build/pkgs/fplll/distros/gentoo.txt b/build/pkgs/fplll/distros/gentoo.txt new file mode 100644 index 00000000000..bff0d457997 --- /dev/null +++ b/build/pkgs/fplll/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/fplll diff --git a/build/pkgs/fplll/package-version.txt b/build/pkgs/fplll/package-version.txt index 84197c89467..74664af7400 100644 --- a/build/pkgs/fplll/package-version.txt +++ b/build/pkgs/fplll/package-version.txt @@ -1 +1 @@ -5.3.2 +5.3.3 diff --git a/build/pkgs/fplll/spkg-check b/build/pkgs/fplll/spkg-check deleted file mode 100644 index eb2927b2f42..00000000000 --- a/build/pkgs/fplll/spkg-check +++ /dev/null @@ -1,21 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -if [ "$UNAME" = "CYGWIN" ]; then - echo "Disable parallel testing on Cygwin" - MAKE="$MAKE -j1" -fi - -echo "Now building and running libfplll test suite..." -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error: The libfplll test suite failed." - exit 1 -fi - -echo "The libfplll test suite passed successfully." diff --git a/build/pkgs/fplll/spkg-check.in b/build/pkgs/fplll/spkg-check.in new file mode 100644 index 00000000000..4ebb7d714c1 --- /dev/null +++ b/build/pkgs/fplll/spkg-check.in @@ -0,0 +1,8 @@ +cd src + +if [ "$UNAME" = "CYGWIN" ]; then + echo "Disable parallel testing on Cygwin" + MAKE="$MAKE -j1" +fi + +$MAKE check diff --git a/build/pkgs/fplll/spkg-configure.m4 b/build/pkgs/fplll/spkg-configure.m4 new file mode 100644 index 00000000000..3f2fdeae81d --- /dev/null +++ b/build/pkgs/fplll/spkg-configure.m4 @@ -0,0 +1,12 @@ +SAGE_SPKG_CONFIGURE([fplll], [ + SAGE_SPKG_DEPCHECK([mpfr], [ + dnl If we're using the system mpfr, use pkgconfig to determine + dnl if there's a usable system copy of fplll. Unless there's + dnl a system that ships fplll without fplll.pc file, falling + dnl back to a manual header/library search is pointless. + PKG_CHECK_MODULES([FPLLL], + [fplll >= 5.3], + [sage_spkg_install_fplll=no], + [sage_spkg_install_fplll=yes]) + ]) +]) diff --git a/build/pkgs/fplll/spkg-install b/build/pkgs/fplll/spkg-install.in similarity index 100% rename from build/pkgs/fplll/spkg-install rename to build/pkgs/fplll/spkg-install.in diff --git a/build/pkgs/fpylll/SPKG.rst b/build/pkgs/fpylll/SPKG.rst new file mode 100644 index 00000000000..a583f5757d8 --- /dev/null +++ b/build/pkgs/fpylll/SPKG.rst @@ -0,0 +1,26 @@ +fpylll +====== + +Description +----------- + +A Python interface for https://github.com/fplll/fplll + +License +------- + +GPL version 2 or later + + +Upstream Contact +---------------- + +https://github.com/fplll/fpylll + +Dependencies +------------ + +- Cython +- fplll +- Sage (optional) +- NumPy (optional) diff --git a/build/pkgs/fpylll/SPKG.txt b/build/pkgs/fpylll/SPKG.txt deleted file mode 100644 index 54c48ca58d3..00000000000 --- a/build/pkgs/fpylll/SPKG.txt +++ /dev/null @@ -1,20 +0,0 @@ -= fpylll = - -== Description == - -A Python interface for https://github.com/fplll/fplll - -== License == - -GPL version 2 or later - -== Upstream Contact == - -https://github.com/fplll/fpylll - -== Dependencies == - -* Cython -* fplll -* Sage (optional) -* NumPy (optional) diff --git a/build/pkgs/fpylll/spkg-install b/build/pkgs/fpylll/spkg-install.in similarity index 100% rename from build/pkgs/fpylll/spkg-install rename to build/pkgs/fpylll/spkg-install.in diff --git a/build/pkgs/freetype/SPKG.rst b/build/pkgs/freetype/SPKG.rst new file mode 100644 index 00000000000..9314b7047b1 --- /dev/null +++ b/build/pkgs/freetype/SPKG.rst @@ -0,0 +1,52 @@ +FreeType +======== + +Description +----------- + +From the documentation: + +FreeType is a software font engine that is designed to be small, +efficient, highly customizable, and portable while capable of +producing high-quality output (glyph images). It can be used in +graphics libraries, display servers, font conversion tools, text image +generation tools, and many other products as well. + +Note that FreeType is a font service and doesn't provide APIs to +perform higher-level features like text layout or graphics processing +(e.g., colored text rendering, ‘hollowing’, etc.). However, it greatly +simplifies these tasks by providing a simple, easy to use, and uniform +interface to access the content of font files. + +Please note that ‘FreeType’ is also called ‘FreeType 2’, to +distinguish it from the old, deprecated ‘FreeType 1’ library, a +predecessor no longer maintained and supported. + +The package in Sage is called freetype (in lowercase). + +License +------- + +- FreeType (BSD-like) +- GNU Public License v2 + +From the documentation: + +FreeType is released under two open-source licenses: our own BSD-like +FreeType License and the GNU Public License, Version 2. It can thus +be used by any kind of projects, be they proprietary or not. + + +Upstream Contact +---------------- + +- home: https://www.freetype.org +- repo: + + - official: http://git.savannah.gnu.org/cgit/freetype + - mirror: https://github.com/aseprite/freetype2/ + +Dependencies +------------ + +See the ``dependencies`` file. diff --git a/build/pkgs/freetype/SPKG.txt b/build/pkgs/freetype/SPKG.txt deleted file mode 100644 index 5c0cc067e8c..00000000000 --- a/build/pkgs/freetype/SPKG.txt +++ /dev/null @@ -1,80 +0,0 @@ -= FreeType = - -== Description == - -From the documentation: - -> FreeType is a software font engine that is designed to be small, -> efficient, highly customizable, and portable while capable of -> producing high-quality output (glyph images). It can be used in -> graphics libraries, display servers, font conversion tools, -> text image generation tools, and many other products as well. - -> Note that FreeType is a font service and doesn't provide APIs to -> perform higher-level features like text layout or graphics processing -> (e.g., colored text rendering, ‘hollowing’, etc.). However, it greatly -> simplifies these tasks by providing a simple, easy to use, and uniform -> interface to access the content of font files. - -> Please note that ‘FreeType’ is also called ‘FreeType 2’, to -> distinguish it from the old, deprecated ‘FreeType 1’ library, -> a predecessor no longer maintained and supported. - -The package in Sage is called freetype (in lowercase). - -== License == - - * FreeType (BSD-like) - * GNU Public License v2 - -From the documentation: - -> FreeType is released under two open-source licenses: our own BSD-like -> FreeType License and the GNU Public License, Version 2. It can thus -> be used by any kind of projects, be they proprietary or not. - -== Upstream Contact == - - * home: https://www.freetype.org - * repo: - * official: http://git.savannah.gnu.org/cgit/freetype - * mirror: https://github.com/aseprite/freetype2/ - -== Dependencies == - -See the `dependencies` file. - -== Releases == - -Old changes are listed below. For more recent history, see the git repository. - -=== freetype-2.5.2.p0 (Emmanuel Charpentier, December 21st 2013) === - * #15561: mindless drop-in-place of the current upstream source. - * Added the license information in the present file. - * Minor patch to libpng in order to work around a double setjmp setup. - * buil/deps : freetype depends on libpng to work around a buildbot race condition. - -=== freetype-2.3.5.p4 (Simon King, December 11th 2011) === - * #12131: Use --libdir, to make the package work on openSUSE - -=== freetype-2.3.5.p3 (Mitesh Patel, October 21st 2010) === - * #9221, #9896: Increase the patch level to force reinstallation when - upgrading to Sage 4.6. This works around a problem with moved - Sage installations. - -=== freetype-2.3.5.p2 (David Kirkby, January 2nd 2010) === - * #7138 Ensure -m64 gets added on all platforms, not just OS X - A better fix will be to remove all the hard-coded -m64 junk - and replace by an environment variable CFLAG64, but until - sage-env is updated, that will not work, so I've just left it - as it has always been, but now working on all platforms if the - compiler is gcc. -=== freetype-2.3.5.p1 (Mike Hansen, June 19th, 2009) === - * Applied Peter Jeremy's fix from #5866. - -=== freetype-2.3.5.p0 (Michael Abshoff, May 18th, 2008) === - * add OSX 64 bit build support - -=== freetype-2.3.5 === - * details lost to history - diff --git a/build/pkgs/freetype/checksums.ini b/build/pkgs/freetype/checksums.ini index 0914871d825..15ea9b472ff 100644 --- a/build/pkgs/freetype/checksums.ini +++ b/build/pkgs/freetype/checksums.ini @@ -1,4 +1,5 @@ tarball=freetype-VERSION.tar.bz2 -sha1=220c82062171c513e4017c523d196933c9de4a7d -md5=60ef7d8160cd4bf8cb118ee9d65367ca -cksum=1149809609 +sha1=3296b64ad1e7540289f22e4b6383e26e928b0a20 +md5=c50a3c9e5e62bdc938a6e1598a782947 +cksum=2822306030 +upstream_url=https://download.savannah.gnu.org/releases/freetype/freetype-VERSION.tar.gz diff --git a/build/pkgs/freetype/distros/cygwin.txt b/build/pkgs/freetype/distros/cygwin.txt new file mode 100644 index 00000000000..efdffca75b2 --- /dev/null +++ b/build/pkgs/freetype/distros/cygwin.txt @@ -0,0 +1 @@ +libfreetype-devel diff --git a/build/pkgs/freetype/distros/homebrew.txt b/build/pkgs/freetype/distros/homebrew.txt new file mode 100644 index 00000000000..098479093ff --- /dev/null +++ b/build/pkgs/freetype/distros/homebrew.txt @@ -0,0 +1 @@ +freetype diff --git a/build/pkgs/freetype/distros/slackware.txt b/build/pkgs/freetype/distros/slackware.txt new file mode 100644 index 00000000000..d9f02e6c56d --- /dev/null +++ b/build/pkgs/freetype/distros/slackware.txt @@ -0,0 +1,4 @@ +freetype +harfbuzz +glib +glib2 diff --git a/build/pkgs/freetype/package-version.txt b/build/pkgs/freetype/package-version.txt index dedcc7d4335..8bbb6e406a7 100644 --- a/build/pkgs/freetype/package-version.txt +++ b/build/pkgs/freetype/package-version.txt @@ -1 +1 @@ -2.9.1 +2.10.1 diff --git a/build/pkgs/freetype/spkg-configure.m4 b/build/pkgs/freetype/spkg-configure.m4 index 2f2b9445f99..846bcb368ce 100644 --- a/build/pkgs/freetype/spkg-configure.m4 +++ b/build/pkgs/freetype/spkg-configure.m4 @@ -1,13 +1,9 @@ SAGE_SPKG_CONFIGURE([freetype], [ - AC_REQUIRE([SAGE_SPKG_CONFIGURE_LIBPNG]) - AC_MSG_CHECKING([Installing libpng? ]) - if test x$sage_spkg_install_libpng = xyes; then - AC_MSG_RESULT([yes; install freetype as well]) - sage_spkg_install_freetype=yes - else - AC_MSG_RESULT([no]) - PKG_CHECK_MODULES([FREETYPE], [freetype2 >= 2.4], [], [sage_spkg_install_freetype=yes]) - fi + SAGE_SPKG_DEPCHECK([libpng], [ + dnl freetype versions are libtool's ones, cf trac #30014 + PKG_CHECK_MODULES([FREETYPE], [freetype2 >= 16.1], [], [sage_spkg_install_freetype=yes]) + ]) +], [], [], [ if test x$sage_spkg_install_freetype = xyes; then AC_SUBST(SAGE_FREETYPE_PREFIX, ['$SAGE_LOCAL']) else diff --git a/build/pkgs/freetype/spkg-install b/build/pkgs/freetype/spkg-install.in similarity index 100% rename from build/pkgs/freetype/spkg-install rename to build/pkgs/freetype/spkg-install.in diff --git a/build/pkgs/fricas/SPKG.rst b/build/pkgs/fricas/SPKG.rst new file mode 100644 index 00000000000..cb86c9c65d3 --- /dev/null +++ b/build/pkgs/fricas/SPKG.rst @@ -0,0 +1,23 @@ +fricas +====== + +Description +----------- + +FriCAS is a general purpose computer algebra system. + +License +------- + +Modified BSD license. + + +Upstream Contact +---------------- + +http://fricas.sourceforge.net/ + +Dependencies +------------ + +- ecl diff --git a/build/pkgs/fricas/SPKG.txt b/build/pkgs/fricas/SPKG.txt deleted file mode 100644 index 753f7318252..00000000000 --- a/build/pkgs/fricas/SPKG.txt +++ /dev/null @@ -1,19 +0,0 @@ -= fricas = - -== Description == - -FriCAS is a general purpose computer algebra system. - -== License == - -Modified BSD license. - -== Upstream Contact == - -http://fricas.sourceforge.net/ - -== Dependencies == - - * ecl - - diff --git a/build/pkgs/fricas/spkg-install b/build/pkgs/fricas/spkg-install.in similarity index 100% rename from build/pkgs/fricas/spkg-install rename to build/pkgs/fricas/spkg-install.in diff --git a/build/pkgs/frobby/SPKG.rst b/build/pkgs/frobby/SPKG.rst new file mode 100644 index 00000000000..43d67a408ee --- /dev/null +++ b/build/pkgs/frobby/SPKG.rst @@ -0,0 +1,44 @@ +Frobby +====== + +Description +----------- + +The software package Frobby provides a number of computations on +monomial ideals. The current main feature is the socle of a monomial +ideal, which is largely equivalent to computing the maximal standard +monomials, the Alexander dual or the irreducible decomposition. + +Operations on monomial ideals are much faster than algorithms designed +for ideals in general, which is what makes a specialized library for +these operations on monomial ideals useful. + +License +------- + +- GPL version 2.0 or later + +Maintainers +----------- + +- Bjarke Hammersholt Roune (www.broune.com) + + +Upstream Contact +---------------- + +- Bjarke Hammersholt Roune (www.broune.com) + +Dependencies +------------ + +- GMP built with support for C++ + + +Special Update/Build instructions +--------------------------------- + +Download Frobby at www.broune.com/ and then type "make spkg VER=blah" +which wil create an spkg named frobby-VER.spkg in bin/. The files +related to doing this is in the sage/ sub-directory of the Frobby source +distribution. diff --git a/build/pkgs/frobby/SPKG.txt b/build/pkgs/frobby/SPKG.txt deleted file mode 100644 index bc18b4b46fe..00000000000 --- a/build/pkgs/frobby/SPKG.txt +++ /dev/null @@ -1,55 +0,0 @@ -= Frobby = - -== Description == -The software package Frobby provides a number of computations on -monomial ideals. The current main feature is the socle of a monomial -ideal, which is largely equivalent to computing the maximal standard -monomials, the Alexander dual or the irreducible decomposition. - -Operations on monomial ideals are much faster than algorithms designed -for ideals in general, which is what makes a specialized library for -these operations on monomial ideals useful. - -== License == - * GPL version 2.0 or later - -== Maintainers == - * Bjarke Hammersholt Roune (www.broune.com) - -== Upstream Contact == - * Bjarke Hammersholt Roune (www.broune.com) - -== Dependencies == - * GMP built with support for C++ - -== Special Update/Build instructions == -Download Frobby at www.broune.com/ and then type "make spkg VER=blah" which wil create an spkg -named frobby-VER.spkg in bin/. The files related to doing this is in the sage/ sub-directory of the -Frobby source distribution. - -== Changelog == - -=== frobby-0.9.0.p2 (Dima Pasechnik, Jun 30, 2016) === - * conversion to new-style package - * integration of Macaulay2 patches - * fix linking (rpath) - -=== frobby-0.9.0.p1 (Dima Pasechnik, Sept 7, 2013) === - * added for getpid() - -=== frobby-0.9.0 (Mike Hansen, May 24th, 2012) === - * #13007: Update to 0.9.0. - * Add a patch to the Makefile so that the build succeeds with SAGE_CHECK="yes" - -=== frobby-0.7.6 (Bjarke Hammersholt Roune, May 20th, 2008) === - * Move to Frobby 0.7.6 with Makefile improvements by Michale Abshoff. - -=== frobby-0.7.5.p1 (Michael Abshoff, May 19th, 2008) === - * fix the main Makefile to set include and library directories correctly when some env variables are defined - -=== frobby-0.7.5.p0 (Michael Abshoff, May 19th, 2008) === - * various cleanups, i.e. repo, .hgignore, missing SPKG.txt sections - * move all sources into src - -=== frobby-0.7.5 (Bjarke Hammersholt Roune) === - * initial version diff --git a/build/pkgs/frobby/spkg-check b/build/pkgs/frobby/spkg-check deleted file mode 100644 index 8774de98829..00000000000 --- a/build/pkgs/frobby/spkg-check +++ /dev/null @@ -1,12 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -GMP_INC_DIR="$SAGE_LOCAL/include"; export GMP_INC_DIR -ldflags="-L$SAGE_LOCAL/lib/ -lgmpxx -lgmp"; export ldflags - -cd src/ -$MAKE test MODE=debug # run tests with assertions turned on -$MAKE test MODE=release # test the binaries used by Sage diff --git a/build/pkgs/frobby/spkg-check.in b/build/pkgs/frobby/spkg-check.in new file mode 100644 index 00000000000..f2ae54886bf --- /dev/null +++ b/build/pkgs/frobby/spkg-check.in @@ -0,0 +1,6 @@ +GMP_INC_DIR="$SAGE_LOCAL/include"; export GMP_INC_DIR +ldflags="-L$SAGE_LOCAL/lib/ -lgmpxx -lgmp"; export ldflags + +cd src/ +$MAKE test MODE=debug # run tests with assertions turned on +$MAKE test MODE=release # test the binaries used by Sage diff --git a/build/pkgs/frobby/spkg-install b/build/pkgs/frobby/spkg-install deleted file mode 100644 index 7af177d9fee..00000000000 --- a/build/pkgs/frobby/spkg-install +++ /dev/null @@ -1,31 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -rm -rf "$SAGE_LOCAL/bin/frobby" - -GMP_INC_DIR="$SAGE_LOCAL/include"; export GMP_INC_DIR -ldflags="-Wl,-rpath,$SAGE_LOCAL/lib -L$SAGE_LOCAL/lib/ -lgmpxx -lgmp"; export ldflags - -cd src - -$MAKE MODE=release - -if [ $? -ne 0 ]; then - echo "Error building Frobby." - exit 1 -fi - -if [ ! -f bin/release/frobby ]; then - echo "Frobby executable not found." - exit 1 -fi - -$CP bin/release/frobby "$SAGE_LOCAL/bin/" - -if [ ! -f "$SAGE_LOCAL/bin/frobby" ]; then - echo "Frobby executable not copied." - exit 1 -fi diff --git a/build/pkgs/frobby/spkg-install.in b/build/pkgs/frobby/spkg-install.in new file mode 100644 index 00000000000..1a97cc6b682 --- /dev/null +++ b/build/pkgs/frobby/spkg-install.in @@ -0,0 +1,25 @@ +rm -rf "$SAGE_LOCAL/bin/frobby" + +GMP_INC_DIR="$SAGE_LOCAL/include"; export GMP_INC_DIR +ldflags="-Wl,-rpath,$SAGE_LOCAL/lib -L$SAGE_LOCAL/lib/ -lgmpxx -lgmp"; export ldflags + +cd src + +$MAKE MODE=release + +if [ $? -ne 0 ]; then + echo "Error building Frobby." + exit 1 +fi + +if [ ! -f bin/release/frobby ]; then + echo "Frobby executable not found." + exit 1 +fi + +$CP bin/release/frobby "$SAGE_LOCAL/bin/" + +if [ ! -f "$SAGE_LOCAL/bin/frobby" ]; then + echo "Frobby executable not copied." + exit 1 +fi diff --git a/build/pkgs/functools32/SPKG.txt b/build/pkgs/functools32/SPKG.txt deleted file mode 100644 index 8cab3c8ca44..00000000000 --- a/build/pkgs/functools32/SPKG.txt +++ /dev/null @@ -1,18 +0,0 @@ -= functools32 = - -== Description == - -Backport of the functools module from Python 3.2.3 for use on 2.7 and PyPy. - -== License == - -Python Software Foundation License - -== Upstream Contact == - -Home page: https://pypi.python.org/pypi/functools32 - -== Dependencies == - -Python, Setuptools - diff --git a/build/pkgs/functools32/checksums.ini b/build/pkgs/functools32/checksums.ini deleted file mode 100644 index 7357e795b7d..00000000000 --- a/build/pkgs/functools32/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=functools32-VERSION.tar.gz -sha1=a520082a56af52c7af8d2d1390856bf1a0d755df -md5=09f24ffd9af9f6cd0f63cb9f4e23d4b2 -cksum=3970476845 diff --git a/build/pkgs/functools32/dependencies b/build/pkgs/functools32/dependencies deleted file mode 100644 index 2573414ce8a..00000000000 --- a/build/pkgs/functools32/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | setuptools pip - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/functools32/package-version.txt b/build/pkgs/functools32/package-version.txt deleted file mode 100644 index bab4980050f..00000000000 --- a/build/pkgs/functools32/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -3.2.3-2.p0 diff --git a/build/pkgs/functools32/spkg-install b/build/pkgs/functools32/spkg-install deleted file mode 100644 index 1a768c61ce2..00000000000 --- a/build/pkgs/functools32/spkg-install +++ /dev/null @@ -1,19 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -if [ "$SAGE_PYTHON3" = yes ]; then - echo "Skipping functools32 since it is not necessary on Python 3" - exit 0 -fi - -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing functools32 ... exiting" - exit 1 -fi diff --git a/build/pkgs/future/SPKG.txt b/build/pkgs/future/SPKG.txt deleted file mode 100644 index c3fd24e9b50..00000000000 --- a/build/pkgs/future/SPKG.txt +++ /dev/null @@ -1,27 +0,0 @@ -= future = - -== Description == - -Clean single-source support for Python 3 and 2 - -future is the missing compatibility layer between Python 2 and Python -3. It allows you to use a single, clean Python 3.x-compatible codebase -to support both Python 2 and Python 3 with minimal overhead. - -It is designed to be used as follows: - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from builtins import ( - bytes, dict, int, list, object, range, str, - ascii, chr, hex, input, next, oct, open, - pow, round, super, - filter, map, zip) - -followed by predominantly standard, idiomatic Python 3 code that then -runs similarly on Python 2.6/2.7 and Python 3.3+. - -The imports have no effect on Python 3. On Python 2, they shadow the -corresponding builtins, which normally have different semantics on -Python 3 versus 2, to provide their Python 3 semantics. - diff --git a/build/pkgs/future/checksums.ini b/build/pkgs/future/checksums.ini deleted file mode 100644 index 2e6dd115e6c..00000000000 --- a/build/pkgs/future/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=future-VERSION.tar.gz -sha1=7abd068d09c637f4fa5fb2df3d90cc9607523aee -md5=e42113b4b72fabb5273ff88417104913 -cksum=2947769885 diff --git a/build/pkgs/future/dependencies b/build/pkgs/future/dependencies deleted file mode 100644 index 2573414ce8a..00000000000 --- a/build/pkgs/future/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | setuptools pip - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/future/package-version.txt b/build/pkgs/future/package-version.txt deleted file mode 100644 index 7cca7711a0d..00000000000 --- a/build/pkgs/future/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -0.17.1 diff --git a/build/pkgs/future/spkg-install b/build/pkgs/future/spkg-install deleted file mode 100644 index edc39fa5930..00000000000 --- a/build/pkgs/future/spkg-install +++ /dev/null @@ -1,7 +0,0 @@ -cd src - -sdh_pip_install . -if [ $? -ne 0 ]; then - echo "Error installing future ... exiting" - exit 1 -fi diff --git a/build/pkgs/gambit/SPKG.rst b/build/pkgs/gambit/SPKG.rst new file mode 100644 index 00000000000..d40cbdd1637 --- /dev/null +++ b/build/pkgs/gambit/SPKG.rst @@ -0,0 +1,30 @@ +gambit +====== + +Description +----------- + +Gambit is a set of software tools for doing computation on finite, +noncooperative games. The Gambit Project was founded in the mid-1980s by +Richard McKelvey at the California Institute of Technology. + +License +------- + +GPL v2+ + + +Upstream Contact +---------------- + +- Website: http://www.gambit-project.org/ +- Mailing List: http://sourceforge.net/p/gambit/mailman/gambit-devel/ + +Dependencies +------------ + +- python +- cython +- setuptools +- IPython +- scipy diff --git a/build/pkgs/gambit/SPKG.txt b/build/pkgs/gambit/SPKG.txt deleted file mode 100644 index 01710e57d06..00000000000 --- a/build/pkgs/gambit/SPKG.txt +++ /dev/null @@ -1,24 +0,0 @@ -= gambit = - -== Description == - -Gambit is a set of software tools for doing computation on finite, -noncooperative games. The Gambit Project was founded in the mid-1980s by -Richard McKelvey at the California Institute of Technology. - -== License == - -GPL v2+ - -== Upstream Contact == - -* Website: http://www.gambit-project.org/ -* Mailing List: http://sourceforge.net/p/gambit/mailman/gambit-devel/ - -== Dependencies == - -* python -* cython -* setuptools -* IPython -* scipy diff --git a/build/pkgs/gambit/dependencies b/build/pkgs/gambit/dependencies index fa2eafaa220..2040b648774 100644 --- a/build/pkgs/gambit/dependencies +++ b/build/pkgs/gambit/dependencies @@ -1,4 +1,4 @@ -cython | setuptools +cython | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/gambit/spkg-install b/build/pkgs/gambit/spkg-install.in similarity index 100% rename from build/pkgs/gambit/spkg-install rename to build/pkgs/gambit/spkg-install.in diff --git a/build/pkgs/gap/SPKG.rst b/build/pkgs/gap/SPKG.rst new file mode 100644 index 00000000000..6ecef93ba91 --- /dev/null +++ b/build/pkgs/gap/SPKG.rst @@ -0,0 +1,59 @@ +GAP +=== + +Description +----------- + +GAP is a system for computational discrete algebra, with particular +emphasis on Computational Group Theory. GAP provides a programming +language, a library of thousands of functions implementing algebraic +algorithms written in the GAP language as well as large data libraries +of algebraic objects. See also the overview and the description of the +mathematical capabilities. GAP is used in research and teaching for +studying groups and their representations, rings, vector spaces, +algebras, combinatorial structures, and more. The system, including +source, is distributed freely. You can study and easily modify or extend +it for your special use. + +This is a stripped-down version of GAP. The databases, which are +architecture-independent, are in a separate package. + + +Upstream Contact +---------------- + +David Joyner, wdjoyner@gmail.com (on the GAP team, but Steve Linton, +sal@dcs.st-and.ac.uk, is basically the lead developer) + +Dependencies +------------ + +- Readline +- MPIR + + +Special Update/Build Instructions +--------------------------------- + +This is a stripped-down version of GAP. The downloading of the sources +and removal of unneeded parts is done by the script spkg-src. When you +update GAP, please also update and use the spkg-src script. + +- Do we really want to copy everything from the build directory??? + + You need the full GAP tree to compile/install many GAP packages. + +- There's apparently a command missing (in ``spkg-install``) building + the + (HTML?) documentation. Earlier changelog entries as well as the + description + above state the documentation was removed from the upstream + sources... + Since the (pre-)built HTML documentation is currently included, I've + commented out some lines in that part of ``spkg-install``. -leif + +Patches +~~~~~~~ + +- writeandcheck.patch: fix infinite loop in writeandcheck() when + writing an error message fails. diff --git a/build/pkgs/gap/SPKG.txt b/build/pkgs/gap/SPKG.txt deleted file mode 100644 index 021c1578fb1..00000000000 --- a/build/pkgs/gap/SPKG.txt +++ /dev/null @@ -1,47 +0,0 @@ -= GAP = - -== Description == - -GAP is a system for computational discrete algebra, with particular -emphasis on Computational Group Theory. GAP provides a programming -language, a library of thousands of functions implementing algebraic -algorithms written in the GAP language as well as large data libraries -of algebraic objects. See also the overview and the description of the -mathematical capabilities. GAP is used in research and teaching for -studying groups and their representations, rings, vector spaces, -algebras, combinatorial structures, and more. The system, including -source, is distributed freely. You can study and easily modify or -extend it for your special use. - -This is a stripped-down version of GAP. The databases, which are -architecture-independent, are in a separate package. - -== Upstream Contact == - -David Joyner, wdjoyner@gmail.com (on the GAP team, but -Steve Linton, sal@dcs.st-and.ac.uk, is basically the lead developer) - -== Dependencies == - - * Readline - * MPIR - -== Special Update/Build Instructions == - -This is a stripped-down version of GAP. The downloading of the sources -and removal of unneeded parts is done by the script spkg-src. When you -update GAP, please also update and use the spkg-src script. - - * Do we really want to copy everything from the build directory??? - You need the full GAP tree to compile/install many GAP packages. - - * There's apparently a command missing (in `spkg-install`) building the - (HTML?) documentation. Earlier changelog entries as well as the description - above state the documentation was removed from the upstream sources... - Since the (pre-)built HTML documentation is currently included, I've - commented out some lines in that part of `spkg-install`. -leif - -=== Patches === - - * writeandcheck.patch: fix infinite loop in writeandcheck() when - writing an error message fails. diff --git a/build/pkgs/gap/spkg-check b/build/pkgs/gap/spkg-check deleted file mode 100644 index 863cfb88237..00000000000 --- a/build/pkgs/gap/spkg-check +++ /dev/null @@ -1,21 +0,0 @@ -cd src - -make testinstall -if [[ $? -ne 0 ]]; then - echo >&2 "Error running the GAP testsuite." - exit 1 -fi - -LOG=dev/log/testinstall2_* - -echo "================================================================" -echo "Test log:" -cat $LOG -echo "================================================================" - -ERRORS=`grep ^##### $LOG` -if [[ ! -z "$ERRORS" ]]; then - echo >&2 "Error running the GAP testsuite." - exit 1 -fi -echo "The GAP testsuite completed successfully." diff --git a/build/pkgs/gap/spkg-check.in b/build/pkgs/gap/spkg-check.in new file mode 100644 index 00000000000..d9791d33293 --- /dev/null +++ b/build/pkgs/gap/spkg-check.in @@ -0,0 +1,21 @@ +cd src + +# #28728: Fix test failure in tst/testinstall/strings.tst +export LC_CTYPE=en_US.UTF-8 + +make testinstall +if [[ $? -ne 0 ]]; then + exit 1 +fi + +LOG=dev/log/testinstall2_* + +echo "================================================================" +echo "Test log:" +cat $LOG +echo "================================================================" + +ERRORS=`grep ^##### $LOG` +if [[ ! -z "$ERRORS" ]]; then + exit 1 +fi diff --git a/build/pkgs/gap/spkg-install b/build/pkgs/gap/spkg-install.in similarity index 100% rename from build/pkgs/gap/spkg-install rename to build/pkgs/gap/spkg-install.in diff --git a/build/pkgs/gap/spkg-legacy-uninstall b/build/pkgs/gap/spkg-legacy-uninstall.in similarity index 100% rename from build/pkgs/gap/spkg-legacy-uninstall rename to build/pkgs/gap/spkg-legacy-uninstall.in diff --git a/build/pkgs/gap/spkg-prerm b/build/pkgs/gap/spkg-prerm.in similarity index 100% rename from build/pkgs/gap/spkg-prerm rename to build/pkgs/gap/spkg-prerm.in diff --git a/build/pkgs/gap3/SPKG.rst b/build/pkgs/gap3/SPKG.rst new file mode 100644 index 00000000000..929b89867c2 --- /dev/null +++ b/build/pkgs/gap3/SPKG.rst @@ -0,0 +1,86 @@ + +Jean Michel's GAP 3 distribution +================================ + +Description +----------- + +This package installs Jean Michel's pre-packaged GAP3, which is a +minimal GAP3 distribution containing packages that have no equivalent in +GAP4. + +Below is the full description from Jean Michel's webpage (accessed 23 +July 2015). + + A pre-packaged GAP3 with everything you need + + To help people who are just interested in GAP3 because they need a + package + which has not been ported to GAP4, I have prepared an easy-to install + minimal GAP3 distribution containing an up-to-date versions of the + packages: + + anusq, arep, autag, chevie, cryst, dce, grim, matrix, meataxe, + monoid, + nq, pcqa, sisyphos, specht, ve, vkcurve. + + These packages have been chosen since most have no equivalent in + GAP4. They + are autoloaded when starting gap. + + This distribution includes only partial lists of small groups, + 2-groups, + 3-groups, character tables from the Atlas and tables of marks. It + does not + include either the packages: + + anupq, grape, kbmag, xgap, cohomolo, gliss, guava, xmod + + which have some equivalent in GAP4. You can get these extra features + at + + http://www.math.rwth-aachen.de/~Frank.Luebeck/gap/GAP3 + + In this distribution: + +- The on-line help includes the documentation of the included packages. +- The html documentation (htm/index.html) also does. +- The manual (manual.pdf) also does. + +License +------- + +Most parts of the GAP distribution, including the core part of the GAP +system, are distributed under the terms of the GNU General Public +License (see http://www.gnu.org/licenses/gpl.html or the file GPL in the +etc directory of the GAP installation). + + +SPKG Maintainers +---------------- + +- Christian Stump + + +Upstream Contact +---------------- + +Jean Michel +http://webusers.imj-prg.fr/~jean.michel/ + + +Special Update/Build Instructions +--------------------------------- + +The difference between the distributed tarball and Jean Michel's +original tarball also contains the binaries + +Patches +~~~~~~~ + +None + +Dependencies +------------ + +None diff --git a/build/pkgs/gap3/SPKG.txt b/build/pkgs/gap3/SPKG.txt deleted file mode 100644 index aee3bc3fe0f..00000000000 --- a/build/pkgs/gap3/SPKG.txt +++ /dev/null @@ -1,67 +0,0 @@ -= Jean Michel's GAP 3 distribution = - -== Description == - -This package installs Jean Michel's pre-packaged GAP3, which is a minimal GAP3 -distribution containing packages that have no equivalent in GAP4. - -Below is the full description from Jean Michel's webpage -(accessed 23 July 2015). - - A pre-packaged GAP3 with everything you need - - To help people who are just interested in GAP3 because they need a package - which has not been ported to GAP4, I have prepared an easy-to install - minimal GAP3 distribution containing an up-to-date versions of the - packages: - - anusq, arep, autag, chevie, cryst, dce, grim, matrix, meataxe, monoid, - nq, pcqa, sisyphos, specht, ve, vkcurve. - - These packages have been chosen since most have no equivalent in GAP4. They - are autoloaded when starting gap. - - This distribution includes only partial lists of small groups, 2-groups, - 3-groups, character tables from the Atlas and tables of marks. It does not - include either the packages: - - anupq, grape, kbmag, xgap, cohomolo, gliss, guava, xmod - - which have some equivalent in GAP4. You can get these extra features at - - http://www.math.rwth-aachen.de/~Frank.Luebeck/gap/GAP3 - - In this distribution: - - - The on-line help includes the documentation of the included packages. - - The html documentation (htm/index.html) also does. - - The manual (manual.pdf) also does. - -== License == - -Most parts of the GAP distribution, including the core part of the GAP system, -are distributed under the terms of the GNU General Public License (see -http://www.gnu.org/licenses/gpl.html or the file GPL in the etc directory of -the GAP installation). - -== SPKG Maintainers == - -* Christian Stump - -== Upstream Contact == - -Jean Michel -http://webusers.imj-prg.fr/~jean.michel/ - -== Special Update/Build Instructions == - -The difference between the distributed tarball and Jean Michel's -original tarball also contains the binaries - -=== Patches === - -None - -== Dependencies === - -None diff --git a/build/pkgs/gap3/spkg-install b/build/pkgs/gap3/spkg-install.in similarity index 100% rename from build/pkgs/gap3/spkg-install rename to build/pkgs/gap3/spkg-install.in diff --git a/build/pkgs/gap3/spkg-src b/build/pkgs/gap3/spkg-src.in similarity index 100% rename from build/pkgs/gap3/spkg-src rename to build/pkgs/gap3/spkg-src.in diff --git a/build/pkgs/gap_jupyter/SPKG.rst b/build/pkgs/gap_jupyter/SPKG.rst new file mode 100644 index 00000000000..9e46f9e46a6 --- /dev/null +++ b/build/pkgs/gap_jupyter/SPKG.rst @@ -0,0 +1,22 @@ + +jupyter-kernel-gap +================== + +Description +----------- + +Jupyter kernel for GAP + +This wrapper-kernel is a Jupyter kernel for the GAP Computer Algebra +System based on the same ideas as the bash wrapper kernel. + +License +------- + +3-Clause BSD License + + +Upstream Contact +---------------- + +- https://github.com/gap-packages/jupyter-gap diff --git a/build/pkgs/gap_jupyter/SPKG.txt b/build/pkgs/gap_jupyter/SPKG.txt deleted file mode 100644 index 4e136a47c1c..00000000000 --- a/build/pkgs/gap_jupyter/SPKG.txt +++ /dev/null @@ -1,16 +0,0 @@ -= jupyter-kernel-gap = - -== Description == - -Jupyter kernel for GAP - -This wrapper-kernel is a Jupyter kernel for the GAP Computer Algebra System -based on the same ideas as the bash wrapper kernel. - -== License == - -3-Clause BSD License - -== Upstream Contact == - -* https://github.com/gap-packages/jupyter-gap diff --git a/build/pkgs/gap_jupyter/dependencies b/build/pkgs/gap_jupyter/dependencies index c5cdd5d14fb..41133c3ff0a 100644 --- a/build/pkgs/gap_jupyter/dependencies +++ b/build/pkgs/gap_jupyter/dependencies @@ -1 +1 @@ -$(PYTHON) | pip ipython gap +$(PYTHON) | $(PYTHON_TOOLCHAIN) ipython gap diff --git a/build/pkgs/flask/spkg-install b/build/pkgs/gap_jupyter/spkg-install.in similarity index 100% rename from build/pkgs/flask/spkg-install rename to build/pkgs/gap_jupyter/spkg-install.in diff --git a/build/pkgs/gap_packages/SPKG.rst b/build/pkgs/gap_packages/SPKG.rst new file mode 100644 index 00000000000..780d0823563 --- /dev/null +++ b/build/pkgs/gap_packages/SPKG.rst @@ -0,0 +1,148 @@ +gap_packages +============ + +Description +----------- + +Several "official" and "undeposited" GAP packages available from +http://www.gap-system.org/Packages/packages.html + + +Upstream Contact +---------------- + +- Dmitrii Pasechnik, dimpase@gmail.com +- David Joyner, wdjoyner@gmail.com (on the GAP team) +- Steve Linton, sal@dcs.st-and.ac.uk (basically the GAP lead developer) + +Dependencies +------------ + +- GAP (a standard spkg) + +TODO +---- + +The crystallographic group packages are untested/untestable. They rely +on polymake and the dependency "cryst" is missing. This needs to be +cleaned up. + +Notes +----- + +A brief description of each package follows: + +cohomolo - The cohomolo package is a GAP interface to some ``C`` programs +for computing Schur multipliers and covering groups of finite groups and +first and second cohomology groups of finite groups acting on finite +modules. (Author: Max Horn, Markus Pfeiffer) + +CoReLG - Contains functionality for working with real semisimple Lie +algebras. (Author: Heiko Dietrich, Paolo Faccin, Willem Adriaan de +Graaf) + +crime - package to compute the cohomology ring of finite p-groups, +induced maps, and Massey products. (Author: Marcus Bishop) + +cryst - Computing with crystallographic groups (Authors: Bettina Eick, +Franz Gähler, Werner Nickel) + +CTblLib - The GAP Character Table Library (Author: Thomas Breuer) + +DESIGN is a package for classifying, partitioning and studying block +designs. (Author: Leonard H. Soicher) + +FactInt is a package providing routines for factoring integers, in +particular: + +- Pollard's p-1 +- Williams' p+1 +- Elliptic Curves Method (ECM) +- Continued Fraction Algorithm (CFRAC) +- Multiple Polynomial Quadratic Sieve (MPQS) + +(Author: Stefan Kohl) + +GAPDoc is a package containing a definition of a structure for GAP +documentation, based on XML. It also contains conversion programs for +producing text-, DVI-, PDF- or HTML-versions of such documents, with +hyperlinks if possible. (Authors: Frank Luebeck, Max Neunhoeffer) + +GBNP - The GBNP package provides algorithms for computing Grobner bases +of noncommutative polynomials with coefficients from a field implemented +in GAP and with respect to the "total degree first then lexicographical" +ordering. Further provided are some variations, such as a weighted and +truncated version and a tracing facility. The word "algorithm" is to be +interpreted loosely here: in general one cannot expect such an algorithm +to terminate, as it would imply solvability of the word problem for +finitely presented (semi)groups. (Authors: A.M. Cohen, J.W. Knopper) + +GRAPE is a package for computing with graphs and groups, and is +primarily designed for constructing and analysing graphs related to +groups, finite geometries, and designs. (Author: Leonard H. Soicher) + +GUAVA is included here, and with Sage standard. + +HAP (Homological Algebra Programming) is a GAP package providing some +functions for group cohomology computation. (Author: Graham Ellis) + +HAPcryst - an extension package for HAP, which allows for group +cohomology computation for a wider class of groups. (Author: Marc +Roeder) + +hecke - Provides functions for calculating decomposition matrices of +Hecke algebras of the symmetric groups and q-Schur algebras. Hecke is a +port of the GAP 3 package Specht 2.4 to GAP 4. (Author: Dmitriy Traytel) + +LAGUNA - this package provides functionality for calculation of the +normalized unit group of the modular group algebra of the finite p-group +and for investigation of Lie algebra associated with group algebras and +other associative algebras. (Authors :Victor Bovdi, Alexander Konovalov, +Richard Rossmanith, Csaba Schneider) + +liealgdb - A database of Lie algebras (Author: Serena Cicalo', Willem +Adriaan de Graaf, Csaba Schneider) + +LiePRing - Database and algorithms for Lie p-rings (Author: Michael +Vaughan-Lee, Bettina Eick) + +LieRing - contains functionality for working with finitely presented Lie +rings and the Lazard correspondence. (Author: Serena Cicalo', Willem +Adriaan de Graaf) + +loops - Provides researchers in nonassociative algebra with a +computational tool that integrates standard notions of loop theory with +libraries of loops and group-theoretical algorithms of GAP. The package +also expands GAP toward nonassociative structures. (Authors: Gabor Nagy, +Petr Vojtechovsky) + +mapclass - The package calculates the mapping class group orbits for a +given finite group. (Authors: Adam James, Kay Magaard, Sergey +Shpectorov, Helmut Volklein) + +polymake - an interface with the (standalone) polymake program used by +HAPcryst. (Author: Marc Roeder) + +qpa - Quivers and Path Algebras provides data structures and algorithms +for doing computations with finite dimensional quotients of path +algebras, and finitely generated modules over such algebras. The current +version of the QPA package has data structures for quivers, quotients of +path algebras, and modules, homomorphisms and complexes of modules over +quotients of path algebras. (Authors: Edward Green, Oeyvind Solberg) + +quagroup - Contains functionality for working with quantized enveloping +algebras of finite-dimensional semisimple Lie algebras. (Author: Willem +Adriaan de Graaf) + +repsn - The package provides GAP functions for computing characteristic +zero matrix representations of finite groups. (Author: Vahid Dabbaghian) + +sla - a package for doing computations with simple Lie algebras (Author: +Willem Adriaan de Graaf) + +SONATA ("System Of Nearrings And Their Applications") is a package which +constructs finite nearrings and related objects. (Authors: Erhard +Aichinger, Franz Binder, Jürgen Ecker, Peter Mayr, Christof Noebauer) + +TORIC is a GAP package for computing with toric varieties. (Author: +David Joyner) diff --git a/build/pkgs/gap_packages/SPKG.txt b/build/pkgs/gap_packages/SPKG.txt deleted file mode 100644 index b50a4430aff..00000000000 --- a/build/pkgs/gap_packages/SPKG.txt +++ /dev/null @@ -1,152 +0,0 @@ -= gap_packages = - -== Description == - -Several "official" and "undeposited" GAP packages available from -http://www.gap-system.org/Packages/packages.html - -== Upstream Contact == - - * Dmitrii Pasechnik, dimpase@gmail.com - * David Joyner, wdjoyner@gmail.com (on the GAP team) - * Steve Linton, sal@dcs.st-and.ac.uk (basically the GAP lead developer) - -== Dependencies == - - * GAP (a standard spkg) - -== TODO == - -The crystallographic group packages are untested/untestable. They rely -on polymake and the dependency "cryst" is missing. This needs to be -cleaned up. - -== Notes == - -A brief description of each package follows: - -cohomolo - The cohomolo package is a GAP interface to some -`C' programs for computing Schur multipliers and covering groups -of finite groups and first and second cohomology groups of finite -groups acting on finite modules. -(Author: Max Horn, Markus Pfeiffer) - -CoReLG - Contains functionality for working with real semisimple -Lie algebras. -(Author: Heiko Dietrich, Paolo Faccin, Willem Adriaan de Graaf) - -crime - package to compute the cohomology ring of finite -p-groups, induced maps, and Massey products. -(Author: Marcus Bishop) - -cryst - Computing with crystallographic groups -(Authors: Bettina Eick, Franz Gähler, Werner Nickel) - -CTblLib - The GAP Character Table Library -(Author: Thomas Breuer) - -DESIGN is a package for classifying, partitioning and studying block designs. -(Author: Leonard H. Soicher) - -FactInt is a package providing routines for factoring integers, in particular: - * Pollard's p-1 - * Williams' p+1 - * Elliptic Curves Method (ECM) - * Continued Fraction Algorithm (CFRAC) - * Multiple Polynomial Quadratic Sieve (MPQS) -(Author: Stefan Kohl) - -GAPDoc is a package containing a definition of a structure for -GAP documentation, based on XML. It also contains conversion -programs for producing text-, DVI-, PDF- or HTML-versions of such -documents, with hyperlinks if possible. -(Authors: Frank Luebeck, Max Neunhoeffer) - -GBNP - The GBNP package provides algorithms for computing Grobner bases -of noncommutative polynomials with coefficients from a field implemented -in GAP and with respect to the "total degree first then lexicographical" -ordering. Further provided are some variations, such as a weighted and -truncated version and a tracing facility. The word "algorithm" is to be -interpreted loosely here: in general one cannot expect such an algorithm -to terminate, as it would imply solvability of the word problem for -finitely presented (semi)groups. -(Authors: A.M. Cohen, J.W. Knopper) - -GRAPE is a package for computing with graphs and groups, and is primarily -designed for constructing and analysing graphs related to groups, -finite geometries, and designs. -(Author: Leonard H. Soicher) - -GUAVA is included here, and with Sage standard. - -HAP (Homological Algebra Programming) is a GAP package -providing some functions for group cohomology computation. -(Author: Graham Ellis) - -HAPcryst - an extension package for HAP, which allows for -group cohomology computation for a wider class of groups. -(Author: Marc Roeder) - -hecke - Provides functions for calculating decomposition matrices -of Hecke algebras of the symmetric groups and q-Schur algebras. -Hecke is a port of the GAP 3 package Specht 2.4 to GAP 4. -(Author: Dmitriy Traytel) - -LAGUNA - this package provides functionality for calculation of the -normalized unit group of the modular group algebra of the finite -p-group and for investigation of Lie algebra associated with group -algebras and other associative algebras. -(Authors :Victor Bovdi, Alexander Konovalov, Richard Rossmanith, -Csaba Schneider) - -liealgdb - A database of Lie algebras -(Author: Serena Cicalo', Willem Adriaan de Graaf, Csaba Schneider) - -LiePRing - Database and algorithms for Lie p-rings -(Author: Michael Vaughan-Lee, Bettina Eick) - -LieRing - contains functionality for working with finitely -presented Lie rings and the Lazard correspondence. -(Author: Serena Cicalo', Willem Adriaan de Graaf) - -loops - Provides researchers in nonassociative algebra with a -computational tool that integrates standard notions of loop theory -with libraries of loops and group-theoretical algorithms of GAP. -The package also expands GAP toward nonassociative structures. -(Authors: Gabor Nagy, Petr Vojtechovsky) - -mapclass - The package calculates the mapping class group orbits -for a given finite group. -(Authors: Adam James, Kay Magaard, Sergey Shpectorov, Helmut Volklein) - -polymake - an interface with the (standalone) polymake program -used by HAPcryst. -(Author: Marc Roeder) - -qpa - Quivers and Path Algebras provides data structures and -algorithms for doing computations with finite dimensional quotients -of path algebras, and finitely generated modules over such algebras. -The current version of the QPA package has data structures for quivers, -quotients of path algebras, and modules, homomorphisms and complexes -of modules over quotients of path algebras. -(Authors: Edward Green, Oeyvind Solberg) - -quagroup - Contains functionality for working with quantized -enveloping algebras of finite-dimensional semisimple Lie algebras. -(Author: Willem Adriaan de Graaf) - -repsn - The package provides GAP functions for computing -characteristic zero matrix representations of finite groups. -(Author: Vahid Dabbaghian) - -sla - a package for doing computations with simple Lie algebras -(Author: Willem Adriaan de Graaf) - -SONATA ("System Of Nearrings And Their Applications") is a package -which constructs finite nearrings and related objects. -(Authors: Erhard Aichinger, Franz Binder, Jürgen Ecker, Peter Mayr, -Christof Noebauer) - -TORIC is a GAP package for computing with toric varieties. -(Author: David Joyner) - diff --git a/build/pkgs/gap_packages/spkg-install b/build/pkgs/gap_packages/spkg-install.in similarity index 100% rename from build/pkgs/gap_packages/spkg-install rename to build/pkgs/gap_packages/spkg-install.in diff --git a/build/pkgs/gc/SPKG.rst b/build/pkgs/gc/SPKG.rst new file mode 100644 index 00000000000..e6bf7c35fbf --- /dev/null +++ b/build/pkgs/gc/SPKG.rst @@ -0,0 +1,35 @@ +gc +== + +Description +----------- + +The Boehm-Demers-Weiser conservative garbage collector. + +License +------- + +- Permissive BSD + GPL 2.0+ + + +Upstream Contact +---------------- + +Webpage: http://www.hboehm.info/gc/ Email List: +bdwgc@lists.opendylan.org + +Dependencies +------------ + +None. + + +Special Update/Build Instructions +--------------------------------- + +None. + +Patches +~~~~~~~ + +- cygwin64.patch: let libgc build on Cygwin64. diff --git a/build/pkgs/gc/SPKG.txt b/build/pkgs/gc/SPKG.txt deleted file mode 100644 index fc72c50600f..00000000000 --- a/build/pkgs/gc/SPKG.txt +++ /dev/null @@ -1,25 +0,0 @@ -= gc = - -== Description == - -The Boehm-Demers-Weiser conservative garbage collector. - -== License == - -* Permissive BSD + GPL 2.0+ - -== Upstream Contact == - -Webpage: http://www.hboehm.info/gc/ -Email List: bdwgc@lists.opendylan.org - -== Dependencies == - -None. - -== Special Update/Build Instructions == - -None. - -=== Patches === - * cygwin64.patch: let libgc build on Cygwin64. diff --git a/build/pkgs/gc/checksums.ini b/build/pkgs/gc/checksums.ini index aff1209c759..628ecde3d37 100644 --- a/build/pkgs/gc/checksums.ini +++ b/build/pkgs/gc/checksums.ini @@ -1,4 +1,5 @@ tarball=gc-VERSION.tar.gz -sha1=1a3f91a6ea004dcd1f5fc93defcdb2c2bc0c3941 -md5=cf390c4f7f2556a67ec49e964c86c847 -cksum=2477276198 +sha1=4b8b24534f469b64ff4bc2332a9bdf8bef8bf1d4 +md5=67a5093e2f9f381bd550aa891d00b54b +cksum=121524068 +upstream_url=https://github.com/ivmai/bdwgc/releases/download/vVERSION/gc-VERSION.tar.gz diff --git a/build/pkgs/gc/package-version.txt b/build/pkgs/gc/package-version.txt index b7ade0673a6..50c496d20c6 100644 --- a/build/pkgs/gc/package-version.txt +++ b/build/pkgs/gc/package-version.txt @@ -1 +1 @@ -7.6.4.p0 +8.0.4 diff --git a/build/pkgs/gc/spkg-check b/build/pkgs/gc/spkg-check deleted file mode 100644 index cf388fed060..00000000000 --- a/build/pkgs/gc/spkg-check +++ /dev/null @@ -1,20 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -if [ "$SAGE_DEBUG" = "yes" ]; then - export CFLAGS="-O0 -g $CFLAGS" -else - export CFLAGS="-O2 -g $CFLAGS" -fi - -$MAKE check - -if [ $? -ne 0 ]; then - echo "Error testing BoehmGC." - exit 1 -fi diff --git a/build/pkgs/gc/spkg-check.in b/build/pkgs/gc/spkg-check.in new file mode 100644 index 00000000000..6d620bd7417 --- /dev/null +++ b/build/pkgs/gc/spkg-check.in @@ -0,0 +1,9 @@ +cd src + +if [ "$SAGE_DEBUG" = "yes" ]; then + export CFLAGS="-O0 -g $CFLAGS" +else + export CFLAGS="-O2 -g $CFLAGS" +fi + +$MAKE check diff --git a/build/pkgs/gc/spkg-install b/build/pkgs/gc/spkg-install.in similarity index 100% rename from build/pkgs/gc/spkg-install rename to build/pkgs/gc/spkg-install.in diff --git a/build/pkgs/gcc/SPKG.rst b/build/pkgs/gcc/SPKG.rst new file mode 100644 index 00000000000..0fe63a12e96 --- /dev/null +++ b/build/pkgs/gcc/SPKG.rst @@ -0,0 +1,32 @@ +gcc +=== + +Description +----------- + +The GNU Compiler Collection, including the C, C++ and Fortran compiler. + +License +------- + +GPL version 2 or version 3 + + +Upstream Contact +---------------- + +http://gcc.gnu.org/ + +Dependencies +------------ + +- zlib +- MPIR +- MPFR +- MPC + + +Special Update/Build Instructions +--------------------------------- + +None. diff --git a/build/pkgs/gcc/SPKG.txt b/build/pkgs/gcc/SPKG.txt deleted file mode 100644 index 3af5759c43c..00000000000 --- a/build/pkgs/gcc/SPKG.txt +++ /dev/null @@ -1,24 +0,0 @@ -= gcc = - -== Description == - -The GNU Compiler Collection, including the C, C++ and Fortran compiler. - -== License == - -GPL version 2 or version 3 - -== Upstream Contact == - -http://gcc.gnu.org/ - -== Dependencies == - - * zlib - * MPIR - * MPFR - * MPC - -== Special Update/Build Instructions == - -None. diff --git a/build/pkgs/gcc/build-gcc b/build/pkgs/gcc/build-gcc index e6bdd7fbfd4..1b3c72b1298 100755 --- a/build/pkgs/gcc/build-gcc +++ b/build/pkgs/gcc/build-gcc @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Wrapper script for building GCC used for both the gcc and gfortran SPKGs # Usage: @@ -58,6 +58,8 @@ if [ -n "$LD" -a "$LD" != "ld" ]; then CONFIGURE_LD="--with-ld=$LD" fi +# Use SAGE_CXX_WITHOUT_STD instead of CXX. +# This fixes #29162 (gfortran 9.2.0 compile error on debian-jessie with gcc 4.9.2) ../src/configure \ --prefix="$SAGE_LOCAL" \ --with-local-prefix="$SAGE_LOCAL" \ @@ -67,6 +69,6 @@ fi --disable-multilib \ --disable-nls \ --disable-libitm \ - $GCC_CONFIGURE "$CONFIGURE_AS" "$CONFIGURE_LD" + $GCC_CONFIGURE "$CONFIGURE_AS" "$CONFIGURE_LD" CXX="$SAGE_CXX_WITHOUT_STD" sdh_make BOOT_LDFLAGS="-Wl,-rpath,$SAGE_LOCAL/lib" diff --git a/build/pkgs/gcc/distros/cygwin.txt b/build/pkgs/gcc/distros/cygwin.txt new file mode 100644 index 00000000000..dde4bb418b6 --- /dev/null +++ b/build/pkgs/gcc/distros/cygwin.txt @@ -0,0 +1,3 @@ +gcc-core +gcc-g++ +gcc-fortran diff --git a/build/pkgs/gcc/patches/35_all_glibc-2.31-libsanitizer-1.patch b/build/pkgs/gcc/patches/35_all_glibc-2.31-libsanitizer-1.patch new file mode 100644 index 00000000000..4906ae04511 --- /dev/null +++ b/build/pkgs/gcc/patches/35_all_glibc-2.31-libsanitizer-1.patch @@ -0,0 +1,40 @@ +https://bugs.gentoo.org/708346 + +From ce9568e9e9cf6094be30e748821421e703754ffc Mon Sep 17 00:00:00 2001 +From: Jakub Jelinek +Date: Fri, 8 Nov 2019 19:53:18 +0100 +Subject: [PATCH] backport: re PR sanitizer/92154 (new glibc breaks arm + bootstrap due to libsanitizer) + + Backported from mainline + 2019-10-22 Tamar Christina + + PR sanitizer/92154 + * sanitizer_common/sanitizer_platform_limits_posix.cc: + Cherry-pick compiler-rt revision r375220. + +From-SVN: r277981 +--- + libsanitizer/ChangeLog | 9 +++++++++ + .../sanitizer_common/sanitizer_platform_limits_posix.cc | 6 +++++- + 2 files changed, 14 insertions(+), 1 deletion(-) + +--- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc ++++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc +@@ -1156,8 +1156,12 @@ CHECK_SIZE_AND_OFFSET(ipc_perm, uid); + CHECK_SIZE_AND_OFFSET(ipc_perm, gid); + CHECK_SIZE_AND_OFFSET(ipc_perm, cuid); + CHECK_SIZE_AND_OFFSET(ipc_perm, cgid); +-#if !defined(__aarch64__) || !SANITIZER_LINUX || __GLIBC_PREREQ (2, 21) ++#if (!defined(__aarch64__) || !SANITIZER_LINUX || __GLIBC_PREREQ (2, 21)) && \ ++ !defined(__arm__) + /* On aarch64 glibc 2.20 and earlier provided incorrect mode field. */ ++/* On Arm glibc 2.31 and later provide a different mode field, this field is ++ never used by libsanitizer so we can simply ignore this assert for all glibc ++ versions. */ + CHECK_SIZE_AND_OFFSET(ipc_perm, mode); + #endif + +-- +2.25.0 + diff --git a/build/pkgs/gcc/patches/36_all_glibc-2.31-libsanitizer-2.patch b/build/pkgs/gcc/patches/36_all_glibc-2.31-libsanitizer-2.patch new file mode 100644 index 00000000000..1960a11290f --- /dev/null +++ b/build/pkgs/gcc/patches/36_all_glibc-2.31-libsanitizer-2.patch @@ -0,0 +1,76 @@ +https://bugs.gentoo.org/708346 + +From 75003cdd23c310ec385344e8040d490e8dd6d2be Mon Sep 17 00:00:00 2001 +From: Jakub Jelinek +Date: Fri, 20 Dec 2019 17:58:35 +0100 +Subject: [PATCH] backport: re PR sanitizer/92154 (new glibc breaks arm + bootstrap due to libsanitizer) + + Backported from mainline + 2019-11-26 Jakub Jelinek + + PR sanitizer/92154 + * sanitizer_common/sanitizer_platform_limits_posix.h: Cherry-pick + llvm-project revision 947f9692440836dcb8d88b74b69dd379d85974ce. + * sanitizer_common/sanitizer_platform_limits_posix.cc: Likewise. + +From-SVN: r279653 +--- + libsanitizer/ChangeLog | 10 ++++++++++ + .../sanitizer_platform_limits_posix.cc | 9 +++------ + .../sanitizer_platform_limits_posix.h | 15 +-------------- + 3 files changed, 14 insertions(+), 20 deletions(-) + +--- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc ++++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc +@@ -1156,12 +1156,9 @@ CHECK_SIZE_AND_OFFSET(ipc_perm, uid); + CHECK_SIZE_AND_OFFSET(ipc_perm, gid); + CHECK_SIZE_AND_OFFSET(ipc_perm, cuid); + CHECK_SIZE_AND_OFFSET(ipc_perm, cgid); +-#if (!defined(__aarch64__) || !SANITIZER_LINUX || __GLIBC_PREREQ (2, 21)) && \ +- !defined(__arm__) +-/* On aarch64 glibc 2.20 and earlier provided incorrect mode field. */ +-/* On Arm glibc 2.31 and later provide a different mode field, this field is +- never used by libsanitizer so we can simply ignore this assert for all glibc +- versions. */ ++#if !SANITIZER_LINUX || __GLIBC_PREREQ (2, 31) ++/* glibc 2.30 and earlier provided 16-bit mode field instead of 32-bit ++ on many architectures. */ + CHECK_SIZE_AND_OFFSET(ipc_perm, mode); + #endif + +diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h +index 73af92af1e8..6a673a7c995 100644 +--- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h ++++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h +@@ -211,26 +211,13 @@ namespace __sanitizer { + u64 __unused1; + u64 __unused2; + #elif defined(__sparc__) +-#if defined(__arch64__) + unsigned mode; +- unsigned short __pad1; +-#else +- unsigned short __pad1; +- unsigned short mode; + unsigned short __pad2; +-#endif + unsigned short __seq; + unsigned long long __unused1; + unsigned long long __unused2; +-#elif defined(__mips__) || defined(__aarch64__) || defined(__s390x__) +- unsigned int mode; +- unsigned short __seq; +- unsigned short __pad1; +- unsigned long __unused1; +- unsigned long __unused2; + #else +- unsigned short mode; +- unsigned short __pad1; ++ unsigned int mode; + unsigned short __seq; + unsigned short __pad2; + #if defined(__x86_64__) && !defined(_LP64) +-- +2.25.0 + diff --git a/build/pkgs/gcc/spkg-build b/build/pkgs/gcc/spkg-build.in similarity index 100% rename from build/pkgs/gcc/spkg-build rename to build/pkgs/gcc/spkg-build.in diff --git a/build/pkgs/gcc/spkg-configure.m4 b/build/pkgs/gcc/spkg-configure.m4 index 19c55b08aad..e40993fb24c 100644 --- a/build/pkgs/gcc/spkg-configure.m4 +++ b/build/pkgs/gcc/spkg-configure.m4 @@ -41,7 +41,7 @@ AC_DEFUN([SAGE_CHECK_BROKEN_GCC], [ echo '#include ' >conftest.cpp echo 'auto inf = 1.0 / std::complex();' >>conftest.cpp - if ! bash -c "source '$SAGE_SRC/bin/sage-env' && g++ -O3 -c -o conftest.o conftest.cpp"; then + if ! bash -c "source '$SAGE_SRC/bin/sage-env-config' && source '$SAGE_SRC/bin/sage-env' && g++ -O3 -c -o conftest.o conftest.cpp"; then SAGE_BROKEN_GCC=yes fi rm -f conftest.* @@ -88,12 +88,18 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ fi # Figuring out if we are using clang instead of gcc. + AC_LANG_PUSH(C) AX_COMPILER_VENDOR() IS_REALLY_GCC=no if test "x$ax_cv_c_compiler_vendor" = xgnu ; then IS_REALLY_GCC=yes fi + AC_LANG_POP() + # Save the value of CXX without special flags to enable C++11 support + AS_VAR_SET([SAGE_CXX_WITHOUT_STD], [$CXX]) + AC_SUBST(SAGE_CXX_WITHOUT_STD) + # Modify CXX to include an option that enables C++11 support if necessary AX_CXX_COMPILE_STDCXX_11([], optional) if test $HAVE_CXX11 != 1; then SAGE_MUST_INSTALL_GCC([your C++ compiler does not support C++11]) @@ -140,6 +146,11 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ [[[0-3]].*|4.[[0-7]].*], [ # Install our own GCC if the system-provided one is older than gcc-4.8. SAGE_SHOULD_INSTALL_GCC([you have $CXX version $GXX_VERSION, which is quite old]) + ], + [1?.*], [ + # Install our own GCC if the system-provided one is newer than 9.x. + # See https://trac.sagemath.org/ticket/29456 + SAGE_SHOULD_INSTALL_GCC([$CXX is g++ version $GXX_VERSION, which is too recent for this version of Sage]) ]) fi @@ -196,7 +207,7 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ CRTI=`$CC -print-file-name=crti.o 2>/dev/null || true` if test -n "$CRTI" ; then SAGE_CRTI_DIR=$(dirname -- "$CRTI") - if test "$SAGE_CRTI_DIR" == "." ; then + if test "$SAGE_CRTI_DIR" = "." ; then SAGE_CRTI_DIR= fi fi diff --git a/build/pkgs/gcc/spkg-install b/build/pkgs/gcc/spkg-install.in similarity index 100% rename from build/pkgs/gcc/spkg-install rename to build/pkgs/gcc/spkg-install.in diff --git a/build/pkgs/gcc/spkg-postinst b/build/pkgs/gcc/spkg-postinst.in similarity index 100% rename from build/pkgs/gcc/spkg-postinst rename to build/pkgs/gcc/spkg-postinst.in diff --git a/build/pkgs/gcc/spkg-postrm b/build/pkgs/gcc/spkg-postrm.in similarity index 100% rename from build/pkgs/gcc/spkg-postrm rename to build/pkgs/gcc/spkg-postrm.in diff --git a/build/pkgs/gcc/spkg-preinst b/build/pkgs/gcc/spkg-preinst.in similarity index 100% rename from build/pkgs/gcc/spkg-preinst rename to build/pkgs/gcc/spkg-preinst.in diff --git a/build/pkgs/gdb/SPKG.rst b/build/pkgs/gdb/SPKG.rst new file mode 100644 index 00000000000..d4975db94da --- /dev/null +++ b/build/pkgs/gdb/SPKG.rst @@ -0,0 +1,36 @@ +GDB +=== + +Description +----------- + +GDB, the GNU Project debugger, allows you to see what is going on +"inside" another program while it executes -- or what another program +was doing at the moment it crashed. + +License +------- + +GPL v3+ + + +Upstream Contact +---------------- + +http://www.gnu.org/software/gdb/ + +Dependencies +------------ + +- python +- mpc +- mpfr +- ppl +- gmp/mpir +- makeinfo (external) + + +Special Update/Build Instructions +--------------------------------- + +Current version needs makeinfo installed to build successfully. diff --git a/build/pkgs/gdb/SPKG.txt b/build/pkgs/gdb/SPKG.txt deleted file mode 100644 index 865c08f30ac..00000000000 --- a/build/pkgs/gdb/SPKG.txt +++ /dev/null @@ -1,29 +0,0 @@ -= GDB = - -== Description == - -GDB, the GNU Project debugger, allows you to see what is going on -"inside" another program while it executes -- or what another program -was doing at the moment it crashed. - -== License == - -GPL v3+ - -== Upstream Contact == - -http://www.gnu.org/software/gdb/ - -== Dependencies == - -* python -* mpc -* mpfr -* ppl -* gmp/mpir -* makeinfo (external) - -== Special Update/Build Instructions == - -Current version needs makeinfo installed -to build successfully. diff --git a/build/pkgs/gdb/spkg-install b/build/pkgs/gdb/spkg-install.in similarity index 100% rename from build/pkgs/gdb/spkg-install rename to build/pkgs/gdb/spkg-install.in diff --git a/build/pkgs/gentoo-bootstrap.txt b/build/pkgs/gentoo-bootstrap.txt new file mode 100644 index 00000000000..6f590cfce06 --- /dev/null +++ b/build/pkgs/gentoo-bootstrap.txt @@ -0,0 +1,3 @@ +sys-devel/autoconf +sys-devel/automake +sys-devel/libtool diff --git a/build/pkgs/gentoo.txt b/build/pkgs/gentoo.txt new file mode 100644 index 00000000000..51316a87c0b --- /dev/null +++ b/build/pkgs/gentoo.txt @@ -0,0 +1,22 @@ +sys-devel/binutils +sys-libs/binutils-libs +sys-devel/make +dev-scheme/guile +dev-libs/libffi +app-arch/tar +sys-devel/gcc +dev-libs/mpc +sys-libs/glibc +sys-kernel/linux-headers +dev-lang/perl +sys-devel/m4 +sys-devel/bc +dev-lang/python +sys-devel/flex +app-misc/ca-certificates +sys-devel/gettext +dev-libs/libcroco +dev-libs/libxml2 +sys-apps/findutils +sys-apps/which +sys-apps/diffutils diff --git a/build/pkgs/gf2x/SPKG.rst b/build/pkgs/gf2x/SPKG.rst new file mode 100644 index 00000000000..cee64e7ff1f --- /dev/null +++ b/build/pkgs/gf2x/SPKG.rst @@ -0,0 +1,62 @@ +gf2x +==== + +Description +----------- + +gf2x is a C/C++ software package containing routines for fast arithmetic +in GF(2)[x] (multiplication, squaring, GCD) and searching for +irreducible/primitive trinomials. + +Website: http://gf2x.gforge.inria.fr/ + +License +------- + +- GNU GPLv2+. + + +Upstream Contact +---------------- + +- Richard Brent +- Pierrick Gaudry +- Emmanuel Thomé +- Paul Zimmermann + +Dependencies +------------ + +- None + + +Special Update/Build Instructions +--------------------------------- + +- As some patches touch config/acinclude.m4, we have to touch + aclocal.m4, + configure, Makefile.in and gf2x/gf2x-config.h.in to prevent autotools + to try to regenerate these files. + +Patches +~~~~~~~ + +- 0001-Trac-15014-Let-gf2x-build-a-shared-library-on-Cygwin.patch: pass + -no-undefined flag to libtool. +- 0002-tr-portability.patch: backport upstream fix for non-portable tr + use +- 0003-Improve-detection-of-sse2-support.patch: backport upstream + improved check for sse2 + +- 0004-Add-disable-hardware-specific-code.patch: add option + -disable-hardware-specific-code to build system. This is partly + backported from upstream. + +- 0005-Update-autotooled-files.patch: the above patches make changes to + code used by autotools for generation of the build system. This + patches + those files, so that autotools need not be installed. + +- 0006-Fix_make_check_not_failing_on_errors.patch: (upstream patch) + Fix bug in shell script such that 'make check' always fails upon + errors. diff --git a/build/pkgs/gf2x/SPKG.txt b/build/pkgs/gf2x/SPKG.txt deleted file mode 100644 index f383bef530c..00000000000 --- a/build/pkgs/gf2x/SPKG.txt +++ /dev/null @@ -1,40 +0,0 @@ -= gf2x = - -== Description == -gf2x is a C/C++ software package containing routines for fast arithmetic -in GF(2)[x] (multiplication, squaring, GCD) and searching for -irreducible/primitive trinomials. - -Website: http://gf2x.gforge.inria.fr/ - -== License == - * GNU GPLv2+. - -== Upstream Contact == - * Richard Brent - * Pierrick Gaudry - * Emmanuel Thomé - * Paul Zimmermann - -== Dependencies == - * None - -== Special Update/Build Instructions == - * As some patches touch config/acinclude.m4, we have to touch aclocal.m4, - configure, Makefile.in and gf2x/gf2x-config.h.in to prevent autotools - to try to regenerate these files. - -=== Patches === - * 0001-Trac-15014-Let-gf2x-build-a-shared-library-on-Cygwin.patch: pass - -no-undefined flag to libtool. - * 0002-tr-portability.patch: backport upstream fix for non-portable tr use - * 0003-Improve-detection-of-sse2-support.patch: backport upstream improved - check for sse2 - * 0004-Add-disable-hardware-specific-code.patch: add option - --disable-hardware-specific-code to build system. This is partly - backported from upstream. - * 0005-Update-autotooled-files.patch: the above patches make changes to - code used by autotools for generation of the build system. This patches - those files, so that autotools need not be installed. - * 0006-Fix_make_check_not_failing_on_errors.patch: (upstream patch) - Fix bug in shell script such that 'make check' always fails upon errors. diff --git a/build/pkgs/gf2x/distros/fedora.txt b/build/pkgs/gf2x/distros/fedora.txt new file mode 100644 index 00000000000..726d8da490c --- /dev/null +++ b/build/pkgs/gf2x/distros/fedora.txt @@ -0,0 +1 @@ +gf2x gf2x-devel diff --git a/build/pkgs/gf2x/patches/0001-src-tunetoom.c-delete-duplicate-definition-of-rp.patch b/build/pkgs/gf2x/patches/0001-src-tunetoom.c-delete-duplicate-definition-of-rp.patch new file mode 100644 index 00000000000..bcb4c255707 --- /dev/null +++ b/build/pkgs/gf2x/patches/0001-src-tunetoom.c-delete-duplicate-definition-of-rp.patch @@ -0,0 +1,37 @@ +From 5c8737c5c3170358024a4a969e1386cea15932f3 Mon Sep 17 00:00:00 2001 +From: Michael Orlitzky +Date: Sun, 26 Apr 2020 09:56:34 -0400 +Subject: [PATCH 1/1] src/tunetoom.c: delete duplicate definition of rp. + +The "make tune-toom" command has started failing with gcc-10.x because +of its new default -fno-common behavior, + + * https://gcc.gnu.org/gcc-10/porting_to.html + * https://wiki.gentoo.org/wiki/Gcc_10_porting_notes/fno_common + +This leads to an error involving the FILE pointer "rp" that is declared +in global scope in both src/tunetoom.c and src/tuning-common.c. In this +case, the declaration in src/tunetoom.c is simply redundant: that file +includes src/tuning-common.h which already declares "rp" as extern. + +Deleting the redeclaration in src/tunetoom.c makes the build succeed. +--- + src/tunetoom.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/src/tunetoom.c b/src/tunetoom.c +index 7553e0c..1140606 100644 +--- a/src/tunetoom.c ++++ b/src/tunetoom.c +@@ -111,8 +111,6 @@ const char * gf2x_utoom_select_string[] = { + [GF2X_SELECT_UNB_TC3U] = "TC3U", + }; + +-FILE *rp; +- + void tunetoom(long tablesz) + { + long high, n; +-- +2.24.1 + diff --git a/build/pkgs/gf2x/spkg-check b/build/pkgs/gf2x/spkg-check deleted file mode 100644 index 7c99fec932c..00000000000 --- a/build/pkgs/gf2x/spkg-check +++ /dev/null @@ -1,13 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src -$MAKE check - -if [ $? -ne 0 ]; then - echo >&2 "Error: gf2x failed to pass its test suite." - exit 1 -fi diff --git a/build/pkgs/deformation/spkg-check b/build/pkgs/gf2x/spkg-check.in similarity index 100% rename from build/pkgs/deformation/spkg-check rename to build/pkgs/gf2x/spkg-check.in diff --git a/build/pkgs/gf2x/spkg-install b/build/pkgs/gf2x/spkg-install.in similarity index 100% rename from build/pkgs/gf2x/spkg-install rename to build/pkgs/gf2x/spkg-install.in diff --git a/build/pkgs/gfan/SPKG.rst b/build/pkgs/gfan/SPKG.rst new file mode 100644 index 00000000000..292dfe62166 --- /dev/null +++ b/build/pkgs/gfan/SPKG.rst @@ -0,0 +1,46 @@ +gfan +==== + +Description +----------- + +Gfan is a software package for computing Groebner fans and tropical +varieties. These are polyhedral fans associated to polynomial ideals. +The maximal cones of a Groebner fan are in bijection with the marked +reduced Groebner bases of its defining ideal. The software computes all +marked reduced Groebner bases of an ideal. Their union is a universal +Groebner basis. The tropical variety of a polynomial ideal is a certain +subcomplex of the Groebner fan. Gfan contains algorithms for computing +this complex for general ideals and specialized algorithms for tropical +curves, tropical hypersurfaces and tropical varieties of prime ideals. +In addition to the above core functions the package contains many tools +which are useful in the study of Groebner bases, initial ideals and +tropical geometry. The full list of commands can be found in Appendix B +of the manual. For ordinary Groebner basis computations Gfan is not +competitive in speed compared to programs such as CoCoA, Singular and +Macaulay2. + +License +------- + +- GPL version 2 or version 3 (according to the gfan website) + + +Upstream Contact +---------------- + +Anders Nedergaard Jensen; for contact info check out +http://home.imf.au.dk/jensen/software/gfan/gfan.html + +Dependencies +------------ + +- GMP/MPIR +- CDDLIB + + +Special Update/Build Instructions +--------------------------------- + +Remove the doc, homepage, and examples subdirectories, which take up +most of the space. diff --git a/build/pkgs/gfan/SPKG.txt b/build/pkgs/gfan/SPKG.txt deleted file mode 100644 index 022c47d1c0b..00000000000 --- a/build/pkgs/gfan/SPKG.txt +++ /dev/null @@ -1,36 +0,0 @@ -= gfan = - -== Description == - -Gfan is a software package for computing Groebner fans and tropical varieties. -These are polyhedral fans associated to polynomial ideals. The maximal cones -of a Groebner fan are in bijection with the marked reduced Groebner bases of -its defining ideal. The software computes all marked reduced Groebner bases of -an ideal. Their union is a universal Groebner basis. The tropical variety of a -polynomial ideal is a certain subcomplex of the Groebner fan. Gfan contains -algorithms for computing this complex for general ideals and specialized -algorithms for tropical curves, tropical hypersurfaces and tropical varieties -of prime ideals. In addition to the above core functions the package contains -many tools which are useful in the study of Groebner bases, initial ideals and -tropical geometry. The full list of commands can be found in Appendix B of the -manual. For ordinary Groebner basis computations Gfan is not competitive in -speed compared to programs such as CoCoA, Singular and Macaulay2. - -== License == - - * GPL version 2 or version 3 (according to the gfan website) - -== Upstream Contact == - -Anders Nedergaard Jensen; for contact info check out -http://home.imf.au.dk/jensen/software/gfan/gfan.html - -== Dependencies == - - * GMP/MPIR - * CDDLIB - -== Special Update/Build Instructions == - -Remove the doc, homepage, and examples subdirectories, which take up -most of the space. diff --git a/build/pkgs/gfan/distros/gentoo.txt b/build/pkgs/gfan/distros/gentoo.txt new file mode 100644 index 00000000000..c4cf1a97753 --- /dev/null +++ b/build/pkgs/gfan/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/gfan diff --git a/build/pkgs/gfan/spkg-check b/build/pkgs/gfan/spkg-check.in similarity index 100% rename from build/pkgs/gfan/spkg-check rename to build/pkgs/gfan/spkg-check.in diff --git a/build/pkgs/gfan/spkg-install b/build/pkgs/gfan/spkg-install deleted file mode 100644 index 9598d951cde..00000000000 --- a/build/pkgs/gfan/spkg-install +++ /dev/null @@ -1,18 +0,0 @@ -CXXFLAGS="$CXXFLAGS -DNOCDDPREFIX" - -export CC CXX CFLAGS CXXFLAGS LDFLAGS - -cd src - -echo "Now building gfan..." -# We don't use the makefile to install gfan so we don't need to set PREFIX -sdh_make CPPFLAGS="-I$SAGE_LOCAL/include" - -[ -f gfan ] || \ - sdh_die "Error: Build completed normally but gfan executable not found." - -sdh_install gfan "$SAGE_LOCAL/bin" - -cd "${SAGE_DESTDIR_LOCAL}/bin" -echo "Now running gfan to install links in '$SAGE_LOCAL/bin/'..." -./gfan installlinks || sdh_die "gfan links not created correctly" diff --git a/build/pkgs/gfan/spkg-install.in b/build/pkgs/gfan/spkg-install.in new file mode 100644 index 00000000000..f9280a64fc3 --- /dev/null +++ b/build/pkgs/gfan/spkg-install.in @@ -0,0 +1,21 @@ +CXXFLAGS="$CXXFLAGS -DNOCDDPREFIX" + +export CC CXX CFLAGS CXXFLAGS LDFLAGS + +cd src + +# clash of log2 macro with standard library (C++ >= 14), #28984 +find src -type f -print0 | xargs -0 sed -i.bak "s/log2/logger2/g" + +echo "Now building gfan..." +# We don't use the makefile to install gfan so we don't need to set PREFIX +sdh_make CPPFLAGS="-I$SAGE_LOCAL/include" + +[ -f gfan ] || \ + sdh_die "Error: Build completed normally but gfan executable not found." + +sdh_install gfan "$SAGE_LOCAL/bin" + +cd "${SAGE_DESTDIR_LOCAL}/bin" +echo "Now running gfan to install links in '$SAGE_LOCAL/bin/'..." +./gfan installlinks || sdh_die "gfan links not created correctly" diff --git a/build/pkgs/gfan/spkg-legacy-uninstall b/build/pkgs/gfan/spkg-legacy-uninstall.in similarity index 100% rename from build/pkgs/gfan/spkg-legacy-uninstall rename to build/pkgs/gfan/spkg-legacy-uninstall.in diff --git a/build/pkgs/gfortran/SPKG.rst b/build/pkgs/gfortran/SPKG.rst new file mode 100644 index 00000000000..3b4352444d1 --- /dev/null +++ b/build/pkgs/gfortran/SPKG.rst @@ -0,0 +1,33 @@ +gfortran +======== + +Description +----------- + +The GNU Compiler Collection, including the C, C++ and Fortran compiler. +This particular package is meant to only make gfortran available. + +License +------- + +GPL version 2 or version 3 + + +Upstream Contact +---------------- + +http://gcc.gnu.org/ + +Dependencies +------------ + +- zlib +- MPIR +- MPFR +- MPC + + +Special Update/Build Instructions +--------------------------------- + +None. diff --git a/build/pkgs/gfortran/SPKG.txt b/build/pkgs/gfortran/SPKG.txt deleted file mode 100644 index 0f9c79bf9dc..00000000000 --- a/build/pkgs/gfortran/SPKG.txt +++ /dev/null @@ -1,25 +0,0 @@ -= gfortran = - -== Description == - -The GNU Compiler Collection, including the C, C++ and Fortran compiler. -This particular package is meant to only make gfortran available. - -== License == - -GPL version 2 or version 3 - -== Upstream Contact == - -http://gcc.gnu.org/ - -== Dependencies == - - * zlib - * MPIR - * MPFR - * MPC - -== Special Update/Build Instructions == - -None. diff --git a/build/pkgs/gfortran/distros/cygwin.txt b/build/pkgs/gfortran/distros/cygwin.txt new file mode 100644 index 00000000000..8f962328b1e --- /dev/null +++ b/build/pkgs/gfortran/distros/cygwin.txt @@ -0,0 +1 @@ +gcc-fortran diff --git a/build/pkgs/gfortran/distros/homebrew.txt b/build/pkgs/gfortran/distros/homebrew.txt new file mode 100644 index 00000000000..c8f32320ba5 --- /dev/null +++ b/build/pkgs/gfortran/distros/homebrew.txt @@ -0,0 +1 @@ +gcc@9 diff --git a/build/pkgs/gfortran/distros/slackware.txt b/build/pkgs/gfortran/distros/slackware.txt new file mode 100644 index 00000000000..ec91125988f --- /dev/null +++ b/build/pkgs/gfortran/distros/slackware.txt @@ -0,0 +1 @@ +gcc-gfortran diff --git a/build/pkgs/gfortran/set-library-path b/build/pkgs/gfortran/set-library-path new file mode 120000 index 00000000000..3176acfdd88 --- /dev/null +++ b/build/pkgs/gfortran/set-library-path @@ -0,0 +1 @@ +../gcc/set-library-path \ No newline at end of file diff --git a/build/pkgs/gfortran/spkg-build b/build/pkgs/gfortran/spkg-build deleted file mode 100644 index 5c29f9b819a..00000000000 --- a/build/pkgs/gfortran/spkg-build +++ /dev/null @@ -1 +0,0 @@ -./build-gcc --disable-bootstrap --enable-languages=fortran diff --git a/build/pkgs/gfortran/spkg-build.in b/build/pkgs/gfortran/spkg-build.in new file mode 100644 index 00000000000..ac74c12ad5c --- /dev/null +++ b/build/pkgs/gfortran/spkg-build.in @@ -0,0 +1,11 @@ +. ./set-library-path + +# #29241: gfortran on 32bit: Configure for the same ABI as gcc +ARCH=$($CC -dumpmachine 2>/dev/null || echo unknown) +case $(uname -m),"$ARCH" in + x86_64,i[3456]86*) + EXTRA_GCC_CONFIGURE="--build=$ARCH" + ;; +esac + +./build-gcc --disable-bootstrap --enable-languages=fortran $EXTRA_GCC_CONFIGURE diff --git a/build/pkgs/gfortran/spkg-configure.m4 b/build/pkgs/gfortran/spkg-configure.m4 index fa02ea7607a..4794fc745d4 100644 --- a/build/pkgs/gfortran/spkg-configure.m4 +++ b/build/pkgs/gfortran/spkg-configure.m4 @@ -1,3 +1,37 @@ +dnl Usage: SAGE_SHOULD_INSTALL_GFORTRAN(reason) +dnl +dnl Use this macro to indicate that we SHOULD install GFORTRAN. +dnl In this case, GFORTRAN will be installed unless SAGE_INSTALL_GFORTRAN=no. +dnl In the latter case, a warning is given. +AC_DEFUN([SAGE_SHOULD_INSTALL_GFORTRAN], [ + if test x$SAGE_INSTALL_GFORTRAN = xexists; then + # Already installed in Sage, but it should remain selected + true + elif test x$SAGE_INSTALL_GFORTRAN = xno; then + AC_MSG_WARN([$1]) + else + AC_MSG_NOTICE([Installing gfortran because $1]) + sage_spkg_install_gfortran=yes + fi +]) + +dnl Usage: SAGE_MUST_INSTALL_GFORTRAN(reason) +dnl +dnl Use this macro to indicate that we MUST install GFORTRAN. +dnl In this case, it is an error if SAGE_INSTALL_GFORTRAN=no. +AC_DEFUN([SAGE_MUST_INSTALL_GFORTRAN], [ + if test x$SAGE_INSTALL_GFORTRAN = xexists; then + # Already installed in Sage, but it should remain selected + true + elif test x$SAGE_INSTALL_GFORTRAN = xno; then + AC_MSG_ERROR([SAGE_INSTALL_GFORTRAN is set to 'no', but $1]) + else + AC_MSG_NOTICE([Installing gfortran because $1]) + sage_spkg_install_gfortran=yes + fi +]) + + SAGE_SPKG_CONFIGURE([gfortran], [ AC_REQUIRE([SAGE_SPKG_CONFIGURE_GCC]) AC_REQUIRE([AC_PROG_FC]) @@ -6,15 +40,40 @@ SAGE_SPKG_CONFIGURE([gfortran], [ # This helps verify the compiler works too, so if some idiot sets FC to # /usr/bin/ls, we will at least know it's not a working Fortran # compiler. - AC_FC_FREEFORM([], [ + AC_FC_FREEFORM([SAGE_HAVE_FC_FREEFORM=yes], [ AC_MSG_NOTICE([Your Fortran compiler does not accept free-format source code]) AC_MSG_NOTICE([which means the compiler is either seriously broken, or]) AC_MSG_NOTICE([is too old to build Sage.]) - sage_spkg_install_gfortran=yes]) + SAGE_HAVE_FC_FREEFORM=no]) + + AS_VAR_IF(SAGE_HAVE_FC_FREEFORM, [no], [ + AS_VAR_SET(sage_spkg_install_gfortran, [yes]) + ]) # Special case: If we are already installing gcc then don't install # gfortran since it's included if test "x$sage_spkg_install_gcc" = "xyes" -o x$SAGE_INSTALL_GCC = xexists; then sage_spkg_install_gfortran=no + + else + # AX_COMPILER_VENDOR does not work for Fortran. So we just match the name of the executable + AS_CASE(["$FC"], + [*gfortran*], [ + AC_MSG_CHECKING([the version of $FC]) + GFORTRAN_VERSION="`$FC -dumpversion`" + AC_MSG_RESULT([$GFORTRAN_VERSION]) + # Add the .0 because Debian/Ubuntu gives version numbers like + # 4.6 instead of 4.6.4 (Trac #18885) + AS_CASE(["$GFORTRAN_VERSION.0"], + [[[0-3]].*|4.[[0-7]].*], [ + # Install our own gfortran if the system-provided one is older than gcc-4.8. + SAGE_SHOULD_INSTALL_GFORTRAN([$FC is version $GFORTRAN_VERSION, which is quite old]) + ], + [1?.*], [ + # Install our own gfortran if the system-provided one is newer than 9.x. + # See https://trac.sagemath.org/ticket/29456 + SAGE_MUST_INSTALL_GFORTRAN([$FC is version $GFORTRAN_VERSION, which is too recent for this version of Sage]) + ]) + ]) fi ]) diff --git a/build/pkgs/gfortran/spkg-install b/build/pkgs/gfortran/spkg-install deleted file mode 100644 index e643ffe4570..00000000000 --- a/build/pkgs/gfortran/spkg-install +++ /dev/null @@ -1,9 +0,0 @@ -# Exit on error -set -e - -cd gcc-build -sdh_make_install - -# The spkg still installs a minimal C compiler that needs to be removed -# so it doesn't conflict with the configured C compiler. -rm "$SAGE_DESTDIR_LOCAL"/bin/gcc "$SAGE_DESTDIR_LOCAL"/bin/cpp diff --git a/build/pkgs/gfortran/spkg-install.in b/build/pkgs/gfortran/spkg-install.in new file mode 100644 index 00000000000..33f699f11f2 --- /dev/null +++ b/build/pkgs/gfortran/spkg-install.in @@ -0,0 +1,11 @@ +# Exit on error +set -e + +. ./set-library-path + +cd gcc-build +sdh_make_install + +# The spkg still installs a minimal C compiler that needs to be removed +# so it doesn't conflict with the configured C compiler. +rm "$SAGE_DESTDIR_LOCAL"/bin/gcc "$SAGE_DESTDIR_LOCAL"/bin/cpp diff --git a/build/pkgs/gfortran/spkg-postinst.in b/build/pkgs/gfortran/spkg-postinst.in new file mode 100644 index 00000000000..bf4530f1f1f --- /dev/null +++ b/build/pkgs/gfortran/spkg-postinst.in @@ -0,0 +1,20 @@ +# -*- mode: shell-script -*- + +# #27907: Symlink crt1.o and friends where gfortran can find it. +if [ -n "$SAGE_CRTI_DIR" ]; then + INSTALLED_SCRIPTS_DEST="$SAGE_SPKG_SCRIPTS/$PKG_BASE" + LIBGCC=$(dirname -- $("$SAGE_LOCAL"/bin/gfortran -print-libgcc-file-name)) + if [ -z "$LIBGCC" -o ! -d "$LIBGCC" ]; then + echo >&2 "Warning: Unable to locate LIBGCC directory, so cannot install crt symlinks" + exit + fi + echo "Installing symlinks to crt files." + echo "# Remove symlinks created by spkg-postinst" >> "$INSTALLED_SCRIPTS_DEST"/spkg-postrm + for f in "$SAGE_CRTI_DIR"/*crt?.o; do + if [ -r $f -a ! -r "$LIBGCC"/$f ]; then + ln -s $f "$LIBGCC"/ + fb=`basename $f` + echo "rm -f \"$LIBGCC\"/$fb" >> "$INSTALLED_SCRIPTS_DEST"/spkg-postrm + fi + done +fi diff --git a/build/pkgs/gfortran/spkg-postrm.in b/build/pkgs/gfortran/spkg-postrm.in new file mode 100644 index 00000000000..52a5a8ee827 --- /dev/null +++ b/build/pkgs/gfortran/spkg-postrm.in @@ -0,0 +1,3 @@ +# -*- mode: shell-script -*- + +## spkg-postinst adds commands to the bottom of the generated version of this file. diff --git a/build/pkgs/gfortran/spkg-preinst b/build/pkgs/gfortran/spkg-preinst.in similarity index 100% rename from build/pkgs/gfortran/spkg-preinst rename to build/pkgs/gfortran/spkg-preinst.in diff --git a/build/pkgs/giac/SPKG.rst b/build/pkgs/giac/SPKG.rst new file mode 100644 index 00000000000..2bfa3918dec --- /dev/null +++ b/build/pkgs/giac/SPKG.rst @@ -0,0 +1,57 @@ +GIAC +==== + +Description +----------- + +- Giac is a general purpose Computer algebra system by Bernard Parisse. + It consists of: +- a C++ library (libgiac). +- a command line interpreter (icas or giac). +- the built of the FLTK-based GUI (xcas) has been disabled in the + spkg-install file. + +- The english documentation will be installed in: + + $SAGE_LOCAL/share/giac/doc/en/cascmd_en/index.html + +- Author's website with debian, ubuntu, macosx, windows package: + + http://www-fourier.ujf-grenoble.fr/~parisse/giac.html + +- The Freebsd port is math/giacxcas + +Licence +------- + +GPLv3+ + +Note: except the french html documentation which is freely +redistributable for non commercial only purposes. This doc has been +removed in the Sage package, see spkg-src + + +Upstream Contact +---------------- + +- Bernard Parisse: + http://www-fourier.ujf-grenoble.fr/~parisse/giac.html +- Source file (giac-x.y.z-t.tar.gz) in: + + http://www-fourier.ujf-grenoble.fr/~parisse/debian/dists/stable/main/source/ + +Dependencies +------------ + +- gettext, readline +- giac will benefit of ntl, pari, mpfr, gsl, lapack but they should be + already installed by sage. +- giac can also benefit of mpfi for arithmetic on intervals. +- The Documentation is pre-built, hevea or latex or ... are not needed + to install the package. + + +Special Update/Build Instructions +--------------------------------- + +- Use spkg-src to update this package diff --git a/build/pkgs/giac/SPKG.txt b/build/pkgs/giac/SPKG.txt deleted file mode 100644 index d4d0a35ba5b..00000000000 --- a/build/pkgs/giac/SPKG.txt +++ /dev/null @@ -1,40 +0,0 @@ -= GIAC = - -== Description == - - * Giac is a general purpose Computer algebra system by Bernard Parisse. It consists of: - - a C++ library (libgiac). - - a command line interpreter (icas or giac). - - the built of the FLTK-based GUI (xcas) has been disabled in the spkg-install file. - - * The english documentation will be installed in: - $SAGE_LOCAL/share/giac/doc/en/cascmd_en/index.html - - * -Author's website with debian, ubuntu, macosx, windows package: - http://www-fourier.ujf-grenoble.fr/~parisse/giac.html - -The Freebsd port is math/giacxcas - -== Licence == - -GPLv3+ - -Note: except the french html documentation which is freely -redistributable for non commercial only purposes. This doc has been -removed in the Sage package, see spkg-src - -== Upstream Contact == - - * Bernard Parisse: http://www-fourier.ujf-grenoble.fr/~parisse/giac.html - * Source file (giac-x.y.z-t.tar.gz) in: - http://www-fourier.ujf-grenoble.fr/~parisse/debian/dists/stable/main/source/ - -== Dependencies == - - * gettext, readline - * giac will benefit of ntl, pari, mpfr, gsl, lapack but they should be already installed by sage. - * giac can also benefit of mpfi for arithmetic on intervals. - * The Documentation is pre-built, hevea or latex or ... are not needed to install the package. - -== Special Update/Build Instructions == - - * Use spkg-src to update this package 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-check b/build/pkgs/giac/spkg-check deleted file mode 100644 index a18f163eb50..00000000000 --- a/build/pkgs/giac/spkg-check +++ /dev/null @@ -1,18 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo "Error: SAGE_LOCAL undefined - exiting..." - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -mv src/icas src/icas.orig -# testing the newly installed version of giac -ln -s $SAGE_LOCAL/bin/giac src/icas -$MAKE check - -if [ $? -ne 0 ]; then - echo "There was a problem during the giac tests." - exit 1 -fi - diff --git a/build/pkgs/giac/spkg-check.in b/build/pkgs/giac/spkg-check.in new file mode 100644 index 00000000000..2a2ca4bb04b --- /dev/null +++ b/build/pkgs/giac/spkg-check.in @@ -0,0 +1,6 @@ +cd src + +mv src/icas src/icas.orig +# testing the newly installed version of giac +ln -s $SAGE_LOCAL/bin/giac src/icas +$MAKE check diff --git a/build/pkgs/giac/spkg-install b/build/pkgs/giac/spkg-install deleted file mode 100644 index 8e984802c81..00000000000 --- a/build/pkgs/giac/spkg-install +++ /dev/null @@ -1,77 +0,0 @@ -########################################### -## Giac -########################################### - - -############################################################# -# Environment Variables -############################################################# -# If CFLAGS and CXXFLAGS are unset, giac looks to set -g -O2, -# but if they are not empty, the -g -O2 is not added -CFLAGS="-g -O2 $CFLAGS" -CXXFLAGS="-g -O2 $CXXFLAGS" -CPPFLAGS="-I$SAGE_LOCAL/include $CPPFLAGS" - -if [ `uname -m` = "ppc64" ]; then - CPPFLAGS="-Dx86_64 $CPPFLAGS" -fi - -export CFLAGS CXXFLAGS CPPFLAGS - -cd src - -############################################################# -# configure -############################################################# -# -# We use the option --disable-gui to be sure that the built won't stop because the gui tries -# to be built with a bad version of fltk. -# -# If you want to built the gui (xcas), you need fltk from the 1.3 branch. (Not 1.1 nor 2. ) -# and GL headers. -# - - -echo "Configuring giac..." - - -# --disable-ao (avoid libao deps) -# --disable-lapack (avoid lapack and blas deps because they could be from the base system) -# On OS X (10.12) the built in intl is broken -DISABLENLS="" -if [ "$UNAME" = "Darwin" ]; then - echo "OS X Building without Native Language Support" - DISABLENLS="--disable-nls" -fi - -sdh_configure --disable-gui --disable-ao --disable-lapack "$DISABLENLS" --enable-png=no --disable-samplerate - -############################################################# -# Build -############################################################# - -sdh_make - -############################################################# -# Clean old install -############################################################# -echo "Cleaning giac..." -rm -f ${SAGE_LOCAL}/lib/libgiac* -rm -f ${SAGE_LOCAL}/bin/icas -rm -f ${SAGE_LOCAL}/bin/xcas -rm -f ${SAGE_LOCAL}/bin/cas_help -rm -f ${SAGE_LOCAL}/bin/pgiac -rm -f ${SAGE_LOCAL}/bin/en_cas_help -rm -f ${SAGE_LOCAL}/bin/es_cas_help -rm -f ${SAGE_LOCAL}/bin/fr_cas_help -rm -f ${SAGE_LOCAL}/bin/giac -rm -f ${SAGE_LOCAL}/bin/xcasnew -rm -rf ${SAGE_LOCAL}/share/giac -rm -rf ${SAGE_LOCAL}/share/doc/giac -rm -rf ${SAGE_LOCAL}/include/giac - -############################################################# -# Install -############################################################# - -sdh_make_install diff --git a/build/pkgs/giac/spkg-install.in b/build/pkgs/giac/spkg-install.in new file mode 100644 index 00000000000..d6a931b3b10 --- /dev/null +++ b/build/pkgs/giac/spkg-install.in @@ -0,0 +1,81 @@ +########################################### +## Giac +########################################### + + +############################################################# +# Environment Variables +############################################################# +# If CFLAGS and CXXFLAGS are unset, giac looks to set -g -O2, +# but if they are not empty, the -g -O2 is not added +CFLAGS="-g -O2 $CFLAGS" +CXXFLAGS="-g -O2 $CXXFLAGS" +CPPFLAGS="-I$SAGE_LOCAL/include $CPPFLAGS" + +if [ `uname -m` = "ppc64" ]; then + CPPFLAGS="-Dx86_64 $CPPFLAGS" +fi + +# Using pari in a C++17 file with "using namespace std doesn't +# work due to a conflict between std::rank and pari's rank +CXXFLAGS=$(echo "${CXXFLAGS}" | sed "s/-std=c++17//g") + +export CFLAGS CXXFLAGS CPPFLAGS + +cd src + +############################################################# +# configure +############################################################# +# +# We use the option --disable-gui to be sure that the built won't stop because the gui tries +# to be built with a bad version of fltk. +# +# If you want to built the gui (xcas), you need fltk from the 1.3 branch. (Not 1.1 nor 2. ) +# and GL headers. +# + + +echo "Configuring giac..." + + +# --disable-ao (avoid libao deps) +# --disable-lapack (avoid lapack and blas deps because they could be from the base system) +# On OS X (10.12) the built in intl is broken +DISABLENLS="" +if [ "$UNAME" = "Darwin" ]; then + echo "OS X Building without Native Language Support" + DISABLENLS="--disable-nls" +fi + +sdh_configure --disable-gui --disable-ao --disable-lapack "$DISABLENLS" --enable-png=no --disable-samplerate + +############################################################# +# Build +############################################################# + +sdh_make + +############################################################# +# Clean old install +############################################################# +echo "Cleaning giac..." +rm -f ${SAGE_LOCAL}/lib/libgiac* +rm -f ${SAGE_LOCAL}/bin/icas +rm -f ${SAGE_LOCAL}/bin/xcas +rm -f ${SAGE_LOCAL}/bin/cas_help +rm -f ${SAGE_LOCAL}/bin/pgiac +rm -f ${SAGE_LOCAL}/bin/en_cas_help +rm -f ${SAGE_LOCAL}/bin/es_cas_help +rm -f ${SAGE_LOCAL}/bin/fr_cas_help +rm -f ${SAGE_LOCAL}/bin/giac +rm -f ${SAGE_LOCAL}/bin/xcasnew +rm -rf ${SAGE_LOCAL}/share/giac +rm -rf ${SAGE_LOCAL}/share/doc/giac +rm -rf ${SAGE_LOCAL}/include/giac + +############################################################# +# Install +############################################################# + +sdh_make_install 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/giacpy_sage/SPKG.rst b/build/pkgs/giacpy_sage/SPKG.rst new file mode 100644 index 00000000000..af4bd19500c --- /dev/null +++ b/build/pkgs/giacpy_sage/SPKG.rst @@ -0,0 +1,31 @@ +GIACPY +====== + +Description +----------- + +- Giacpy is a cython frontend to the c++ library giac. This is the sage + version. +- The sage version of giacpy after 0.6 is renamed to giacpy_sage + +Licence +------- + +GPLv2 or above + + +Upstream Contact +---------------- + +- Han Frederic: frederic.han@imj-prg.fr + + https://www.imj-prg.fr/~frederic.han/xcas/giacpy/ + +Dependencies +------------ + +- gmp, giac (the C++ library libgiac and headers) + + +Special Update/Build Instructions +--------------------------------- diff --git a/build/pkgs/giacpy_sage/SPKG.txt b/build/pkgs/giacpy_sage/SPKG.txt deleted file mode 100644 index 37a49a2ec9a..00000000000 --- a/build/pkgs/giacpy_sage/SPKG.txt +++ /dev/null @@ -1,22 +0,0 @@ -= GIACPY = - -== Description == - - * Giacpy is a cython frontend to the c++ library giac. This is the sage version. - * The sage version of giacpy after 0.6 is renamed to giacpy_sage - -== Licence == - -GPLv2 or above - -== Upstream Contact == - - * Han Frederic: frederic.han@imj-prg.fr - https://www.imj-prg.fr/~frederic.han/xcas/giacpy/ - -== Dependencies == - - * gmp, giac (the C++ library libgiac and headers) - -== Special Update/Build Instructions == - diff --git a/build/pkgs/giacpy_sage/dependencies b/build/pkgs/giacpy_sage/dependencies index 49ae7e5eade..b457adc630e 100644 --- a/build/pkgs/giacpy_sage/dependencies +++ b/build/pkgs/giacpy_sage/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) cython giac cysignals | $(SAGERUNTIME) pip sympy maxima +$(PYTHON) cython giac cysignals | $(SAGERUNTIME) $(PYTHON_TOOLCHAIN) sympy maxima spkg-check uses the sage command. The doctest suite (SAGE_CHECK=yes) also uses Sympy and Maxima ---------- diff --git a/build/pkgs/giacpy_sage/spkg-check b/build/pkgs/giacpy_sage/spkg-check deleted file mode 100644 index 1f75535859a..00000000000 --- a/build/pkgs/giacpy_sage/spkg-check +++ /dev/null @@ -1,15 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo "Error: SAGE_LOCAL undefined - exiting..." - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -# --force-lib because we dont want sage -t to compile giacpy.pyx -# we only use the test suite to test giacpy.so -sage -t --verbose --force-lib src/giacpy_sage.pyx - -if [ $? -ne 0 ]; then - echo "There was a problem during the giacpy tests." - exit 1 -fi - diff --git a/build/pkgs/giacpy_sage/spkg-check.in b/build/pkgs/giacpy_sage/spkg-check.in new file mode 100644 index 00000000000..0e15e6456ac --- /dev/null +++ b/build/pkgs/giacpy_sage/spkg-check.in @@ -0,0 +1,3 @@ +# --force-lib because we dont want sage -t to compile giacpy.pyx +# we only use the test suite to test giacpy.so +sage -t --verbose --force-lib src/giacpy_sage.pyx diff --git a/build/pkgs/giacpy_sage/spkg-install b/build/pkgs/giacpy_sage/spkg-install deleted file mode 100644 index b756d8288c7..00000000000 --- a/build/pkgs/giacpy_sage/spkg-install +++ /dev/null @@ -1,32 +0,0 @@ -########################################### -## Giacpy -########################################### - - -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - - -# It is very important that we have SAGE_LOCAL set, otherwise this -# might potentially delete stuff in /lib -# old giacpy spkg files <0.6 but no dir in case someone put a python version of giacpy. -echo "Deleting $SAGE_LOCAL/lib/python*/site-packages/giacpy-*" -rm -f "$SAGE_LOCAL"/lib/python*/site-packages/giacpy-* -echo "Deleting $SAGE_LOCAL/lib/python*/site-packages/giacpy.so" -rm -f "$SAGE_LOCAL"/lib/python*/site-packages/giacpy.so -# new giacpy spkg files >0.6 -echo "Deleting $SAGE_LOCAL/lib/python*/site-packages/giacpy_sage*" -rm -f "$SAGE_LOCAL"/lib/python*/site-packages/giacpy_sage* - - -sdh_pip_install . - -if [ $? -ne 0 ]; then - exit 1 -fi - diff --git a/build/pkgs/giacpy_sage/spkg-install.in b/build/pkgs/giacpy_sage/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/giacpy_sage/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/git/SPKG.rst b/build/pkgs/git/SPKG.rst new file mode 100644 index 00000000000..408192996a7 --- /dev/null +++ b/build/pkgs/git/SPKG.rst @@ -0,0 +1,28 @@ + +git - the stupid content tracker +================================ + +Description +----------- + + Git is a fast, scalable, distributed revision control system with an + unusually rich command set that provides both high-operations and + full access to internals. + +- ``man git`` + + +Upstream Contact +---------------- + +- Maintainer: Junio C. Hamano +- Website: http://git-scm.com/ + +Dependencies +------------ + +- zlib + +Note: excluding libcurl and expat because they are large and only +required if you're communicating with repositories over HTTP. If you +need to do so, please use an external version of git. diff --git a/build/pkgs/git/SPKG.txt b/build/pkgs/git/SPKG.txt deleted file mode 100644 index 51e88426e20..00000000000 --- a/build/pkgs/git/SPKG.txt +++ /dev/null @@ -1,49 +0,0 @@ -= git - the stupid content tracker = - -== Description == - - Git is a fast, scalable, distributed revision control system with an - unusually rich command set that provides both high-operations and - full access to internals. - - -- `man git` - -== Upstream Contact == - - * Maintainer: Junio C. Hamano - * Website: http://git-scm.com/ - -== Dependencies == - - * zlib - -Note: excluding libcurl and expat because they are large and only -required if you're communicating with repositories over HTTP. If you -need to do so, please use an external version of git. - -== Changelog == - -=== git-1.7.12.2.p0 (Jeroen Demeyer, 2012-10-02) === - - * #12707: Upgrade to git-1.7.12.2 - * Disable Tcl/Tk GUI (otherwise Tcl/Tk is a dependency) - * Various fixes to spkg-install - * Keep SANE_TOOL_PATH empty to ensure the PATH isn't changed - * Figure out *correct* path to "install" - * Add patch no-autoconf.patch to prevent running autoconf - -=== git-1.7.10 (Keshav Kini, 2012-04-20) === - - * #12707: Upgrade to latest stable - * Fix building on OS X by making the installer ignore Fink and Darwin - Ports - * Make sure not to use the system Python for anything - -=== git-1.7.9.4 (Keshav Kini, 2012-03-19) === - - * Upgrade to latest stable - * Track dependencies - -=== git-1.7.2.4 (William Stein, 2012-03-19) === - - * Initial version diff --git a/build/pkgs/git/distros/cygwin.txt b/build/pkgs/git/distros/cygwin.txt new file mode 100644 index 00000000000..5664e303b5d --- /dev/null +++ b/build/pkgs/git/distros/cygwin.txt @@ -0,0 +1 @@ +git diff --git a/build/pkgs/git/distros/homebrew.txt b/build/pkgs/git/distros/homebrew.txt new file mode 100644 index 00000000000..5664e303b5d --- /dev/null +++ b/build/pkgs/git/distros/homebrew.txt @@ -0,0 +1 @@ +git diff --git a/build/pkgs/git/distros/slackware.txt b/build/pkgs/git/distros/slackware.txt new file mode 100644 index 00000000000..5664e303b5d --- /dev/null +++ b/build/pkgs/git/distros/slackware.txt @@ -0,0 +1 @@ +git diff --git a/build/pkgs/git/spkg-check b/build/pkgs/git/spkg-check deleted file mode 100644 index 044d63dc564..00000000000 --- a/build/pkgs/git/spkg-check +++ /dev/null @@ -1,15 +0,0 @@ -# OSX Git with FSF GCC is broken, disable completely for now. See #17091 -export NO_APPLE_COMMON_CRYPTO=1 - -# Disable network tests -export GIT_TEST_HTTPD=false - -cd src - -echo "Testing git..." -$MAKE test -if [ $? -ne 0 ]; then - echo >&2 "Error running git's test suite." - exit 1 -fi - diff --git a/build/pkgs/git/spkg-check.in b/build/pkgs/git/spkg-check.in new file mode 100644 index 00000000000..b406e1dbbad --- /dev/null +++ b/build/pkgs/git/spkg-check.in @@ -0,0 +1,8 @@ +# Do not run a test suite: see https://trac.sagemath.org/ticket/30093. +# +# - The test suite takes a long time and can often fail in minor ways. +# - If some major aspect of git fails, it only affects the build +# process of Sage, not the mathematical correctness of its results. +# - If some minor aspect of git fails, it won't affect anything about Sage. + +echo "We skip the test suite for git: see https://trac.sagemath.org/ticket/30093." diff --git a/build/pkgs/git/spkg-install b/build/pkgs/git/spkg-install deleted file mode 100644 index 76db099a075..00000000000 --- a/build/pkgs/git/spkg-install +++ /dev/null @@ -1,72 +0,0 @@ -die () { - echo "$@" >&2 - exit 1 -} - -[ -n "$SAGE_LOCAL" ] || die 'Error: $SAGE_LOCAL not set. Source sage-env or run this script from `sage -sh`.' - -# Path to "install" command -for cmd in /usr/ucb/install ginstall install; do - [ -z "$INSTALL" ] || break - INSTALL=`command -v $cmd 2>/dev/null` -done -[ -n "$INSTALL" ] || die 'No install program found' -echo "Using install program $INSTALL" - -# Gettext is a soft build dependency (i18n) -command -v msgfmt >/dev/null 2>&1 -if [ $? -ne 0 ]; then - gettext='NO_GETTEXT=YesPlease' -else - gettext='' -fi - -cd src - -# We don't want to think about Fink or Macports -export NO_FINK=1 -export NO_DARWIN_PORTS=1 - -# Use softlinks instead of hardlinks (see Trac #19467) -export NO_INSTALL_HARDLINKS=1 - -# OSX Git with FSF GCC is broken, disable completely for now. See #17091 -if [ "$UNAME" = "Darwin" ]; then - export NO_OPENSSL=1 -fi - -# First make GIT-VERSION-FILE (we patched Makefile such that configure -# no longer depends on this, so it's safer to explicitly build this). -$MAKE GIT-VERSION-FILE - -# Configure without Tcl/Tk (otherwise git *requires* Tcl/Tk). -# We keep SANE_TOOL_PATH empty, otherwise git messes with the PATH on -# some systems, leading for example to a different "make" being used. -echo "Configuring git..." -./configure --prefix="$SAGE_LOCAL" \ - --libexecdir="$SAGE_LOCAL"/libexec \ - --with-python="$SAGE_LOCAL"/bin/python \ - --without-tcltk \ - --with-sane-tool-path= -if [ $? -ne 0 ]; then - echo >&2 "Error configuring git." - exit 1 -fi - - -echo "Building git..." -$MAKE $gettext -if [ $? -ne 0 ]; then - echo >&2 "Error building git." - exit 1 -fi - - -echo "Installing git..." -$MAKE -j1 INSTALL="$INSTALL" install $gettext -if [ $? -ne 0 ]; then - echo >&2 'Error installing git.' - exit 1 -fi - - diff --git a/build/pkgs/git/spkg-install.in b/build/pkgs/git/spkg-install.in new file mode 100644 index 00000000000..1d0dd2c98a8 --- /dev/null +++ b/build/pkgs/git/spkg-install.in @@ -0,0 +1,70 @@ +die () { + echo "$@" >&2 + exit 1 +} + +# Path to "install" command +for cmd in /usr/ucb/install ginstall install; do + [ -z "$INSTALL" ] || break + INSTALL=`command -v $cmd 2>/dev/null` +done +[ -n "$INSTALL" ] || die 'No install program found' +echo "Using install program $INSTALL" + +# Gettext is a soft build dependency (i18n) +command -v msgfmt >/dev/null 2>&1 +if [ $? -ne 0 ]; then + gettext='NO_GETTEXT=YesPlease' +else + gettext='' +fi + +cd src + +# We don't want to think about Fink or Macports +export NO_FINK=1 +export NO_DARWIN_PORTS=1 + +# Use softlinks instead of hardlinks (see Trac #19467) +export NO_INSTALL_HARDLINKS=1 + +# OSX Git with FSF GCC is broken, disable completely for now. See #17091 +if [ "$UNAME" = "Darwin" ]; then + export NO_OPENSSL=1 +fi + +# First make GIT-VERSION-FILE (we patched Makefile such that configure +# no longer depends on this, so it's safer to explicitly build this). +$MAKE GIT-VERSION-FILE + +# Configure without Tcl/Tk (otherwise git *requires* Tcl/Tk). +# We keep SANE_TOOL_PATH empty, otherwise git messes with the PATH on +# some systems, leading for example to a different "make" being used. +echo "Configuring git..." +./configure --prefix="$SAGE_LOCAL" \ + --libexecdir="$SAGE_LOCAL"/libexec \ + --with-python="$SAGE_LOCAL"/bin/python \ + --without-tcltk \ + --with-sane-tool-path= +if [ $? -ne 0 ]; then + echo >&2 "Error configuring git." + exit 1 +fi + + +echo "Building git..." +$MAKE $gettext +if [ $? -ne 0 ]; then + echo >&2 "Error building git." + exit 1 +fi + + +echo "Installing git..." +$MAKE -j1 INSTALL="$INSTALL" install $gettext +if [ $? -ne 0 ]; then + echo >&2 'Error installing git.' + exit 1 +fi + + diff --git a/build/pkgs/git_trac/SPKG.rst b/build/pkgs/git_trac/SPKG.rst new file mode 100644 index 00000000000..d11ec626fbe --- /dev/null +++ b/build/pkgs/git_trac/SPKG.rst @@ -0,0 +1,32 @@ + +Git-Trac +======== + +Description +----------- + +This module implements a "git trac" subcommand of the git suite that +interfaces with trac over XMLRPC. + +License +------- + +GPLv3+ + + +Upstream Contact +---------------- + +- https://github.com/sagemath/git-trac-command +- Volker Braun + +Dependencies +------------ + +- python 2.7 or 3.3+ + + +Special Update/Build Instructions +--------------------------------- + +Nothing special, just use the provided setup.py diff --git a/build/pkgs/git_trac/SPKG.txt b/build/pkgs/git_trac/SPKG.txt deleted file mode 100644 index 08c3b2b7683..00000000000 --- a/build/pkgs/git_trac/SPKG.txt +++ /dev/null @@ -1,24 +0,0 @@ -= Git-Trac = - -== Description == - -This module implements a "git trac" subcommand of the git suite that -interfaces with trac over XMLRPC. - -== License == - -GPLv3+ - -== Upstream Contact == - -https://github.com/sagemath/git-trac-command -Volker Braun - -== Dependencies == - -* python 2.7 or 3.3+ - -== Special Update/Build Instructions == - -Nothing special, just use the provided setup.py - diff --git a/build/pkgs/git_trac/dependencies b/build/pkgs/git_trac/dependencies index 2573414ce8a..15df0c4d6d8 100644 --- a/build/pkgs/git_trac/dependencies +++ b/build/pkgs/git_trac/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/git_trac/spkg-install b/build/pkgs/git_trac/spkg-install deleted file mode 100644 index c3082ad27ca..00000000000 --- a/build/pkgs/git_trac/spkg-install +++ /dev/null @@ -1,14 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined... exiting" - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo >&2 "Error installing git-trac, exiting." - exit 1 -fi diff --git a/build/pkgs/git_trac/spkg-install.in b/build/pkgs/git_trac/spkg-install.in new file mode 100644 index 00000000000..ab4c552d06b --- /dev/null +++ b/build/pkgs/git_trac/spkg-install.in @@ -0,0 +1,8 @@ +cd src + +sdh_pip_install . + +if [ $? -ne 0 ]; then + echo >&2 "Error installing git-trac, exiting." + exit 1 +fi diff --git a/build/pkgs/givaro/SPKG.rst b/build/pkgs/givaro/SPKG.rst new file mode 100644 index 00000000000..54198224ef2 --- /dev/null +++ b/build/pkgs/givaro/SPKG.rst @@ -0,0 +1,36 @@ +Givaro +====== + +Description +----------- + +Givaro is a C++ library for arithmetic and algebraic computations. Its +main features are implementations of the basic arithmetic of many +mathematical entities: Primes fields, Extensions Fields, Finite Fields, +Finite Rings, Polynomials, Algebraic numbers, Arbitrary precision +integers and rationals (C++ wrappers over gmp) It also provides +data-structures and templated classes for the manipulation of basic +algebraic objects, such as vectors, matrices (dense, sparse, +structured), univariate polynomials (and therefore recursive +multivariate). + +Website: http://www-lmc.imag.fr/CASYS/LOGICIELS/givaro/ + +SPKG Repository: https://bitbucket.org/malb/givaro-spkg + +License +------- + +- GNU GPL + + +Upstream Contact +---------------- + +- Clement Pernet + +Dependencies +------------ + +- GNU patch +- GMP/MPIR diff --git a/build/pkgs/givaro/SPKG.txt b/build/pkgs/givaro/SPKG.txt deleted file mode 100644 index 17ad6f9bec3..00000000000 --- a/build/pkgs/givaro/SPKG.txt +++ /dev/null @@ -1,167 +0,0 @@ -= Givaro = - -== Description == - -Givaro is a C++ library for arithmetic and algebraic computations. Its -main features are implementations of the basic arithmetic of many -mathematical entities: Primes fields, Extensions Fields, Finite -Fields, Finite Rings, Polynomials, Algebraic numbers, Arbitrary -precision integers and rationals (C++ wrappers over gmp) It also -provides data-structures and templated classes for the manipulation of -basic algebraic objects, such as vectors, matrices (dense, sparse, -structured), univariate polynomials (and therefore recursive -multivariate). - -Website: http://www-lmc.imag.fr/CASYS/LOGICIELS/givaro/ - -SPKG Repository: https://bitbucket.org/malb/givaro-spkg - -== License == - - * GNU GPL - -== Upstream Contact == - - * Clement Pernet - -== Dependencies == - - * GNU patch - * GMP/MPIR - -== Changelog == - -=== givaro-4.0.3 (Clement Pernet, 18 November 2017) === - * #24214: updated to new upstream release. - -=== givaro-4.0.2 (Clement Pernet, 30 July 2016) === - * #13164: updated to new upstream release. - -=== givaro-3.7.1 (Jean-Pierre Flori, 9 August 2012) === - * #13164: updated to new upstream release. - -=== givaro-3.7.0 (Martin Albrecht, June 7th, 2012) === - * #9511: updated to new upstream release. - -=== givaro-3.2.13.p0 (Jeroen Demeyer, 25 May 2012) === - * #12761: Restore upstream sources to vanilla 3.2.13 (the previous - src/ directory was some never-released CVS version between - givaro-3.2.13.rc1 and givaro-3.2.13, but bootstrapped with a - different automake). - * Remove gmp++.h.patch which is upstreamed (the old diff was wrong). - * Use `patch` to apply all patches. - * Fix patch for givtablelimits.h such that it can be applied on all - systems, not only Cygwin. - * Merged all GCC-4.7.0 patches into one: cplusplus_scoping.patch - * Don't touch .pyx files, instead fix module_list.py (also on #12761). - -=== givaro-3.2.13.rc1.p4 (Leif Leonhardy, March 27th 2012) === - * #12761: Fix headers not conforming to C++11 to make Sage (especially the - Sage library) build with GCC 4.7.0 (and without `-fpermissive`). - Same for Givaro's test suite, which uses / instantiates much more! - (These headers get installed into `$SAGE_LOCAL/include/givaro/`.) - New patches: - - patches/src.kernel.integer.givintnumtheo.inl.patch - - patches/src.kernel.integer.givintrsa.inl.patch - - patches/src.library.poly1.givpoly1factor.inl.patch - - patches/src.library.poly1.givpoly1padic.h.patch - - patches/src.library.poly1.givpoly1proot.inl.patch - * Remove the obsolete Debian `dist/` directory. - * Remove obsolete GCC 4.3 patch. - * Rename diffs of prepatched files that are (still) copied over to `*.diff` - (rather than `*.patch`) such that they don't get "automatically" applied - by the `patch -p1` loop, which I added. - * Fix permissions of `SPKG.txt` and `spkg-install`, and two upstream files. - * Add "Special Update/Build Instructions" section. - * Clean up `spkg-check` and `spkg-install`. - * Also set up environment variables in `spkg-check`, as `make check` involves - compilation. (Although `configure` should have put them into the generated - Makefiles.) - * Use `$MAKE` in `spkg-check` as well. - * Exit in case the build failed! - * Only `touch` extension modules (`*.pyx`) if they (already) exist. - -=== givaro-3.2.13.rc1.p3 (Simon King, Dec 10th, 2011) === - * #12131: Use --libdir, to make the package work on openSUSE. - -=== givaro-3.2.13rc1.p2 (John Palmieri, June 27th, 2010) === - * #9352: Trivial typo in spkg-check. - -=== givaro-3.2.13rc1.p1 (Willem Jan Palenstijn, Apr 30th, 2010) === - * #8788: Avoid local static object with destructor to prevent double frees - on some platforms. - -=== givaro-3.2.13rc1.p0 (Jaap Spies, Jan 25th, 2010) === - * If $SAGE64="yes" add -m64 to CFLAGS. This works on Open Solaris x64 64 bit. - It used to work only on OSX and may work on other 64 bit systems. - * This is trac http://trac.sagemath.org/sage_trac/ticket/8062 - -=== givaro-3.2.13rc1 (Clement Pernet, Sept 18th, 2008) === - * Fix endianess pb with PPC-OSX - -=== givaro-3.2.12rc0 (Clement Pernet, July 10th, 2008) === - * Upgrade to givaro-3.2.12rc0 - -=== givaro-3.2.11 (Clement Pernet, June 25th, 2008) === - * Upgrade to givaro-3.2.11 (fixing long long issue on 64 bit archs) - -=== givaro-3.2.10.rc3.p3 (Michael Abshoff, May 18th, 2008) === - * improve 64 bit OSX build support - -=== givaro-3.2.10.rc3.p2 (William Stein, May 16th, 2008) === - * Fix cygwin "missing logb declaration" problem. - -=== givaro-3.2.10.rc3.p1 (Michael Abshoff, April 17th, 2008) === - * Fix Itanium specific gcc 4.3 build problem - -=== givaro-3.2.10.rc3.p0 (Michael Abshoff, April 15th, 2008) === - * fix gcc 4.3 build - patch send upstream - -=== givaro-3.2.10.rc3 (Michael Abshoff, March 15th, 2008) === - * update to upstream 3.2.10.rc3 - * add 64 bit OSX 10.5 support - * remove all patches since they were integrated upstream - * add spkg-check - -=== givaro-3.2.10 (Clement Pernet, March 2nd, 2008) === - * Updated to upstream 3.2.10 - * removed most patches - * Note: this was based on 3.2.10.rc2 - -=== givaro-3.2.6.p5 (Michael Abshoff) === - * fix #1091. This copies over updated version of givintrsa.h, - givintfactor.h and givintnumtheo.h into src/src/kernel/integer. - I also keft the patch itself in the patches directory. - -=== 2007-12-06 === - * include in givaromm.C to fix gcc 4.3 issue - -=== 2007-11-02 (Michael Abshoff) === - * apply rpw's work aorund for OSX 10.5 - * apply the same fix to givzpz32std.inl - * add .hgignore - -=== 2007-02-03 (Martin Albrecht) === - * new upstream release: 3.2.6 - * Changes to upstream (everything else below is irrelevant): - ./src/library/poly1/givpoly1factor.h (2006-10-21 fix) - ./src/kernel/zpz/givgfq.inl (2006-10-21 fix) - ./src/kernel/zpz/givgfq.h (2006-10-21 fix) - ./aclocal.m4 64-bit (2006-10-29 fix) - ./src/library/poly1/givpoly1padic.h (2006-11-09 fix) - -=== 2006-12-15 William Stein === - * I don't know why, but I had to comment out "Rep& amxy( Rep& r, const Rep& a, const Rep& b, const Rep& c ) const { return Integer::amxy(r,a,b,c); }" in src/kernel/integer/givinteger.h in order to get Givaro to compile on my computer. I'm guessing maybe amxy was deprecated from the C++ stl? - -=== 2006-12-10 (Martin Albrecht) === - * delete[] not delete where new[] in GivaroMM - -=== 2006-11-09 (Martin Albrecht) === - * GCC 4.0.0 on OSX PPC boxes doesn't seem to like "using Poly1Dom::_domain" so we work around this in givpoly1padic.h - -=== 2006-10-29 (Martin Albrecht) === - * replaced macro AC_LIBTOOL_SYS_DYNAMIC_LINKER with same macro from libtool 1.5.23a in aclocal.m4 to fix build on x86-64 systems as suggested by Kate Minola - -=== 2006-10-21 (Martin Albrecht) === - * ported constructor with modulus parameter from linbox to givaro - * added sage_generator() which returns the generator == cardinality if interpreted as an integer not a random one diff --git a/build/pkgs/givaro/distros/fedora.txt b/build/pkgs/givaro/distros/fedora.txt index 9540863331f..6848d8c4e7f 100644 --- a/build/pkgs/givaro/distros/fedora.txt +++ b/build/pkgs/givaro/distros/fedora.txt @@ -1 +1 @@ -givaro-devel +givaro givaro-devel diff --git a/build/pkgs/givaro/distros/gentoo.txt b/build/pkgs/givaro/distros/gentoo.txt new file mode 100644 index 00000000000..66fe0f086e2 --- /dev/null +++ b/build/pkgs/givaro/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/givaro diff --git a/build/pkgs/givaro/patches/fix-ksh-pkgconfig.patch b/build/pkgs/givaro/patches/fix-ksh-pkgconfig.patch new file mode 100644 index 00000000000..28fd95088c8 --- /dev/null +++ b/build/pkgs/givaro/patches/fix-ksh-pkgconfig.patch @@ -0,0 +1,27 @@ +From 91dcba743e15288abe69966a5f71704d9adcc57c Mon Sep 17 00:00:00 2001 +From: Dima Pasechnik +Date: Fri, 8 May 2020 10:22:57 +0100 +Subject: [PATCH 1/1] remove 1st and last lines in givaro.pc.in + +--- + givaro.pc.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/givaro.pc.in b/givaro.pc.in +index 285b854..af38bf3 100644 +--- a/givaro.pc.in ++++ b/givaro.pc.in +@@ -1,4 +1,3 @@ +-/------------------ givaro.pc ------------------------ + prefix=@prefix@ + exec_prefix=@prefix@ + libdir=@prefix@/lib +@@ -11,4 +10,3 @@ Version: @VERSION@ + Requires: + Libs: -L@libdir@ -lgivaro @LIBS@ + Cflags: -I@includedir@ @REQUIRED_FLAGS@ +-\------------------------------------------------------- +\ No newline at end of file +-- +2.26.2 + diff --git a/build/pkgs/givaro/spkg-check b/build/pkgs/givaro/spkg-check deleted file mode 100644 index cae9295df9a..00000000000 --- a/build/pkgs/givaro/spkg-check +++ /dev/null @@ -1,28 +0,0 @@ -if [[ -z "$SAGE_LOCAL" ]]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -################################# -# Set up environment variables: # -################################# - -# It shouldn't be necessary to add Sage's include directory here, -# since we configure with '--with-gmp=...'. -# Also, '-I...' should normally be added to (just) CPPFLAGS. -CFLAGS="$CFLAGS -fPIC -I\"$SAGE_LOCAL/include\"" -CXXFLAGS="$CXXFLAGS -fPIC -I\"$SAGE_LOCAL/include\"" - -export CFLAGS CPPFLAGS CXXFLAGS LDFLAGS - - -cd src/ - -echo "Building and running Givaro's test suite..." -$MAKE check -if [[ $? -ne 0 ]]; then - echo >&2 "Error while building or running Givaro's test suite." - exit 1 -fi -echo "Givaro's test suite passed." diff --git a/build/pkgs/givaro/spkg-check.in b/build/pkgs/givaro/spkg-check.in new file mode 100644 index 00000000000..d3d631b3896 --- /dev/null +++ b/build/pkgs/givaro/spkg-check.in @@ -0,0 +1,17 @@ +################################# +# Set up environment variables: # +################################# + +# It shouldn't be necessary to add Sage's include directory here, +# since we configure with '--with-gmp=...'. +# Also, '-I...' should normally be added to (just) CPPFLAGS. +CFLAGS="$CFLAGS -fPIC -I\"$SAGE_LOCAL/include\"" +CXXFLAGS="$CXXFLAGS -fPIC -I\"$SAGE_LOCAL/include\"" + +export CFLAGS CPPFLAGS CXXFLAGS LDFLAGS + + +cd src/ + +echo "Building and running Givaro's test suite..." +$MAKE check diff --git a/build/pkgs/givaro/spkg-install b/build/pkgs/givaro/spkg-install.in similarity index 100% rename from build/pkgs/givaro/spkg-install rename to build/pkgs/givaro/spkg-install.in diff --git a/build/pkgs/glpk/SPKG.rst b/build/pkgs/glpk/SPKG.rst new file mode 100644 index 00000000000..5dfc404acd5 --- /dev/null +++ b/build/pkgs/glpk/SPKG.rst @@ -0,0 +1,72 @@ +GLPK +==== + +Description +----------- + +The GLPK (GNU Linear Programming Kit) package is intended for solving +large-scale linear programming (LP), mixed integer programming (MIP), +and other related problems. It is a set of routines written in ANSI C +and organized in the form of a callable library. + +GLPK supports the GNU MathProg modelling language, which is a subset of +the AMPL language. + +The GLPK package includes the following main components: + +- primal and dual simplex methods +- primal-dual interior-point method +- branch-and-cut method +- translator for GNU MathProg +- application program interface (API) +- stand-alone LP/MIP solver + +License +------- + +The GLPK package is GPL version 3. + + +Upstream Contact +---------------- + +GLPK is currently being maintained by: + +- Andrew Makhorin (mao@gnu.org, mao@mai2.rcnet.ru) + +http://www.gnu.org/software/glpk/#maintainer + +Dependencies +------------ + +- GMP/MPIR +- zlib + + +Special Update/Build Instructions +--------------------------------- + +- ``configure`` doesn't support specifying the location of the GMP + library to use; only ``--with-gmp[=yes]`` or ``--with-gmp=no`` + are valid options. (So we \*have to\* add Sage's include and + library directories to ``CPPFLAGS`` and ``LDFLAGS``, respectively.) + +- Do we need the ``--disable-static``? The stand-alone solver presumably + runs faster when built with a static library; also other + (stand-alone) + programs using it would. + (Instead, we should perhaps use ``--enable-static --enable-shared`` + to + go safe.) + +Patches +~~~~~~~ + +- All patches below are currently used by spkg-src +- src/01-zlib.patch: don't build the included zlib library. +- src/02-cygwin_sharedlib.patch: Let a shared library be built on + Cygwin by + passing the -no-undefined flag to libtool. + + The numbering reflect the order in which they have been created from + glpk pristine's sources diff --git a/build/pkgs/glpk/SPKG.txt b/build/pkgs/glpk/SPKG.txt deleted file mode 100644 index 5dfc5a169e0..00000000000 --- a/build/pkgs/glpk/SPKG.txt +++ /dev/null @@ -1,59 +0,0 @@ -= GLPK = - -== Description == - -The GLPK (GNU Linear Programming Kit) package is intended for solving -large-scale linear programming (LP), mixed integer programming (MIP), -and other related problems. It is a set of routines written in ANSI C -and organized in the form of a callable library. - -GLPK supports the GNU MathProg modelling language, which is a subset of -the AMPL language. - -The GLPK package includes the following main components: - - * primal and dual simplex methods - * primal-dual interior-point method - * branch-and-cut method - * translator for GNU MathProg - * application program interface (API) - * stand-alone LP/MIP solver - -== License == - -The GLPK package is GPL version 3. - -== Upstream Contact == - -GLPK is currently being maintained by: - - * Andrew Makhorin (mao@gnu.org, mao@mai2.rcnet.ru) - -http://www.gnu.org/software/glpk/#maintainer - -== Dependencies == - - * GMP/MPIR - * zlib - -== Special Update/Build Instructions == - - * `configure` doesn't support specifying the location of the GMP - library to use; only `--with-gmp[=yes]` or `--with-gmp=no` - are valid options. (So we *have to* add Sage's include and - library directories to `CPPFLAGS` and `LDFLAGS`, respectively.) - * Do we need the `--disable-static`? The stand-alone solver presumably - runs faster when built with a static library; also other (stand-alone) - programs using it would. - (Instead, we should perhaps use `--enable-static --enable-shared` to - go safe.) - -=== Patches === - - * All patches below are currently used by spkg-src - * src/01-zlib.patch: don't build the included zlib library. - * src/02-cygwin_sharedlib.patch: Let a shared library be built on Cygwin by - passing the -no-undefined flag to libtool. - - The numbering reflect the order in which they have been created from - glpk pristine's sources diff --git a/build/pkgs/glpk/distros/arch.txt b/build/pkgs/glpk/distros/arch.txt new file mode 100644 index 00000000000..aca7917cfa1 --- /dev/null +++ b/build/pkgs/glpk/distros/arch.txt @@ -0,0 +1 @@ +glpk diff --git a/build/pkgs/glpk/distros/cygwin.txt b/build/pkgs/glpk/distros/cygwin.txt new file mode 100644 index 00000000000..2e6c6a10cb8 --- /dev/null +++ b/build/pkgs/glpk/distros/cygwin.txt @@ -0,0 +1 @@ +glpk libglpk-devel diff --git a/build/pkgs/glpk/distros/debian.txt b/build/pkgs/glpk/distros/debian.txt new file mode 100644 index 00000000000..892d31668c0 --- /dev/null +++ b/build/pkgs/glpk/distros/debian.txt @@ -0,0 +1 @@ +glpk-utils libglpk-dev diff --git a/build/pkgs/glpk/distros/fedora.txt b/build/pkgs/glpk/distros/fedora.txt new file mode 100644 index 00000000000..73ab7f14b14 --- /dev/null +++ b/build/pkgs/glpk/distros/fedora.txt @@ -0,0 +1 @@ +glpk glpk-devel glpk-utils diff --git a/build/pkgs/glpk/distros/gentoo.txt b/build/pkgs/glpk/distros/gentoo.txt new file mode 100644 index 00000000000..81570a9366d --- /dev/null +++ b/build/pkgs/glpk/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/glpk diff --git a/build/pkgs/glpk/distros/homebrew.txt b/build/pkgs/glpk/distros/homebrew.txt new file mode 100644 index 00000000000..aca7917cfa1 --- /dev/null +++ b/build/pkgs/glpk/distros/homebrew.txt @@ -0,0 +1 @@ +glpk diff --git a/build/pkgs/glpk/spkg-check b/build/pkgs/glpk/spkg-check deleted file mode 100644 index e1934d21191..00000000000 --- a/build/pkgs/glpk/spkg-check +++ /dev/null @@ -1,22 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting"; - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -# Let GLPK use Sage's GMP/MPIR (cf. comments in SPKG.txt and spkg-install): -CPPFLAGS="-I$SAGE_LOCAL/include $CPPFLAGS" -LDFLAGS="-L$SAGE_LOCAL/lib $LDFLAGS" - -# No need to (re)export LDFLAGS etc., as `sage-env` does this. -# But it currently *doesn't* export CPPFLAGS. For safety, export them all: -export CPPFLAGS LDFLAGS - -cd src/ - -echo "Running GLPK's testsuite..." -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error testing GLPK." - exit 1 -fi diff --git a/build/pkgs/glpk/spkg-check.in b/build/pkgs/glpk/spkg-check.in new file mode 100644 index 00000000000..d4a910e493a --- /dev/null +++ b/build/pkgs/glpk/spkg-check.in @@ -0,0 +1,11 @@ +# Let GLPK use Sage's GMP/MPIR (cf. comments in SPKG.txt and spkg-install): +CPPFLAGS="-I$SAGE_LOCAL/include $CPPFLAGS" +LDFLAGS="-L$SAGE_LOCAL/lib $LDFLAGS" + +# No need to (re)export LDFLAGS etc., as `sage-env` does this. +# But it currently *doesn't* export CPPFLAGS. For safety, export them all: +export CPPFLAGS LDFLAGS + +cd src/ + +$MAKE check diff --git a/build/pkgs/glpk/spkg-install b/build/pkgs/glpk/spkg-install.in similarity index 100% rename from build/pkgs/glpk/spkg-install rename to build/pkgs/glpk/spkg-install.in diff --git a/build/pkgs/glucose/SPKG.rst b/build/pkgs/glucose/SPKG.rst new file mode 100644 index 00000000000..d37117825b2 --- /dev/null +++ b/build/pkgs/glucose/SPKG.rst @@ -0,0 +1,45 @@ +glucose +======= + +Description +----------- + +Glucose is a SAT solver. + +Citing its website: \*The name of the solver is a contraction of the +concept of "glue clauses", a particular kind of clauses that glucose +detects and preserves during search. Glucose is heavily based on +Minisat, so please do cite Minisat also if you want to cite Glucose.\* + +License +------- + +- nonparallel glucose: MIT + +- parallel glucose-syrup: MIT modified with: + + The parallel version of Glucose (all files modified since Glucose 3.0 + releases, 2013) cannot be used in any competitive event (sat + competitions/evaluations) without the express permission of the + authors + (Gilles Audemard / Laurent Simon). This is also the case for any + competitive + event using Glucose Parallel as an embedded SAT engine (single core + or not). + + +Upstream Contact +---------------- + +Website: http://www.labri.fr/perso/lsimon/glucose/ + +Dependencies +------------ + +zlib + + +Special Update/Build Instructions +--------------------------------- + +None. diff --git a/build/pkgs/glucose/SPKG.txt b/build/pkgs/glucose/SPKG.txt deleted file mode 100644 index e2871477ba4..00000000000 --- a/build/pkgs/glucose/SPKG.txt +++ /dev/null @@ -1,35 +0,0 @@ -= glucose = - -== Description == - -Glucose is a SAT solver. - -Citing its website: *The name of the solver is a contraction of the concept of -"glue clauses", a particular kind of clauses that glucose detects and preserves -during search. Glucose is heavily based on Minisat, so please do cite Minisat -also if you want to cite Glucose.* - -== License == - -- nonparallel glucose: MIT - -- parallel glucose-syrup: MIT modified with: - - The parallel version of Glucose (all files modified since Glucose 3.0 - releases, 2013) cannot be used in any competitive event (sat - competitions/evaluations) without the express permission of the authors - (Gilles Audemard / Laurent Simon). This is also the case for any competitive - event using Glucose Parallel as an embedded SAT engine (single core or not). - -== Upstream Contact == - -Website: http://www.labri.fr/perso/lsimon/glucose/ - -== Dependencies == - -zlib - -== Special Update/Build Instructions == - -None. - diff --git a/build/pkgs/glucose/spkg-install b/build/pkgs/glucose/spkg-install.in similarity index 100% rename from build/pkgs/glucose/spkg-install rename to build/pkgs/glucose/spkg-install.in diff --git a/build/pkgs/gmp/SPKG.rst b/build/pkgs/gmp/SPKG.rst new file mode 100644 index 00000000000..c9103d3e5ca --- /dev/null +++ b/build/pkgs/gmp/SPKG.rst @@ -0,0 +1,22 @@ +GMP +=== + +Description +----------- + +GMP is a free library for arbitrary precision arithmetic, operating on +signed integers, rational numbers, and floating-point numbers. There is +no practical limit to the precision except the ones implied by the +available memory in the machine GMP runs on. GMP has a rich set of +functions, and the functions have a regular interface. + +License +------- + +- LGPL V3 + + +Upstream Contact +---------------- + +- http://gmplib.org diff --git a/build/pkgs/gmp/SPKG.txt b/build/pkgs/gmp/SPKG.txt deleted file mode 100644 index 2d0a54911db..00000000000 --- a/build/pkgs/gmp/SPKG.txt +++ /dev/null @@ -1,15 +0,0 @@ -= GMP = - -== Description == - -GMP is a free library for arbitrary precision arithmetic, operating on signed -integers, rational numbers, and floating-point numbers. -There is no practical limit to the precision except the ones implied by the available memory in the machine GMP runs on. -GMP has a rich set of functions, and the functions have a regular interface. - -== License == - * LGPL V3 - -== Upstream Contact == - * http://gmplib.org - diff --git a/build/pkgs/gmp/distros/cygwin.txt b/build/pkgs/gmp/distros/cygwin.txt new file mode 100644 index 00000000000..bde3aa97bd3 --- /dev/null +++ b/build/pkgs/gmp/distros/cygwin.txt @@ -0,0 +1 @@ +libgmp-devel diff --git a/build/pkgs/gmp/distros/gentoo.txt b/build/pkgs/gmp/distros/gentoo.txt new file mode 100644 index 00000000000..48f89aa34a0 --- /dev/null +++ b/build/pkgs/gmp/distros/gentoo.txt @@ -0,0 +1 @@ +dev-libs/gmp diff --git a/build/pkgs/gmp/distros/homebrew.txt b/build/pkgs/gmp/distros/homebrew.txt new file mode 100644 index 00000000000..a0a04787c06 --- /dev/null +++ b/build/pkgs/gmp/distros/homebrew.txt @@ -0,0 +1 @@ +gmp diff --git a/build/pkgs/gmp/distros/slackware.txt b/build/pkgs/gmp/distros/slackware.txt new file mode 100644 index 00000000000..a0a04787c06 --- /dev/null +++ b/build/pkgs/gmp/distros/slackware.txt @@ -0,0 +1 @@ +gmp diff --git a/build/pkgs/gmp/spkg-check b/build/pkgs/gmp/spkg-check deleted file mode 100644 index f6b9b0a6f29..00000000000 --- a/build/pkgs/gmp/spkg-check +++ /dev/null @@ -1,19 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -# We don't have to set up any environment variables here since the -# Makefiles already have them from 'configure'. - -echo "Now building and running GMP's test suite..." -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error: The GMP test suite failed." - exit 1 -fi - -echo "The GMP test suite passed successfully." diff --git a/build/pkgs/gmp/spkg-check.in b/build/pkgs/gmp/spkg-check.in new file mode 100644 index 00000000000..4727de00ffe --- /dev/null +++ b/build/pkgs/gmp/spkg-check.in @@ -0,0 +1,6 @@ +cd src + +# We don't have to set up any environment variables here since the +# Makefiles already have them from 'configure'. + +$MAKE check diff --git a/build/pkgs/gmp/spkg-install b/build/pkgs/gmp/spkg-install deleted file mode 100644 index 611da0f1e6c..00000000000 --- a/build/pkgs/gmp/spkg-install +++ /dev/null @@ -1,289 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src/ - -############################################################################### -# Previous GMP installations are only removed after a *successful* (re)build, -# before installing the new one. (Done below.) -############################################################################### - -############################################################################### -# Set up environment variables: -############################################################################### - -user_cflags=$CFLAGS # Save them. 'sage-env' sets CC, but not CFLAGS. -required_cflags="" # Additional mandatory settings required by Sage, accumulated below. -user_ldflags=$LDFLAGS # Save them. -required_ldflags="" # Additional mandatory settings required by Sage, accumulated below. -user_abi=$ABI # Just save it. -# In case we changed CPPFLAGS or CXXFLAGS, too, we should save the user's here as well. -# We don't have to add (e.g.) '-m64' to CFLAGS/CPPFLAGS/CXXFLAGS/LDFLAGS, since -# GMP's 'configure' is smart enough to add it if necessary or appropriate. - - -if [ -z "$CFLAG32" ]; then - CFLAG32="-m32" # Only used in this script, no need to export it. -fi -if [ -z "$CFLAG64" ]; then - CFLAG64="-m64" # Only used in this script, no need to export it. -fi - - -if [ "$SAGE_DEBUG" = yes ]; then - # Disable optimization, add debug symbols: - required_cflags="$required_cflags -g -O0" - echo >&2 "Warning: Building GMP with SAGE_DEBUG=yes disables optimization." -else - # Add debug symbols by default - required_cflags="$required_cflags -g" -fi - - -case "$UNAME" in - SunOS) - true;; # Auto-detect ABI - Darwin) - # In some cases (see SAGE_ROOT/spkg/bin/sage-env), on Darwin, - # CC might be set to clang, but GMP doesn't seem to build - # with clang. - CLANG=`command -v clang` - GCC=`command -v gcc` - if [ -n "$CC" ] && [ "$CC" = "$CLANG" ] && [ -n "$GCC" ] ; then - export CC="$GCC" - fi - # Do not set ABI=32 on MacOS X 10.6 (Darwin 10) and later, since - # there everything defaults to 64-bit: - if [ "`uname -r | sed 's/\..*//'`" -lt 10 ]; then - # Assume MacOS X 10.4 or 10.5 (Darwin 8 or 9); also, PPC CPUs - # are only supported by these, not later versions. - echo "Building a 32-bit version of GMP, which is the only supported option." - ABI=32 - case "`uname -m`" in - ppc|ppc64|[Pp]ower*) # Apple's 'uname' returns strange strings - # The Darwin assembler rejects code using an - # extended instruction set by default (cf. #8664): - required_cflags="$required_cflags -Wa,-force_cpusubtype_ALL" - ;; - esac - else - # Darwin 10 (MacOS X 10.6) or later. - # We don't have to set ABI here. - echo "Building a 64-bit version of GMP, which is the default." - fi - ;; # Darwin - Linux) - # GMP fails to build on 32-bit operating systems running on - # 64-bit CPUs if CFLAGS happen to contain '-m32' and ABI is - # *not* set, so we set it here if necessary: - # (Cf. http://groups.google.com/group/gmp-devel/browse_thread/thread/46ccdc5dfc3485cd#) - # Note: This code snippet could in principle be moved out of the - # Linux branch, but since we already set ABI for other - # OSs above (and print an according message), it's here. - if [ -z "$ABI" ]; then - echo "int main(){return 0;}" > foo.c - # Try building and running a 64-bit executable: - # (Building usually succeeds even on 32-bit systems, unless e.g. a 32-bit - # CPU is explicitly selected by CFLAGS, while running does not.) - if $CC $CFLAGS $CFLAG64 -o foo foo.c 2>/dev/null && ./foo 2>/dev/null; then - # We can run 64-bit executables. - # Setting ABI=64 shouldn't be necessary, but shouldn't hurt either. - echo "Building a 64-bit version of GMP." - case "`uname -m`" in - ppc*) ABI=mode64;; - *) ABI=64 - esac - elif $CC $CFLAGS $CFLAG32 -o foo foo.c 2>/dev/null && ./foo 2>/dev/null; then - # We're on a 32-bit OS which cannot run 64-bit executables. - echo "Building a 32-bit version of GMP." - ABI=32 - else - # It seems the compiler does not support -m32 nor -m64 (e.g. - # GCC on Itanium rejects both); do not set ABI at all. - echo "Your compiler does not support '$CFLAG32' nor '$CFLAG64'. Leaving ABI unset." - fi - rm -f foo foo.c - fi - ;; # Linux - *) # e.g. AIX or HP-UX - echo >&2 "Warning: Your platform ($UNAME) isn't yet explicitly supported" \ - "by this GMP spkg, i.e., by Sage's part of it." -esac - -# Work around a bug in GCC 4.7.0 which breaks the build on Itanium CPUs. -# See #12765, #12751, and http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48496 -if [ "`uname -m`" = ia64 ] && [ "`testcc.sh $CC`" = GCC ] ; then - gcc_version=`$CC -dumpversion` - case "$gcc_version" in - 4.7.0) - required_cflags="$required_cflags -O0 -finline-functions -fschedule-insns" - echo >&2 "Warning: Disabling almost all optimization due to a bug in GCC 4.7.0" - echo >&2 " on Itanium, which otherwise would break the build." - echo >&2 " See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48496" - echo >&2 " for current status and further details." - ;; - esac -fi - -export ABI CFLAGS CXXFLAGS LDFLAGS # Partially redundant, but safe(r). -# We don't export CPPFLAGS here, since we don't (have to) modify them. - -############################################################################### -# Now configure GMP, eventually modifying CFLAGS [further]: -############################################################################### - -GMP_CONFIGURE="--enable-shared $GMP_CONFIGURE" - -# Also build the static library to be used by e.g. ECM -# unless we are on Cygwin where we can only build a shared -# or a static library but not both: -if [ "$UNAME" = "CYGWIN" ]; then - echo "Building GMP with the C++ interface and (only) shared libraries." - GMP_CONFIGURE="--enable-cxx $GMP_CONFIGURE --disable-static" -else - echo "Building GMP with the C++ interface and (also) static libraries." - GMP_CONFIGURE="--enable-cxx --enable-static $GMP_CONFIGURE" -fi - -# If SAGE_FAT_BINARY is enabled, then add --enable-fat to configure -# options on Linux x86 systems. On other systems, fat binaries are not -# supported. Then we specify a build architecture which doesn't -# have a CPU name in it. This means which use the vanilla config.guess -# (renamed to configfsf.guess in GMP) file instead of GMP's version. -if [ "$SAGE_FAT_BINARY" = "yes" ]; then - case "$UNAME-`uname -m`" in - Linux-i[3456]86) - echo "** Building with \"fat binary\" support for 32-bit CPUs **" - GMP_CONFIGURE="--enable-fat $GMP_CONFIGURE" - ;; - Linux-x86_64|Linux-amd64) - echo "** Building with \"fat binary\" support for 64-bit CPUs **" - GMP_CONFIGURE="--enable-fat $GMP_CONFIGURE" - ;; - *) # Anything else - echo "** Building a generic binary (not assuming any specific CPU) **" - GMP_CONFIGURE="--build=`./configfsf.guess` $GMP_CONFIGURE" - ;; - esac -fi - - -# Pre-configure GMP to get the settings it would use if CFLAGS were empty: -echo "Checking what CFLAGS GMP would use if they were empty..." -(unset CFLAGS CPPFLAGS CXXFLAGS && ./configure $GMP_CONFIGURE) &>configure-empty.log -if [ $? -ne 0 ]; then - # Output the log of the failed configure run - cat configure-empty.log - echo >&2 "Error configuring GMP (with CFLAGS unset)." - echo >&2 "Consult `pwd`/config.log for for details." - exit 1 -fi - - -# Read GMP-selected flags from Makefile -gmp_cc=`sed -n 's/^CC *= *//p' Makefile` -gmp_cflags=`sed -n 's/^CFLAGS *= *//p' Makefile` -if [ -z "$gmp_cc" ]; then - echo >&2 "Error: failed to determine \$CC from Makefile" - echo >&2 "Please report this to " - exit 1 -fi -echo "Settings chosen by GMP when configuring with CFLAGS unset:" -echo " CC: $gmp_cc" -echo " CFLAGS: $gmp_cflags" -echo "Settings added by Sage to build GMP, taking into account SAGE_DEBUG etc.:" -echo " CFLAGS: $required_cflags" # Might be empty. -echo " LDFLAGS: $required_ldflags" # Might be empty. -echo " ABI: $ABI" # Might be empty, or the one specified by the user. -echo "Settings from the \"global\" environment:" -echo " CC: $CC" # Set by Sage, maybe overridden by the user. -echo " CFLAGS: $user_cflags" -echo " LDFLAGS: $user_ldflags" -echo " ABI: $user_abi" -echo " (CPP, CPPFLAGS, CXX and CXXFLAGS are listed below; these don't get modified.)" - -# Finally: use GMP's flags, plus those required by Sage for the -# package to build properly, plus those specified by the user. -CFLAGS="$gmp_cflags $required_cflags $user_cflags" -LDFLAGS="$required_ldflags $user_ldflags" - -echo "Finally using the following settings:" -echo " CC=$CC" -echo " CFLAGS=$CFLAGS" -echo " CPP=$CPP" -echo " CPPFLAGS=$CPPFLAGS" -echo " CXX=$CXX" -echo " CXXFLAGS=$CXXFLAGS" -echo " LDFLAGS=$LDFLAGS" -echo " ABI=$ABI" -echo "(These settings may still get overridden by 'configure' or Makefiles.)" - -############################################################################### -# Now really configure GMP with proper settings: -############################################################################### - -# We also add '--libdir="$SAGE_LOCAL/lib"' below, since newer autotools may -# otherwise put the libraries into .../lib64 on 64-bit systems (cf. #12131). - -echo "Configuring GMP with the following options:" -echo " --prefix=\"$SAGE_LOCAL\" --libdir=\"$SAGE_LOCAL/lib\" $GMP_CONFIGURE" -echo "You can set GMP_CONFIGURE to pass additional parameters." - -# Clear the cache of the previous configure run -find . -name config.cache -exec rm -f {} \; - -./configure --prefix="$SAGE_LOCAL" --libdir="$SAGE_LOCAL/lib" $GMP_CONFIGURE -if [ $? -ne 0 ]; then - echo >&2 "Error configuring GMP. (See above for the options passed to it.)" - exit 1 -fi - -############################################################################### -# Now build GMP: -############################################################################### - -echo "Now building GMP..." -$MAKE -if [ $? -ne 0 ]; then - echo >&2 "Error building GMP." - exit 1 -fi - -echo "Build succeeded." - -############################################################################### -# Remove previous installation (if any), *after* a successful build: -############################################################################### - -echo "Removing old GMP/GMPIR headers..." -rm -f "$SAGE_LOCAL"/include/{gmp,mpir}*.h - -# Do NOT delete old GMP/GMP shared libraries as Sage's versions of libraries -# used by GCC might still refer to them, such that their deletion would break -# GCC inside Sage. (We could perhaps remove libgmp* though.) -if false; then - echo "Removing old GMP/MPIR libraries..." - rm -f "$SAGE_LOCAL"/lib/lib{gmp,mpir}* -else - echo "Not removing old GMP/MPIR shared libraries, as other libraries" - echo "and executables might still refer to them:" - ls -l "$SAGE_LOCAL"/lib/lib{gmp,mpir}* - echo "(Libraries with the same version number will get updated though.)" -fi - -# Mark MPIR as not installed (since we will overwrite it) -rm -f "$SAGE_SPKG_INST"/mpir-* - -############################################################################### -# Now install GMP: -############################################################################### - -echo "Now installing GMP..." -$MAKE install -if [ $? -ne 0 ]; then - echo >&2 "Error installing GMP." - exit 1 -fi diff --git a/build/pkgs/gmp/spkg-install.in b/build/pkgs/gmp/spkg-install.in new file mode 100644 index 00000000000..61096b76cab --- /dev/null +++ b/build/pkgs/gmp/spkg-install.in @@ -0,0 +1,283 @@ +cd src/ + +############################################################################### +# Previous GMP installations are only removed after a *successful* (re)build, +# before installing the new one. (Done below.) +############################################################################### + +############################################################################### +# Set up environment variables: +############################################################################### + +user_cflags=$CFLAGS # Save them. 'sage-env' sets CC, but not CFLAGS. +required_cflags="" # Additional mandatory settings required by Sage, accumulated below. +user_ldflags=$LDFLAGS # Save them. +required_ldflags="" # Additional mandatory settings required by Sage, accumulated below. +user_abi=$ABI # Just save it. +# In case we changed CPPFLAGS or CXXFLAGS, too, we should save the user's here as well. +# We don't have to add (e.g.) '-m64' to CFLAGS/CPPFLAGS/CXXFLAGS/LDFLAGS, since +# GMP's 'configure' is smart enough to add it if necessary or appropriate. + + +if [ -z "$CFLAG32" ]; then + CFLAG32="-m32" # Only used in this script, no need to export it. +fi +if [ -z "$CFLAG64" ]; then + CFLAG64="-m64" # Only used in this script, no need to export it. +fi + + +if [ "$SAGE_DEBUG" = yes ]; then + # Disable optimization, add debug symbols: + required_cflags="$required_cflags -g -O0" + echo >&2 "Warning: Building GMP with SAGE_DEBUG=yes disables optimization." +else + # Add debug symbols by default + required_cflags="$required_cflags -g" +fi + + +case "$UNAME" in + SunOS) + true;; # Auto-detect ABI + Darwin) + # In some cases (see SAGE_ROOT/spkg/bin/sage-env), on Darwin, + # CC might be set to clang, but GMP doesn't seem to build + # with clang. + CLANG=`command -v clang` + GCC=`command -v gcc` + if [ -n "$CC" ] && [ "$CC" = "$CLANG" ] && [ -n "$GCC" ] ; then + export CC="$GCC" + fi + # Do not set ABI=32 on MacOS X 10.6 (Darwin 10) and later, since + # there everything defaults to 64-bit: + if [ "`uname -r | sed 's/\..*//'`" -lt 10 ]; then + # Assume MacOS X 10.4 or 10.5 (Darwin 8 or 9); also, PPC CPUs + # are only supported by these, not later versions. + echo "Building a 32-bit version of GMP, which is the only supported option." + ABI=32 + case "`uname -m`" in + ppc|ppc64|[Pp]ower*) # Apple's 'uname' returns strange strings + # The Darwin assembler rejects code using an + # extended instruction set by default (cf. #8664): + required_cflags="$required_cflags -Wa,-force_cpusubtype_ALL" + ;; + esac + else + # Darwin 10 (MacOS X 10.6) or later. + # We don't have to set ABI here. + echo "Building a 64-bit version of GMP, which is the default." + fi + ;; # Darwin + Linux) + # GMP fails to build on 32-bit operating systems running on + # 64-bit CPUs if CFLAGS happen to contain '-m32' and ABI is + # *not* set, so we set it here if necessary: + # (Cf. http://groups.google.com/group/gmp-devel/browse_thread/thread/46ccdc5dfc3485cd#) + # Note: This code snippet could in principle be moved out of the + # Linux branch, but since we already set ABI for other + # OSs above (and print an according message), it's here. + if [ -z "$ABI" ]; then + echo "int main(){return 0;}" > foo.c + # Try building and running a 64-bit executable: + # (Building usually succeeds even on 32-bit systems, unless e.g. a 32-bit + # CPU is explicitly selected by CFLAGS, while running does not.) + if $CC $CFLAGS $CFLAG64 -o foo foo.c 2>/dev/null && ./foo 2>/dev/null; then + # We can run 64-bit executables. + # Setting ABI=64 shouldn't be necessary, but shouldn't hurt either. + echo "Building a 64-bit version of GMP." + case "`uname -m`" in + ppc*) ABI=mode64;; + *) ABI=64 + esac + elif $CC $CFLAGS $CFLAG32 -o foo foo.c 2>/dev/null && ./foo 2>/dev/null; then + # We're on a 32-bit OS which cannot run 64-bit executables. + echo "Building a 32-bit version of GMP." + ABI=32 + else + # It seems the compiler does not support -m32 nor -m64 (e.g. + # GCC on Itanium rejects both); do not set ABI at all. + echo "Your compiler does not support '$CFLAG32' nor '$CFLAG64'. Leaving ABI unset." + fi + rm -f foo foo.c + fi + ;; # Linux + *) # e.g. AIX or HP-UX + echo >&2 "Warning: Your platform ($UNAME) isn't yet explicitly supported" \ + "by this GMP spkg, i.e., by Sage's part of it." +esac + +# Work around a bug in GCC 4.7.0 which breaks the build on Itanium CPUs. +# See #12765, #12751, and http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48496 +if [ "`uname -m`" = ia64 ] && [ "`testcc.sh $CC`" = GCC ] ; then + gcc_version=`$CC -dumpversion` + case "$gcc_version" in + 4.7.0) + required_cflags="$required_cflags -O0 -finline-functions -fschedule-insns" + echo >&2 "Warning: Disabling almost all optimization due to a bug in GCC 4.7.0" + echo >&2 " on Itanium, which otherwise would break the build." + echo >&2 " See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48496" + echo >&2 " for current status and further details." + ;; + esac +fi + +export ABI CFLAGS CXXFLAGS LDFLAGS # Partially redundant, but safe(r). +# We don't export CPPFLAGS here, since we don't (have to) modify them. + +############################################################################### +# Now configure GMP, eventually modifying CFLAGS [further]: +############################################################################### + +GMP_CONFIGURE="--enable-shared $GMP_CONFIGURE" + +# Also build the static library to be used by e.g. ECM +# unless we are on Cygwin where we can only build a shared +# or a static library but not both: +if [ "$UNAME" = "CYGWIN" ]; then + echo "Building GMP with the C++ interface and (only) shared libraries." + GMP_CONFIGURE="--enable-cxx $GMP_CONFIGURE --disable-static" +else + echo "Building GMP with the C++ interface and (also) static libraries." + GMP_CONFIGURE="--enable-cxx --enable-static $GMP_CONFIGURE" +fi + +# If SAGE_FAT_BINARY is enabled, then add --enable-fat to configure +# options on Linux x86 systems. On other systems, fat binaries are not +# supported. Then we specify a build architecture which doesn't +# have a CPU name in it. This means which use the vanilla config.guess +# (renamed to configfsf.guess in GMP) file instead of GMP's version. +if [ "$SAGE_FAT_BINARY" = "yes" ]; then + case "$UNAME-`uname -m`" in + Linux-i[3456]86) + echo "** Building with \"fat binary\" support for 32-bit CPUs **" + GMP_CONFIGURE="--enable-fat $GMP_CONFIGURE" + ;; + Linux-x86_64|Linux-amd64) + echo "** Building with \"fat binary\" support for 64-bit CPUs **" + GMP_CONFIGURE="--enable-fat $GMP_CONFIGURE" + ;; + *) # Anything else + echo "** Building a generic binary (not assuming any specific CPU) **" + GMP_CONFIGURE="--build=`./configfsf.guess` $GMP_CONFIGURE" + ;; + esac +fi + + +# Pre-configure GMP to get the settings it would use if CFLAGS were empty: +echo "Checking what CFLAGS GMP would use if they were empty..." +(unset CFLAGS CPPFLAGS CXXFLAGS && ./configure $GMP_CONFIGURE) &>configure-empty.log +if [ $? -ne 0 ]; then + # Output the log of the failed configure run + cat configure-empty.log + echo >&2 "Error configuring GMP (with CFLAGS unset)." + echo >&2 "Consult `pwd`/config.log for for details." + exit 1 +fi + + +# Read GMP-selected flags from Makefile +gmp_cc=`sed -n 's/^CC *= *//p' Makefile` +gmp_cflags=`sed -n 's/^CFLAGS *= *//p' Makefile` +if [ -z "$gmp_cc" ]; then + echo >&2 "Error: failed to determine \$CC from Makefile" + echo >&2 "Please report this to " + exit 1 +fi +echo "Settings chosen by GMP when configuring with CFLAGS unset:" +echo " CC: $gmp_cc" +echo " CFLAGS: $gmp_cflags" +echo "Settings added by Sage to build GMP, taking into account SAGE_DEBUG etc.:" +echo " CFLAGS: $required_cflags" # Might be empty. +echo " LDFLAGS: $required_ldflags" # Might be empty. +echo " ABI: $ABI" # Might be empty, or the one specified by the user. +echo "Settings from the \"global\" environment:" +echo " CC: $CC" # Set by Sage, maybe overridden by the user. +echo " CFLAGS: $user_cflags" +echo " LDFLAGS: $user_ldflags" +echo " ABI: $user_abi" +echo " (CPP, CPPFLAGS, CXX and CXXFLAGS are listed below; these don't get modified.)" + +# Finally: use GMP's flags, plus those required by Sage for the +# package to build properly, plus those specified by the user. +CFLAGS="$gmp_cflags $required_cflags $user_cflags" +LDFLAGS="$required_ldflags $user_ldflags" + +echo "Finally using the following settings:" +echo " CC=$CC" +echo " CFLAGS=$CFLAGS" +echo " CPP=$CPP" +echo " CPPFLAGS=$CPPFLAGS" +echo " CXX=$CXX" +echo " CXXFLAGS=$CXXFLAGS" +echo " LDFLAGS=$LDFLAGS" +echo " ABI=$ABI" +echo "(These settings may still get overridden by 'configure' or Makefiles.)" + +############################################################################### +# Now really configure GMP with proper settings: +############################################################################### + +# We also add '--libdir="$SAGE_LOCAL/lib"' below, since newer autotools may +# otherwise put the libraries into .../lib64 on 64-bit systems (cf. #12131). + +echo "Configuring GMP with the following options:" +echo " --prefix=\"$SAGE_LOCAL\" --libdir=\"$SAGE_LOCAL/lib\" $GMP_CONFIGURE" +echo "You can set GMP_CONFIGURE to pass additional parameters." + +# Clear the cache of the previous configure run +find . -name config.cache -exec rm -f {} \; + +./configure --prefix="$SAGE_LOCAL" --libdir="$SAGE_LOCAL/lib" $GMP_CONFIGURE +if [ $? -ne 0 ]; then + echo >&2 "Error configuring GMP. (See above for the options passed to it.)" + exit 1 +fi + +############################################################################### +# Now build GMP: +############################################################################### + +echo "Now building GMP..." +$MAKE +if [ $? -ne 0 ]; then + echo >&2 "Error building GMP." + exit 1 +fi + +echo "Build succeeded." + +############################################################################### +# Remove previous installation (if any), *after* a successful build: +############################################################################### + +echo "Removing old GMP/GMPIR headers..." +rm -f "$SAGE_LOCAL"/include/{gmp,mpir}*.h + +# Do NOT delete old GMP/GMP shared libraries as Sage's versions of libraries +# used by GCC might still refer to them, such that their deletion would break +# GCC inside Sage. (We could perhaps remove libgmp* though.) +if false; then + echo "Removing old GMP/MPIR libraries..." + rm -f "$SAGE_LOCAL"/lib/lib{gmp,mpir}* +else + echo "Not removing old GMP/MPIR shared libraries, as other libraries" + echo "and executables might still refer to them:" + ls -l "$SAGE_LOCAL"/lib/lib{gmp,mpir}* + echo "(Libraries with the same version number will get updated though.)" +fi + +# Mark MPIR as not installed (since we will overwrite it) +rm -f "$SAGE_SPKG_INST"/mpir-* + +############################################################################### +# Now install GMP: +############################################################################### + +echo "Now installing GMP..." +$MAKE install +if [ $? -ne 0 ]; then + echo >&2 "Error installing GMP." + exit 1 +fi diff --git a/build/pkgs/gmpy2/SPKG.rst b/build/pkgs/gmpy2/SPKG.rst new file mode 100644 index 00000000000..a31c7512872 --- /dev/null +++ b/build/pkgs/gmpy2/SPKG.rst @@ -0,0 +1,13 @@ +gmpy2 +===== + +Description +----------- + +GMP/MPIR, MPFR, and MPC interface to Python 2.6+ and 3.x + +gmpy2 is a C-coded Python extension module that supports +multiple-precision arithmetic. In addition to supporting GMP or MPIR for +multiple-precision integer and rational arithmetic, gmpy2 adds support +for the MPFR (correctly rounded real floating-point arithmetic) and MPC +(correctly rounded complex floating-point arithmetic) libraries. diff --git a/build/pkgs/gmpy2/SPKG.txt b/build/pkgs/gmpy2/SPKG.txt deleted file mode 100644 index 8e8d7d35327..00000000000 --- a/build/pkgs/gmpy2/SPKG.txt +++ /dev/null @@ -1,11 +0,0 @@ -= gmpy2 = - -== Description == - -GMP/MPIR, MPFR, and MPC interface to Python 2.6+ and 3.x - -gmpy2 is a C-coded Python extension module that supports multiple-precision -arithmetic. In addition to supporting GMP or MPIR for multiple-precision -integer and rational arithmetic, gmpy2 adds support for the MPFR (correctly -rounded real floating-point arithmetic) and MPC (correctly rounded complex -floating-point arithmetic) libraries. diff --git a/build/pkgs/gmpy2/dependencies b/build/pkgs/gmpy2/dependencies index a6ace610880..510fc637e95 100644 --- a/build/pkgs/gmpy2/dependencies +++ b/build/pkgs/gmpy2/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) $(MP_LIBRARY) mpfr mpc | pip +$(PYTHON) $(MP_LIBRARY) mpfr mpc | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/gmpy2/spkg-check b/build/pkgs/gmpy2/spkg-check.in similarity index 100% rename from build/pkgs/gmpy2/spkg-check rename to build/pkgs/gmpy2/spkg-check.in diff --git a/build/pkgs/flask_autoindex/spkg-install b/build/pkgs/gmpy2/spkg-install.in similarity index 100% rename from build/pkgs/flask_autoindex/spkg-install rename to build/pkgs/gmpy2/spkg-install.in diff --git a/build/pkgs/gp2c/SPKG.rst b/build/pkgs/gp2c/SPKG.rst new file mode 100644 index 00000000000..35a232f6cae --- /dev/null +++ b/build/pkgs/gp2c/SPKG.rst @@ -0,0 +1,26 @@ +gp2c +==== + +Description +----------- + +The gp2c compiler is a package for translating GP routines into the C +programming language, so that they can be compiled and used with the +PARI system or the GP calculator. + +License +------- + +GPL version 2+ + + +Upstream Contact +---------------- + +- http://pari.math.u-bordeaux.fr/ + +Dependencies +------------ + +- PARI +- Perl diff --git a/build/pkgs/gp2c/SPKG.txt b/build/pkgs/gp2c/SPKG.txt deleted file mode 100644 index 984b63c7865..00000000000 --- a/build/pkgs/gp2c/SPKG.txt +++ /dev/null @@ -1,18 +0,0 @@ -= gp2c = - -== Description == - -The gp2c compiler is a package for translating GP routines into the C -programming language, so that they can be compiled and used with the PARI -system or the GP calculator. - -== License == - -GPL version 2+ - -== Upstream Contact == - * http://pari.math.u-bordeaux.fr/ - -== Dependencies == - * PARI - * Perl diff --git a/build/pkgs/gp2c/distros/debian.txt b/build/pkgs/gp2c/distros/debian.txt new file mode 100644 index 00000000000..27a335cb37d --- /dev/null +++ b/build/pkgs/gp2c/distros/debian.txt @@ -0,0 +1 @@ +pari-gp2c diff --git a/build/pkgs/gp2c/distros/gentoo.txt b/build/pkgs/gp2c/distros/gentoo.txt new file mode 100644 index 00000000000..3c1e945c625 --- /dev/null +++ b/build/pkgs/gp2c/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/gp2c diff --git a/build/pkgs/gp2c/spkg-check b/build/pkgs/gp2c/spkg-check.in similarity index 100% rename from build/pkgs/gp2c/spkg-check rename to build/pkgs/gp2c/spkg-check.in diff --git a/build/pkgs/gp2c/spkg-configure.m4 b/build/pkgs/gp2c/spkg-configure.m4 index a57ae3ed8f6..37e3631b94b 100644 --- a/build/pkgs/gp2c/spkg-configure.m4 +++ b/build/pkgs/gp2c/spkg-configure.m4 @@ -1,33 +1,77 @@ SAGE_SPKG_CONFIGURE([gp2c], [ - AC_REQUIRE([SAGE_SPKG_CONFIGURE_PARI]) - AC_MSG_CHECKING([installing pari? ]) - if test x$sage_spkg_install_pari = xyes; then - AC_MSG_RESULT([yes; install gp2c as well]) - sage_spkg_install_gp2c=yes - libpari_pari_cfg='$SAGE_LOCAL/lib/pari/pari.cfg' - AC_MSG_NOTICE([pari.cfg is $libpari_pari_cfg ]) - else - AC_MSG_RESULT([no]) - AC_PATH_PROG([GP2C], [gp2c]) - if test x$GP2C = x; then - AC_MSG_NOTICE([using pari/gp from the system, but building gp2c]) - AC_MSG_NOTICE([one might prefer to install a system-wide gp2c, instead]) - AC_MSG_NOTICE([and re-run configure.]) - dnl need to figure out libpari prefix - gp_prefix=`dirname $GP` - gp_prefix=`dirname $gp_prefix` - AC_MSG_NOTICE([gp prefix is $gp_prefix ]) - libpari_pari_cfg=`find $gp_prefix -name pari.cfg` - AC_MSG_NOTICE([pari.cfg is $libpari_pari_cfg ]) - sage_spkg_install_gp2c=yes - fi - fi -], [], [], [ - if test x$sage_spkg_install_gp2c = xyes; then - AC_SUBST(SAGE_PARI_CFG, [$libpari_pari_cfg]) - AC_MSG_RESULT([using Sage's gp2c SPKG]) - else - AC_SUBST(SAGE_PARI_CFG, ['']) - AC_MSG_RESULT([using gp2c from the system]) - fi + # Default to installing the SPKG, if the check is run at all. + sage_spkg_install_gp2c=yes + + AS_IF([test "x$USING_SYSTEM_PARI" = "xyes"], [ + # We're using the system pari, so we can use the system gp2c + # if we find a suitable one. + AC_PATH_PROG([GP2C], [gp2c]) + AS_IF([test -n "$GP2C"], [ + # We found gp2c on the system; use it. + sage_spkg_install_gp2c=no + ]) + ]) +],[],[ + # Pre-check phase. We need to determine the location of pari.cfg + # regardless of whether or not we're using the system copy of pari. + # + # Store the depcheck result to be reused in this macro's check and + # post-check phases. We do this in pre-check because the "check" + # phase itself may be skipped via --with-system-gp2c=no. + USING_SYSTEM_PARI=no + SAGE_SPKG_DEPCHECK([pari], [USING_SYSTEM_PARI=yes]) +],[ + # Post-check phase. Here we may need to locate pari.cfg if we're using + # the system's pari (which can put pari.cfg wherever it wants) but sage's + # gp2c (which needs to know where pari.cfg lives). + # + # Can we avoid this if the user hasn't passed --enable-gp2c to ./configure? + # + AS_IF([test "x$sage_spkg_install_gp2c" = "xyes"], [ + AS_IF([test "x$USING_SYSTEM_PARI" = "xyes"], [ + # Installing the gp2c package but don't know where pari.cfg is. There's + # no good way to do this, except to try every known location. If you + # have two copies of pari.cfg in two locations, this loop will overwrite + # the location of the earlier with the latter... but how on earth that + # could happen is beyond my imagination. Since we don't take a + # --with-pari flag, we guess at the prefix where pari/gp was installed + # by assuming the executable is in e.g. /usr/bin or /usr/local/bin and + # then stripping off the "bin" component of that. + gp_prefix=$(dirname -- "$(dirname -- "$GP")") + AC_MSG_NOTICE([gp prefix is $gp_prefix]) + + # Gentoo: $gp_prefix/share/pari/pari.cfg + # Arch/Conda: $gp_prefix/lib/pari/pari.cfg + # Fedora: $gp_prefix/share/doc/pari/pari.cfg + m4_foreach([pari_cfg_path], [share/pari,lib/pari,share/doc/pari], [ + AS_IF([test -f "${gp_prefix}/pari_cfg_path/pari.cfg"], [ + libpari_pari_cfg="${gp_prefix}/pari_cfg_path/pari.cfg" + AC_MSG_NOTICE([found a pari.cfg at $libpari_pari_cfg]) + ]) + ]) + + # Debian: $gp_prefix/lib//pari/pari.cfg + # + # See https://wiki.debian.org/Multiarch/Tuples for a list of valid + # Debian arch tuples. We rely on "dpkg-architecture" to output the + # right one. If it doesn't, the "-f" test below prevents anything + # too bad from happening. + debian_arch=$(dpkg-architecture -qDEB_BUILD_MULTIARCH 2>/dev/null) + AS_IF([test -f "${gp_prefix}/lib/${debian_arch}/pari/pari.cfg"], [ + libpari_pari_cfg="${gp_prefix}/lib/${debian_arch}/pari/pari.cfg" + AC_MSG_NOTICE([found a pari.cfg at $libpari_pari_cfg]) + ]) + + # If we can't find pari.cfg, gp2c isn't going to work. + AS_IF([test -z "$libpari_pari_cfg"], [ + AC_MSG_ERROR([using system pari and unable to locate pari.cfg]) + ]) + ], [ + # Not using the system pari + libpari_pari_cfg='$SAGE_LOCAL/lib/pari/pari.cfg' + ]) + + AC_MSG_NOTICE([pari.cfg is $libpari_pari_cfg]) + ]) + AC_SUBST(SAGE_PARI_CFG, [$libpari_pari_cfg]) ]) diff --git a/build/pkgs/gp2c/spkg-install b/build/pkgs/gp2c/spkg-install.in similarity index 100% rename from build/pkgs/gp2c/spkg-install rename to build/pkgs/gp2c/spkg-install.in diff --git a/build/pkgs/graphs/SPKG.rst b/build/pkgs/graphs/SPKG.rst new file mode 100644 index 00000000000..e4c2960a5e5 --- /dev/null +++ b/build/pkgs/graphs/SPKG.rst @@ -0,0 +1,32 @@ +graphs +====== + +Description +----------- + +A database of graphs. Created by Emily Kirkman based on the work of +Jason Grout. Since April 2012 it also contains the ISGCI graph database. + + +Upstream Contact +---------------- + +- For ISGCI: + + H.N. de Ridder (hnridder@graphclasses.org) + +- For Andries Brouwer's database: + + The data is taken from from Andries E. Brouwer's website + (https://www.win.tue.nl/~aeb/). Anything related to the data should + be + reported to him directly (aeb@cwi.nl) + + The code used to parse the data and create the .json file is + available at + https://github.com/nathanncohen/strongly_regular_graphs_database. + +Dependencies +------------ + +N/A diff --git a/build/pkgs/graphs/SPKG.txt b/build/pkgs/graphs/SPKG.txt deleted file mode 100644 index c646eb65b4e..00000000000 --- a/build/pkgs/graphs/SPKG.txt +++ /dev/null @@ -1,46 +0,0 @@ -= graphs = - -== Description == - -A database of graphs. Created by Emily Kirkman based on the work of Jason -Grout. Since April 2012 it also contains the ISGCI graph database. - -== Upstream Contact == - - * For ISGCI: - - H.N. de Ridder (hnridder@graphclasses.org) - - * For Andries Brouwer's database: - - The data is taken from from Andries E. Brouwer's website - (https://www.win.tue.nl/~aeb/). Anything related to the data should be - reported to him directly (aeb@cwi.nl) - - The code used to parse the data and create the .json file is available at - https://github.com/nathanncohen/strongly_regular_graphs_database. - -== Dependencies == - -N/A - -== Changelog == - -== graphs-20150724.p6 (Nathann Cohen, 2013-07-24 == - * Added the database on strongly regular graphs - -== graphs-20130920.p5 (Nathann Cohen, 2013-09-20) == - * #14396: Update of isgci_sage.xml, added smallgraphs.txt - -== graphs-20120404.p4 (R. Andrew Ohana, 2012-05-17) == - * #13123: Move SAGE_DATA to SAGE_LOCAL/share - -== graphs-20120404.p3 (Nathann Cohen, 2012-04-04) == - * Addition of the ISGCI database as a XML file. See trac #11880 - -== graphs-20070722.p2 (Keshav Kini, 2012-03-18) == - * #12694: Normalize directory structure - * Make R. Andrew Ohana the maintainer - -== graphs-20070722.p1 (?, ?) == -? diff --git a/build/pkgs/graphs/spkg-install b/build/pkgs/graphs/spkg-install.in similarity index 100% rename from build/pkgs/graphs/spkg-install rename to build/pkgs/graphs/spkg-install.in diff --git a/build/pkgs/gsl/SPKG.rst b/build/pkgs/gsl/SPKG.rst new file mode 100644 index 00000000000..851294e080a --- /dev/null +++ b/build/pkgs/gsl/SPKG.rst @@ -0,0 +1,58 @@ +gsl +=== + +Description +----------- + +Website: http://www.gnu.org/software/gsl/ + +From the website above: The GNU Scientific Library (GSL) is a numerical +library for C and C++ programmers. It is free software under the GNU +General Public License. + +The library provides a wide range of mathematical routines such as +random number generators, special functions and least-squares fitting. +There are over 1000 functions in total with an extensive test suite. If +the variable SAGE_CHECK is exported to the value "yes" when building +Sage, GSL's test suite is run. + +License +------- + +- GPL V3 + + +Upstream Contact +---------------- + +- http://www.gnu.org/software/gsl/ + +GSL mailing lists: + +- Bug-gsl mailing list -- bug reports for the GNU + Scientific Library should be sent to bug-gsl@gnu.org + +- Help-gsl users mailing list -- for questions about + installation, how GSL works and how it is used, or general questions + concerning GSL. + +- Info-gsl mailing list -- announcements of new + releases + are made there. + +Dependencies +------------ + +- None - GSL does not depend on any other Sage package to compile, link + and pass all of GSL's self-tests. Despite that fact, BLAS is listed + as + a dependency. (It comes with its own CBLAS implementation that is + e.g. + used when running the GSL test suite during installation; however, + the + Sage library only uses it as a fall-back, if e.g. BLAS library is not + present.) + + +Special Update/Build Instructions +--------------------------------- diff --git a/build/pkgs/gsl/SPKG.txt b/build/pkgs/gsl/SPKG.txt deleted file mode 100644 index cd433ab7954..00000000000 --- a/build/pkgs/gsl/SPKG.txt +++ /dev/null @@ -1,45 +0,0 @@ -= gsl = - -== Description == - -Website: http://www.gnu.org/software/gsl/ - -From the website above: The GNU Scientific Library (GSL) is a numerical -library for C and C++ programmers. It is free software under the GNU General -Public License. - -The library provides a wide range of mathematical routines such as random -number generators, special functions and least-squares fitting. There are -over 1000 functions in total with an extensive test suite. If the variable -SAGE_CHECK is exported to the value "yes" when building Sage, GSL's test suite -is run. - -== License == - - * GPL V3 - -== Upstream Contact == - - * http://www.gnu.org/software/gsl/ - -GSL mailing lists: - - * Bug-gsl mailing list -- bug reports for the GNU - Scientific Library should be sent to bug-gsl@gnu.org - * Help-gsl users mailing list -- for questions about - installation, how GSL works and how it is used, or general questions - concerning GSL. - * Info-gsl mailing list -- announcements of new releases - are made there. - -== Dependencies == - - * None - GSL does not depend on any other Sage package to compile, link - and pass all of GSL's self-tests. Despite that fact, BLAS is listed as - a dependency. (It comes with its own CBLAS implementation that is e.g. - used when running the GSL test suite during installation; however, the - Sage library only uses it as a fall-back, if e.g. BLAS library is not - present.) - -== Special Update/Build Instructions == - diff --git a/build/pkgs/gsl/distros/arch.txt b/build/pkgs/gsl/distros/arch.txt new file mode 100644 index 00000000000..bd0d9198bf3 --- /dev/null +++ b/build/pkgs/gsl/distros/arch.txt @@ -0,0 +1 @@ +gsl diff --git a/build/pkgs/gsl/distros/cygwin.txt b/build/pkgs/gsl/distros/cygwin.txt new file mode 100644 index 00000000000..3f55673dfe6 --- /dev/null +++ b/build/pkgs/gsl/distros/cygwin.txt @@ -0,0 +1 @@ +libgsl-devel diff --git a/build/pkgs/gsl/distros/fedora.txt b/build/pkgs/gsl/distros/fedora.txt new file mode 100644 index 00000000000..5577d09957b --- /dev/null +++ b/build/pkgs/gsl/distros/fedora.txt @@ -0,0 +1 @@ +gsl gsl-devel diff --git a/build/pkgs/gsl/distros/gentoo.txt b/build/pkgs/gsl/distros/gentoo.txt new file mode 100644 index 00000000000..e1a09f287b0 --- /dev/null +++ b/build/pkgs/gsl/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/gsl diff --git a/build/pkgs/gsl/distros/homebrew.txt b/build/pkgs/gsl/distros/homebrew.txt new file mode 100644 index 00000000000..bd0d9198bf3 --- /dev/null +++ b/build/pkgs/gsl/distros/homebrew.txt @@ -0,0 +1 @@ +gsl diff --git a/build/pkgs/gsl/distros/slackware.txt b/build/pkgs/gsl/distros/slackware.txt new file mode 100644 index 00000000000..bd0d9198bf3 --- /dev/null +++ b/build/pkgs/gsl/distros/slackware.txt @@ -0,0 +1 @@ +gsl diff --git a/build/pkgs/gsl/spkg-check b/build/pkgs/gsl/spkg-check deleted file mode 100644 index 8aeec50e237..00000000000 --- a/build/pkgs/gsl/spkg-check +++ /dev/null @@ -1,17 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -# See upstream bug at http://savannah.gnu.org/bugs/?42960 -export MAKE="$MAKE -j1" - -echo "Running GSL testsuite..." -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error: make check for GSL failed." - exit 1 -fi diff --git a/build/pkgs/gsl/spkg-check.in b/build/pkgs/gsl/spkg-check.in new file mode 100644 index 00000000000..e25914bfd7d --- /dev/null +++ b/build/pkgs/gsl/spkg-check.in @@ -0,0 +1,6 @@ +cd src + +# See upstream bug at http://savannah.gnu.org/bugs/?42960 +export MAKE="$MAKE -j1" + +$MAKE check diff --git a/build/pkgs/gsl/spkg-configure.m4 b/build/pkgs/gsl/spkg-configure.m4 index 486fd67e867..81c5e35e6a9 100644 --- a/build/pkgs/gsl/spkg-configure.m4 +++ b/build/pkgs/gsl/spkg-configure.m4 @@ -3,14 +3,10 @@ SAGE_SPKG_CONFIGURE([gsl], [ SAGE_SPKG_DEPCHECK([atlas openblas], [ PKG_CHECK_MODULES([GSL], [gsl >= $SAGE_GSL_MINVER], [ PKG_CHECK_VAR([GSLPCDIR], [gsl], [pcfiledir], [ - AC_CONFIG_COMMANDS([GSLPCPROCESS], [ - $SED -e 's/\${GSL_CBLAS_LIB}\ //' \ - -e 's/GSL_CBLAS_LIB.*/Requires: cblas/' $GSL_PC \ - > "$SAGE_SRC"/lib/pkgconfig/gsl.pc - ], [ - SED=$ac_cv_path_SED - GSL_PC="$GSLPCDIR"/gsl.pc - ]) + GSL_PC="$GSLPCDIR"/gsl.pc + AC_SUBST([SAGE_SYSTEM_FACADE_PC_FILES]) + AS_VAR_APPEND([SAGE_SYSTEM_FACADE_PC_FILES], [" \$(SAGE_PKGCONFIG)/gsl.pc"]) + AC_SUBST([SAGE_GSL_PC_COMMAND],["\$(SED) -e 's/\$\${GSL_CBLAS_LIB}//' -e \"s/^GSL_CBLAS_LIB=.*/Requires: cblas/\" \"$GSL_PC\" > \"\$(@)\""]) ], [ AC_MSG_WARN([Unable to locate the directory of gsl.pc. This should not happen!]) sage_spkg_install_gsl=yes diff --git a/build/pkgs/gsl/spkg-install b/build/pkgs/gsl/spkg-install.in similarity index 100% rename from build/pkgs/gsl/spkg-install rename to build/pkgs/gsl/spkg-install.in diff --git a/build/pkgs/gsl/spkg-legacy-uninstall b/build/pkgs/gsl/spkg-legacy-uninstall.in similarity index 100% rename from build/pkgs/gsl/spkg-legacy-uninstall rename to build/pkgs/gsl/spkg-legacy-uninstall.in diff --git a/build/pkgs/homebrew-bootstrap.txt b/build/pkgs/homebrew-bootstrap.txt new file mode 100644 index 00000000000..55f45bfd276 --- /dev/null +++ b/build/pkgs/homebrew-bootstrap.txt @@ -0,0 +1,2 @@ +# Packages needed for ./bootstrap +gettext autoconf automake libtool pkg-config diff --git a/build/pkgs/homebrew.txt b/build/pkgs/homebrew.txt new file mode 100644 index 00000000000..863c22a6bee --- /dev/null +++ b/build/pkgs/homebrew.txt @@ -0,0 +1,12 @@ +# This file, build/pkgs/homebrew.txt, contains names of homebrew packages +# needed for installation of Sage from source (in addition to XCode). +# +# In addition, the files build/pkgs/SPKG/homebrew.txt contain the names +# of packages that provide the equivalent of SPKG. +# +# See build/bin/sage-spkg, where this information is processed +# for use in "sage -info SPKG". +# +# Everything on a line after a # character is ignored. + +# No packages needed diff --git a/build/pkgs/html5lib/SPKG.rst b/build/pkgs/html5lib/SPKG.rst new file mode 100644 index 00000000000..1f20ca08cdb --- /dev/null +++ b/build/pkgs/html5lib/SPKG.rst @@ -0,0 +1,23 @@ +html5lib +======== + +Description +----------- + +HTML parser based on the WHATWG HTML specification. + +License +------- + +MIT License + + +Upstream Contact +---------------- + +Home Page: https://github.com/html5lib/html5lib-python/issues + +Dependencies +------------ + +Python, webencodings, six diff --git a/build/pkgs/html5lib/SPKG.txt b/build/pkgs/html5lib/SPKG.txt deleted file mode 100644 index cb3191bac15..00000000000 --- a/build/pkgs/html5lib/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= html5lib = - -== Description == - -HTML parser based on the WHATWG HTML specification. - -== License == - -MIT License - -== Upstream Contact == - -Home Page: https://github.com/html5lib/html5lib-python/issues - -== Dependencies == - -Python, webencodings, six diff --git a/build/pkgs/html5lib/dependencies b/build/pkgs/html5lib/dependencies index 2573414ce8a..15df0c4d6d8 100644 --- a/build/pkgs/html5lib/dependencies +++ b/build/pkgs/html5lib/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/flask_babel/spkg-install b/build/pkgs/html5lib/spkg-install.in similarity index 100% rename from build/pkgs/flask_babel/spkg-install rename to build/pkgs/html5lib/spkg-install.in diff --git a/build/pkgs/iconv/SPKG.rst b/build/pkgs/iconv/SPKG.rst new file mode 100644 index 00000000000..85e009c0c85 --- /dev/null +++ b/build/pkgs/iconv/SPKG.rst @@ -0,0 +1,33 @@ +iconv +===== + +Description +----------- + +GNU libiconv is a library that is used to enable different languages, +with different characters to be handled properly. + +License +------- + +- GPL 3 and LGPL 3. So we can safely link against the library in Sage. + + +Upstream Contact +---------------- + +- http://www.gnu.org/software/libiconv/ +- Bug reports to bug-gnu-libiconv@gnu.org + +Dependencies +------------ + +- None for the purposes of Sage, but in general gettext. + + +Special Update/Build Instructions +--------------------------------- + +- None, other than anyone updating this package should be familiar with + how + to write shell scripts. diff --git a/build/pkgs/iconv/SPKG.txt b/build/pkgs/iconv/SPKG.txt deleted file mode 100644 index 3ae0547a38a..00000000000 --- a/build/pkgs/iconv/SPKG.txt +++ /dev/null @@ -1,74 +0,0 @@ -= iconv = - -== Description == -GNU libiconv is a library that is used to enable different -languages, with different characters to be handled properly. - -== License == - * GPL 3 and LGPL 3. So we can safely link against the library in Sage. - -== Upstream Contact == - * http://www.gnu.org/software/libiconv/ - * Bug reports to bug-gnu-libiconv@gnu.org - -== Dependencies == - * None for the purposes of Sage, but in general gettext. - -== Special Update/Build Instructions == - * None, other than anyone updating this package should be familiar with how - to write shell scripts. - -== Changelog == - -=== iconv-1.14 (Jean-Pierre Flori, 26 May 2013) === - * #14647: update to version 1.14. - -=== iconv-1.13.1.p4 (Jean-Pierre Flori, 5 January 2013) === - * #13912: let iconv build on Cygwin without additional prereqs. - -=== iconv-1.13.1.p3 (David Kirkby, August 10th, 2010) === - * Use '$MAKE' instead of 'make' in spkg-install and spkg-check to enable - parallel builds, and allow the user to specify a different 'make' program. - * Use CC="$CC $CFLAG64" instead of adding $CFLAG64 to CFLAGS. The latter - caused problems on Solaris 10 x86 and an early version of OpenSolaris - on x86. It was never a problem on recent versions of OpenSolaris, or - Solaris 10 on SPARC. See #9718 for a discussion of this. - * Changed the format of the tests in spkg-install and spkg-check to be a - little clearer. - * Added a few extra comments. - * Removed code to remove old files, to avoid causing any confusion. - * Quoted "$SAGE_LOCAL" where this had been omitted before. - * Removed trailing white space on lines. - * Removed a surplus ; in both spkg-check and spkg-install. - * Added the "Upstream Contact" section to SPKG.txt. - * Changed the "Special Update/Build Instructions" to be "none", - as what was written before was confusing. - * Install iconv on HP-UX in addition to the two platforms iconv was - previously installed on (Solaris and Cygwin). - * Additionally force 'make check' to execute on HP-UX, too. - * No longer assume bash is in /bin, as it is not on HP-UX or AIX. - Instead use "#!/usr/bin/env bash", as suggested in the Sage - Developers Guide. - * Consistently use "$UNAME" (which is set by 'sage-env') rather than - `uname`. (Reviewer change. Also further cosmetic changes.) - * Stylistic change: Use 'case' statements for $UNAME case distinctions - rather than (nested) 'if's with or-lists of 'test' statements. - (Reviewer change, too.) - * All patches/changes made at #9603. - -=== iconv-1.13.1.p2 (John Palmieri, March 31st 2010) === - * spkg-check: only run 'make check' on Solaris and Cygwin. - -=== iconv-1.13.1.p1 (William Stein, March 31st 2010) === - * Really ensure iconv spkg only does something on Solaris and Cygwin. - In particular, don't delete old versions thus breaking everybody's - upgrades. - -=== iconv-1.13.1.p0 (David Kirkby, March 21st 2010) === - * #8567 Ensure iconv only builds on Solaris and Cygwin as - having two copies of iconv causes problems on some Linux systems. - -=== iconv-1.13.1 (David Kirkby, February 13th 2010) === - * #8191 First release of libiconv, needed for R on Solaris - and probably for Cygwin too (see #7319) - diff --git a/build/pkgs/iconv/distros/cygwin.txt b/build/pkgs/iconv/distros/cygwin.txt new file mode 100644 index 00000000000..df78e31976a --- /dev/null +++ b/build/pkgs/iconv/distros/cygwin.txt @@ -0,0 +1 @@ +libiconv-devel diff --git a/build/pkgs/iconv/spkg-check b/build/pkgs/iconv/spkg-check deleted file mode 100644 index 371fb5122a2..00000000000 --- a/build/pkgs/iconv/spkg-check +++ /dev/null @@ -1,42 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo "SAGE_LOCAL undefined ... exiting" - exit 1 -fi - -# Only test iconv on Solaris, HP-UX and Cygwin, as those are the only -# platforms on which the iconv shipped with Sage will be installed. On -# other platforms Sage uses the system's iconv. - -case "$UNAME" in -SunOS) - # We must test iconv, but on Solaris some tests will always fail. - echo "If you see 3 core dumps, don't be too alarmed." - echo "This is a known Solaris bug and can safely be ignored. See" - echo " http://trac.sagemath.org/sage_trac/ticket/8270" - echo "It will probably be fixed in later releases of Solaris 10," - echo "and was fixed in build 66 of OpenSolaris:" - echo " http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6550204" -esac -case "$UNAME" in -CYGWIN|HP-UX|SunOS) - - cd src - - $MAKE check - - if [ $? -ne 0 ]; then - echo "Error encountered while running the iconv test suite ... exiting" - exit 1 - fi - echo "All the tests for iconv passed." - exit 0 - ;; -*) # Not CYGWIN, HP-UX or SunOS (Solaris) - echo "iconv was not to be tested, since Sage will use the system's iconv" - echo "and not the one shipped with Sage." - echo "The iconv supplied by Sage is only used on Cygwin, HP-UX and Solaris." - exit 0 -esac - -# NOT REACHED - diff --git a/build/pkgs/iconv/spkg-check.in b/build/pkgs/iconv/spkg-check.in new file mode 100644 index 00000000000..4ae5a6c622d --- /dev/null +++ b/build/pkgs/iconv/spkg-check.in @@ -0,0 +1,37 @@ +# Only test iconv on Solaris, HP-UX and Cygwin, as those are the only +# platforms on which the iconv shipped with Sage will be installed. On +# other platforms Sage uses the system's iconv. + +case "$UNAME" in +SunOS) + # We must test iconv, but on Solaris some tests will always fail. + echo "If you see 3 core dumps, don't be too alarmed." + echo "This is a known Solaris bug and can safely be ignored. See" + echo " http://trac.sagemath.org/sage_trac/ticket/8270" + echo "It will probably be fixed in later releases of Solaris 10," + echo "and was fixed in build 66 of OpenSolaris:" + echo " http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6550204" +esac +case "$UNAME" in +CYGWIN|HP-UX|SunOS) + + cd src + + $MAKE check + + if [ $? -ne 0 ]; then + echo "Error encountered while running the iconv test suite ... exiting" + exit 1 + fi + echo "All the tests for iconv passed." + exit 0 + ;; +*) # Not CYGWIN, HP-UX or SunOS (Solaris) + echo "iconv was not to be tested, since Sage will use the system's iconv" + echo "and not the one shipped with Sage." + echo "The iconv supplied by Sage is only used on Cygwin, HP-UX and Solaris." + exit 0 +esac + +# NOT REACHED + diff --git a/build/pkgs/iconv/spkg-configure.m4 b/build/pkgs/iconv/spkg-configure.m4 index 9262ecdbb66..b98ce4c58bf 100644 --- a/build/pkgs/iconv/spkg-configure.m4 +++ b/build/pkgs/iconv/spkg-configure.m4 @@ -1,6 +1,6 @@ SAGE_SPKG_CONFIGURE([iconv], [ AM_ICONV - if test x$am_cv_func_iconv != xyes; then + if test x"$am_cv_func_iconv" != xyes; then sage_spkg_install_iconv=yes fi ]) diff --git a/build/pkgs/iconv/spkg-install b/build/pkgs/iconv/spkg-install.in similarity index 100% rename from build/pkgs/iconv/spkg-install rename to build/pkgs/iconv/spkg-install.in diff --git a/build/pkgs/igraph/SPKG.rst b/build/pkgs/igraph/SPKG.rst new file mode 100644 index 00000000000..1ba18e70031 --- /dev/null +++ b/build/pkgs/igraph/SPKG.rst @@ -0,0 +1,32 @@ +igraph +====== + +Description +----------- + +igraph is a library for creating and manipulating graphs. It is intended +to be as powerful (ie. fast) as possible to enable the analysis of large +graphs. + +License +------- + +GPL version 2 + + +Upstream Contact +---------------- + +http://igraph.org/c/ + +Dependencies +------------ + +- GMP/MPIR +- libxml2, but this is not shipped with Sage, so the user has to + install + libxml2-dev from her distro. + + +Special Update/Build Instructions +--------------------------------- diff --git a/build/pkgs/igraph/SPKG.txt b/build/pkgs/igraph/SPKG.txt deleted file mode 100644 index e70329d56a4..00000000000 --- a/build/pkgs/igraph/SPKG.txt +++ /dev/null @@ -1,23 +0,0 @@ -= igraph = - -== Description == - -igraph is a library for creating and manipulating graphs. -It is intended to be as powerful (ie. fast) as possible to enable the -analysis of large graphs. - -== License == - -GPL version 2 - -== Upstream Contact == - -http://igraph.org/c/ - -== Dependencies == - -* GMP/MPIR -* libxml2, but this is not shipped with Sage, so the user has to install - libxml2-dev from her distro. - -== Special Update/Build Instructions == diff --git a/build/pkgs/igraph/spkg-check b/build/pkgs/igraph/spkg-check.in similarity index 100% rename from build/pkgs/igraph/spkg-check rename to build/pkgs/igraph/spkg-check.in diff --git a/build/pkgs/igraph/spkg-install b/build/pkgs/igraph/spkg-install.in similarity index 100% rename from build/pkgs/igraph/spkg-install rename to build/pkgs/igraph/spkg-install.in diff --git a/build/pkgs/imagesize/SPKG.rst b/build/pkgs/imagesize/SPKG.rst new file mode 100644 index 00000000000..8bd0d46070b --- /dev/null +++ b/build/pkgs/imagesize/SPKG.rst @@ -0,0 +1,7 @@ +imagesize +========= + +Description +----------- + +It parses image files' header and return image size. diff --git a/build/pkgs/imagesize/SPKG.txt b/build/pkgs/imagesize/SPKG.txt deleted file mode 100644 index 2ec0272eda7..00000000000 --- a/build/pkgs/imagesize/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= imagesize = - -== Description == - -It parses image files' header and return image size. diff --git a/build/pkgs/imagesize/dependencies b/build/pkgs/imagesize/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/imagesize/dependencies +++ b/build/pkgs/imagesize/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/flask_oldsessions/spkg-install b/build/pkgs/imagesize/spkg-install.in similarity index 100% rename from build/pkgs/flask_oldsessions/spkg-install rename to build/pkgs/imagesize/spkg-install.in diff --git a/build/pkgs/iml/SPKG.rst b/build/pkgs/iml/SPKG.rst new file mode 100644 index 00000000000..1981fced5d8 --- /dev/null +++ b/build/pkgs/iml/SPKG.rst @@ -0,0 +1,46 @@ +IML +=== + +Description +----------- + +IML is a free library of C source code which implements algorithms for +computing exact solutions to dense systems of linear equations over the +integers. IML is designed to be used with the ATLAS/BLAS library and GMP +bignum library. + +Written in portable C, IML can be used on both 32-bit and 64-bit +machines. It can be called from C++. + +Website: http://www.cs.uwaterloo.ca/~astorjoh/iml.html + +License +------- + +- GPLv2+ + + +Upstream Contact +---------------- + +- Zhuliang Chen z4chen@uwaterloo.ca +- Arne Storjohann astorjoh@uwaterloo.ca + +Dependencies +------------ + +- GMP +- ATLAS + + +Special Update/Build Instructions +--------------------------------- + +- As of version 1.0.4, you need to repackage the upstream tarball + using the spkg-src script because there was a bugfix version of 1.0.4 + reposted upstream without version number bump. + +Patches +~~~~~~~ + +- examples.patch: Modified some of the examples. diff --git a/build/pkgs/iml/SPKG.txt b/build/pkgs/iml/SPKG.txt deleted file mode 100644 index 88c3e68ab00..00000000000 --- a/build/pkgs/iml/SPKG.txt +++ /dev/null @@ -1,36 +0,0 @@ -= IML = - -== Description == - -IML is a free library of C source code which implements algorithms for -computing exact solutions to dense systems of linear equations over the -integers. IML is designed to be used with the ATLAS/BLAS library and -GMP bignum library. - -Written in portable C, IML can be used on both 32-bit and 64-bit -machines. It can be called from C++. - -Website: http://www.cs.uwaterloo.ca/~astorjoh/iml.html - -== License == - - * GPLv2+ - -== Upstream Contact == - - * Zhuliang Chen z4chen@uwaterloo.ca - * Arne Storjohann astorjoh@uwaterloo.ca - -== Dependencies == - * GMP - * ATLAS - -== Special Update/Build Instructions == - - * As of version 1.0.4, you need to repackage the upstream tarball - using the spkg-src script because there was a bugfix version of 1.0.4 - reposted upstream without version number bump. - -=== Patches === - - * examples.patch: Modified some of the examples. diff --git a/build/pkgs/iml/distros/conda.txt b/build/pkgs/iml/distros/conda.txt new file mode 100644 index 00000000000..c1773871ebc --- /dev/null +++ b/build/pkgs/iml/distros/conda.txt @@ -0,0 +1 @@ +iml diff --git a/build/pkgs/iml/distros/debian.txt b/build/pkgs/iml/distros/debian.txt new file mode 100644 index 00000000000..dbdc4b58c56 --- /dev/null +++ b/build/pkgs/iml/distros/debian.txt @@ -0,0 +1 @@ +libiml-dev diff --git a/build/pkgs/iml/distros/fedora.txt b/build/pkgs/iml/distros/fedora.txt new file mode 100644 index 00000000000..32307096d65 --- /dev/null +++ b/build/pkgs/iml/distros/fedora.txt @@ -0,0 +1 @@ +iml iml-devel diff --git a/build/pkgs/iml/distros/gentoo.txt b/build/pkgs/iml/distros/gentoo.txt new file mode 100644 index 00000000000..5502770bdff --- /dev/null +++ b/build/pkgs/iml/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/iml diff --git a/build/pkgs/iml/distros/opensuse.txt b/build/pkgs/iml/distros/opensuse.txt new file mode 100644 index 00000000000..c1773871ebc --- /dev/null +++ b/build/pkgs/iml/distros/opensuse.txt @@ -0,0 +1 @@ +iml diff --git a/build/pkgs/iml/spkg-check b/build/pkgs/iml/spkg-check deleted file mode 100644 index baf9e4db4c2..00000000000 --- a/build/pkgs/iml/spkg-check +++ /dev/null @@ -1,21 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -if [ "$SAGE_DEBUG" = "yes" ]; then - export CFLAGS="-O0 -g $CFLAGS" -else - export CFLAGS="-O2 -g $CFLAGS" -fi - - -echo "Testing the IML library" -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error testing IML." - exit 1 -fi diff --git a/build/pkgs/iml/spkg-check.in b/build/pkgs/iml/spkg-check.in new file mode 100644 index 00000000000..6d620bd7417 --- /dev/null +++ b/build/pkgs/iml/spkg-check.in @@ -0,0 +1,9 @@ +cd src + +if [ "$SAGE_DEBUG" = "yes" ]; then + export CFLAGS="-O0 -g $CFLAGS" +else + export CFLAGS="-O2 -g $CFLAGS" +fi + +$MAKE check diff --git a/build/pkgs/iml/spkg-configure.m4 b/build/pkgs/iml/spkg-configure.m4 new file mode 100644 index 00000000000..f51c33ea0ec --- /dev/null +++ b/build/pkgs/iml/spkg-configure.m4 @@ -0,0 +1,16 @@ +SAGE_SPKG_CONFIGURE([iml], [ + dnl m4_pushdef([SAGE_IML_MINVER],["1.0.4"]) 1.0.5 seems OK too + SAGE_SPKG_DEPCHECK([gmp mpir openblas], [ + AC_CHECK_HEADER([iml.h], [ + AC_SEARCH_LIBS([nonsingSolvLlhsMM], [iml], [], + [sage_spkg_install_iml=yes]) + ], [ + sage_spkg_install_iml=yes + ], [ + #ifdef HAVE_GMP_H + #include + #endif + ]) + ]) + dnl m4_popdef([SAGE_IML_MINVER]) +]) diff --git a/build/pkgs/iml/spkg-install b/build/pkgs/iml/spkg-install.in similarity index 100% rename from build/pkgs/iml/spkg-install rename to build/pkgs/iml/spkg-install.in diff --git a/build/pkgs/ipaddress/SPKG.txt b/build/pkgs/ipaddress/SPKG.txt deleted file mode 100644 index e9c9755006b..00000000000 --- a/build/pkgs/ipaddress/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= ipaddress = - -== Description == - -Python 3.3+'s ipaddress for Python 2.6, 2.7, 3.2. diff --git a/build/pkgs/ipaddress/checksums.ini b/build/pkgs/ipaddress/checksums.ini deleted file mode 100644 index c77b736147a..00000000000 --- a/build/pkgs/ipaddress/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=ipaddress-VERSION.tar.gz -sha1=7b60cef3c7fdb7fa9c991ddff5968754cec6adb0 -md5=74c1ce3109f30eaa1ab3dd342e7b76d4 -cksum=275990046 diff --git a/build/pkgs/ipaddress/dependencies b/build/pkgs/ipaddress/dependencies deleted file mode 100644 index d5dab729e18..00000000000 --- a/build/pkgs/ipaddress/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | pip - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/ipaddress/package-version.txt b/build/pkgs/ipaddress/package-version.txt deleted file mode 100644 index c787b213b07..00000000000 --- a/build/pkgs/ipaddress/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.22 diff --git a/build/pkgs/ipaddress/spkg-install b/build/pkgs/ipaddress/spkg-install deleted file mode 100644 index 796815681b0..00000000000 --- a/build/pkgs/ipaddress/spkg-install +++ /dev/null @@ -1,6 +0,0 @@ -if [ "$SAGE_PYTHON3" = yes ]; then - echo "Skipping ipaddress: it is not necessary for on Python 3.3+." - exit 0 -fi - -cd src && sdh_pip_install . diff --git a/build/pkgs/ipykernel/SPKG.rst b/build/pkgs/ipykernel/SPKG.rst new file mode 100644 index 00000000000..15fd660b6aa --- /dev/null +++ b/build/pkgs/ipykernel/SPKG.rst @@ -0,0 +1,9 @@ +ipykernel +========= + +Description +----------- + +IPython Kernel for Jupyter + +This package provides the IPython kernel for Jupyter. diff --git a/build/pkgs/ipykernel/SPKG.txt b/build/pkgs/ipykernel/SPKG.txt deleted file mode 100644 index de67f28d0b2..00000000000 --- a/build/pkgs/ipykernel/SPKG.txt +++ /dev/null @@ -1,7 +0,0 @@ -= ipykernel = - -== Description == - -IPython Kernel for Jupyter - -This package provides the IPython kernel for Jupyter. diff --git a/build/pkgs/ipykernel/checksums.ini b/build/pkgs/ipykernel/checksums.ini index 6a36135ca1e..cc509ba876c 100644 --- a/build/pkgs/ipykernel/checksums.ini +++ b/build/pkgs/ipykernel/checksums.ini @@ -1,4 +1,5 @@ tarball=ipykernel-VERSION.tar.gz -sha1=e8cd8fb9c9e9228c44682852db2d2af782a0ed35 -md5=91af54e6a4ad908ded0bed383dd03374 -cksum=1184432452 +sha1=488a72393a8af0d10baab0893d05f185bf14afff +md5=2866e6370fab41e6a544d2e3ba384cce +cksum=285781029 +upstream_url=https://pypi.io/packages/source/i/ipykernel/ipykernel-VERSION.tar.gz diff --git a/build/pkgs/ipykernel/dependencies b/build/pkgs/ipykernel/dependencies index 838e66a7605..541ee185018 100644 --- a/build/pkgs/ipykernel/dependencies +++ b/build/pkgs/ipykernel/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | ipython jupyter_client pip scandir +$(PYTHON) | ipython jupyter_client $(PYTHON_TOOLCHAIN) scandir ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/ipykernel/package-version.txt b/build/pkgs/ipykernel/package-version.txt index 326ec6355f3..26d99a283f2 100644 --- a/build/pkgs/ipykernel/package-version.txt +++ b/build/pkgs/ipykernel/package-version.txt @@ -1 +1 @@ -4.8.2 +5.2.1 diff --git a/build/pkgs/ipykernel/spkg-install.in b/build/pkgs/ipykernel/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/ipykernel/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/ipython/SPKG.rst b/build/pkgs/ipython/SPKG.rst new file mode 100644 index 00000000000..ad92a49fcab --- /dev/null +++ b/build/pkgs/ipython/SPKG.rst @@ -0,0 +1,37 @@ +IPython +======= + +Description +----------- + +From the IPython website: + +IPython is a multiplatform, Free Software project (BSD licensed) that +offers: + +- An enhanced Python shell designed for efficient interactive + work. It includes many enhancements over the default Python shell, + including the ability for controlling interactively all major GUI + toolkits in a non-blocking manner. + +- A library to build customized interactive environments using Python + as the basic language (but with the possibility of having extended + or alternate syntaxes). + +- A system for interactive distributed and parallel computing (this is + part of IPython's new development). + +License +------- + +BSD + + +Upstream Contact +---------------- + +http://ipython.scipy.org/ + +ipython-dev@scipy.org + +ipython-user@scipy.org diff --git a/build/pkgs/ipython/SPKG.txt b/build/pkgs/ipython/SPKG.txt deleted file mode 100644 index c342a637a18..00000000000 --- a/build/pkgs/ipython/SPKG.txt +++ /dev/null @@ -1,28 +0,0 @@ -= IPython = - -== Description == -From the IPython website: - -IPython is a multiplatform, Free Software project (BSD licensed) that offers: - - * An enhanced Python shell designed for efficient interactive - work. It includes many enhancements over the default Python shell, - including the ability for controlling interactively all major GUI - toolkits in a non-blocking manner. - * A library to build customized interactive environments using Python - as the basic language (but with the possibility of having extended - or alternate syntaxes). - * A system for interactive distributed and parallel computing (this is - part of IPython's new development). - -== License == - -BSD - -== Upstream Contact == - -http://ipython.scipy.org/ - -ipython-dev@scipy.org - -ipython-user@scipy.org diff --git a/build/pkgs/ipython/checksums.ini b/build/pkgs/ipython/checksums.ini index 924a090c446..2979434cf9b 100644 --- a/build/pkgs/ipython/checksums.ini +++ b/build/pkgs/ipython/checksums.ini @@ -1,4 +1,5 @@ tarball=ipython-VERSION.tar.gz -sha1=987b66cc662db8bd2ae96eee2f2237266d0c92dc -md5=7014b8824981eef2cb893ea5398d6b8d -cksum=2260693848 +sha1=8e2751e3365bbfd97277760b110fb2f290e0d3d3 +md5=de97012f2496dfcc6f005b68ca0eda4a +cksum=1236566613 +upstream_url=https://pypi.io/packages/source/i/ipython/ipython-VERSION.tar.gz diff --git a/build/pkgs/ipython/dependencies b/build/pkgs/ipython/dependencies index 2beb4d61c1f..f15f6f0aca1 100644 --- a/build/pkgs/ipython/dependencies +++ b/build/pkgs/ipython/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) jinja2 tornado pyzmq pickleshare simplegeneric traitlets | pip backports_shutil_get_terminal_size wcwidth prompt_toolkit pygments pexpect appnope +$(PYTHON) jinja2 tornado pyzmq pickleshare simplegeneric traitlets | $(PYTHON_TOOLCHAIN) wcwidth prompt_toolkit pygments pexpect appnope backcall jedi ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/ipython/package-version.txt b/build/pkgs/ipython/package-version.txt index 11d9efa3d5a..eb1dc6a51a0 100644 --- a/build/pkgs/ipython/package-version.txt +++ b/build/pkgs/ipython/package-version.txt @@ -1 +1 @@ -5.8.0 +7.13.0 diff --git a/build/pkgs/ipython/spkg-install b/build/pkgs/ipython/spkg-install deleted file mode 100644 index a220915b32f..00000000000 --- a/build/pkgs/ipython/spkg-install +++ /dev/null @@ -1,18 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -# Old installations of ipython can leave a symlink which can interfere -# with proper installation. -rm -f "$SAGE_LOCAL"/bin/ipython - -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo >&2 "Error installing IPython" - exit 1 -fi diff --git a/build/pkgs/ipython/spkg-install.in b/build/pkgs/ipython/spkg-install.in new file mode 100644 index 00000000000..5096a907a95 --- /dev/null +++ b/build/pkgs/ipython/spkg-install.in @@ -0,0 +1,7 @@ +# Old installations of ipython can leave a symlink which can interfere +# with proper installation. +rm -f "$SAGE_LOCAL"/bin/ipython + +cd src + +sdh_pip_install . diff --git a/build/pkgs/ipython_genutils/SPKG.rst b/build/pkgs/ipython_genutils/SPKG.rst new file mode 100644 index 00000000000..ae072987fbc --- /dev/null +++ b/build/pkgs/ipython_genutils/SPKG.rst @@ -0,0 +1,7 @@ +ipython_genutils +================ + +Description +----------- + +Vestigial utilities from IPython diff --git a/build/pkgs/ipython_genutils/SPKG.txt b/build/pkgs/ipython_genutils/SPKG.txt deleted file mode 100644 index e6b7acbe301..00000000000 --- a/build/pkgs/ipython_genutils/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= ipython_genutils = - -== Description == - -Vestigial utilities from IPython diff --git a/build/pkgs/ipython_genutils/dependencies b/build/pkgs/ipython_genutils/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/ipython_genutils/dependencies +++ b/build/pkgs/ipython_genutils/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/flask_openid/spkg-install b/build/pkgs/ipython_genutils/spkg-install.in similarity index 100% rename from build/pkgs/flask_openid/spkg-install rename to build/pkgs/ipython_genutils/spkg-install.in diff --git a/build/pkgs/ipywidgets/SPKG.rst b/build/pkgs/ipywidgets/SPKG.rst new file mode 100644 index 00000000000..92343dbe112 --- /dev/null +++ b/build/pkgs/ipywidgets/SPKG.rst @@ -0,0 +1,7 @@ +ipywidgets +========== + +Description +----------- + +Interactive HTML widgets for Jupyter notebooks and the IPython kernel. diff --git a/build/pkgs/ipywidgets/SPKG.txt b/build/pkgs/ipywidgets/SPKG.txt deleted file mode 100644 index 7fd95c0eddd..00000000000 --- a/build/pkgs/ipywidgets/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= ipywidgets = - -== Description == - -Interactive HTML widgets for Jupyter notebooks and the IPython kernel. diff --git a/build/pkgs/ipywidgets/dependencies b/build/pkgs/ipywidgets/dependencies index a254e5c7c4e..4a98eebc81f 100644 --- a/build/pkgs/ipywidgets/dependencies +++ b/build/pkgs/ipywidgets/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) widgetsnbextension | pip +$(PYTHON) widgetsnbextension | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/ipywidgets/spkg-install.in b/build/pkgs/ipywidgets/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/ipywidgets/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/isl/SPKG.rst b/build/pkgs/isl/SPKG.rst new file mode 100644 index 00000000000..edf794be2ce --- /dev/null +++ b/build/pkgs/isl/SPKG.rst @@ -0,0 +1,42 @@ +isl +=== + +Description +----------- + +isl is a thread-safe C library for manipulating sets and relations of +integer points bounded by affine constraints. The descriptions of the +sets and relations may involve both parameters and existentially +quantified variables. All computations are performed in exact integer +arithmetic using GMP. + +License +------- + +isl is released under the MIT license, but depends on the LGPL GMP +library. + + +Upstream Contact +---------------- + +- http://groups.google.com/group/isl-development + +Citation +-------- + +:: + + @incollection{Verdoolaege2010isl, + author = {Verdoolaege, Sven}, + title = {isl: An Integer Set Library for the Polyhedral Model}, + booktitle = {Mathematical Software - ICMS 2010}, + series = {Lecture Notes in Computer Science}, + editor = {Fukuda, Komei and Hoeven, Joris and Joswig, Michael and + Takayama, Nobuki}, + publisher = {Springer}, + isbn = {978-3-642-15581-9}, + pages = {299-302}, + volume = {6327}, + year = {2010} + } diff --git a/build/pkgs/isl/SPKG.txt b/build/pkgs/isl/SPKG.txt deleted file mode 100644 index d0ea17aa5e3..00000000000 --- a/build/pkgs/isl/SPKG.txt +++ /dev/null @@ -1,33 +0,0 @@ -= isl = - -== Description == - -isl is a thread-safe C library for manipulating sets and relations -of integer points bounded by affine constraints. The descriptions of -the sets and relations may involve both parameters and existentially -quantified variables. All computations are performed in exact integer -arithmetic using GMP. - -== License == - -isl is released under the MIT license, but depends on the LGPL GMP library. - -== Upstream Contact == - - * http://groups.google.com/group/isl-development - -== Citation == - -@incollection{Verdoolaege2010isl, - author = {Verdoolaege, Sven}, - title = {isl: An Integer Set Library for the Polyhedral Model}, - booktitle = {Mathematical Software - ICMS 2010}, - series = {Lecture Notes in Computer Science}, - editor = {Fukuda, Komei and Hoeven, Joris and Joswig, Michael and - Takayama, Nobuki}, - publisher = {Springer}, - isbn = {978-3-642-15581-9}, - pages = {299-302}, - volume = {6327}, - year = {2010} -} diff --git a/build/pkgs/isl/distros/cygwin.txt b/build/pkgs/isl/distros/cygwin.txt new file mode 100644 index 00000000000..a922268ab95 --- /dev/null +++ b/build/pkgs/isl/distros/cygwin.txt @@ -0,0 +1 @@ +libisl-devel diff --git a/build/pkgs/isl/distros/gentoo.txt b/build/pkgs/isl/distros/gentoo.txt new file mode 100644 index 00000000000..e30528a1f98 --- /dev/null +++ b/build/pkgs/isl/distros/gentoo.txt @@ -0,0 +1 @@ +dev-libs/isl diff --git a/build/pkgs/isl/distros/homebrew.txt b/build/pkgs/isl/distros/homebrew.txt new file mode 100644 index 00000000000..ca114727532 --- /dev/null +++ b/build/pkgs/isl/distros/homebrew.txt @@ -0,0 +1 @@ +isl diff --git a/build/pkgs/isl/spkg-check b/build/pkgs/isl/spkg-check deleted file mode 100644 index 8cd43978e7f..00000000000 --- a/build/pkgs/isl/spkg-check +++ /dev/null @@ -1,2 +0,0 @@ -cd src -sdh_make check diff --git a/build/pkgs/isl/spkg-check.in b/build/pkgs/isl/spkg-check.in new file mode 100644 index 00000000000..45b317a382c --- /dev/null +++ b/build/pkgs/isl/spkg-check.in @@ -0,0 +1,2 @@ +cd src +sdh_make_check diff --git a/build/pkgs/isl/spkg-install b/build/pkgs/isl/spkg-install.in similarity index 100% rename from build/pkgs/isl/spkg-install rename to build/pkgs/isl/spkg-install.in diff --git a/build/pkgs/itsdangerous/SPKG.rst b/build/pkgs/itsdangerous/SPKG.rst new file mode 100644 index 00000000000..51e0b6f16c9 --- /dev/null +++ b/build/pkgs/itsdangerous/SPKG.rst @@ -0,0 +1,8 @@ +itsdangerous +============ + +Description +----------- + +Various helpers to pass data to untrusted environments and to get it +back safe and sound. diff --git a/build/pkgs/itsdangerous/SPKG.txt b/build/pkgs/itsdangerous/SPKG.txt deleted file mode 100644 index 32264811471..00000000000 --- a/build/pkgs/itsdangerous/SPKG.txt +++ /dev/null @@ -1,6 +0,0 @@ -= itsdangerous = - -== Description == - -Various helpers to pass data to untrusted environments and to get it back -safe and sound. diff --git a/build/pkgs/itsdangerous/dependencies b/build/pkgs/itsdangerous/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/itsdangerous/dependencies +++ b/build/pkgs/itsdangerous/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/flask_silk/spkg-install b/build/pkgs/itsdangerous/spkg-install.in similarity index 100% rename from build/pkgs/flask_silk/spkg-install rename to build/pkgs/itsdangerous/spkg-install.in diff --git a/build/pkgs/jedi/SPKG.txt b/build/pkgs/jedi/SPKG.txt new file mode 100644 index 00000000000..234538d27bc --- /dev/null +++ b/build/pkgs/jedi/SPKG.txt @@ -0,0 +1,8 @@ += jedi = + +== Description == + +Jedi is a static analysis tool for Python that is typically used in +IDEs/editors plugins. Jedi has a focus on autocompletion and goto +functionality. Other features include refactoring, code search and +finding references. diff --git a/build/pkgs/jedi/checksums.ini b/build/pkgs/jedi/checksums.ini new file mode 100644 index 00000000000..0aa980641c6 --- /dev/null +++ b/build/pkgs/jedi/checksums.ini @@ -0,0 +1,5 @@ +tarball=jedi-VERSION.tar.gz +sha1=87408742e4bc7a0cea9512757388ed58587c3956 +md5=d6a8e5832939c51dceda474b720696f6 +cksum=783840182 +upstream_url=https://pypi.io/packages/source/j/jedi/jedi-VERSION.tar.gz diff --git a/build/pkgs/jedi/dependencies b/build/pkgs/jedi/dependencies new file mode 100644 index 00000000000..0f21b645bd9 --- /dev/null +++ b/build/pkgs/jedi/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) parso | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/jedi/package-version.txt b/build/pkgs/jedi/package-version.txt new file mode 100644 index 00000000000..c5523bd09b1 --- /dev/null +++ b/build/pkgs/jedi/package-version.txt @@ -0,0 +1 @@ +0.17.0 diff --git a/build/pkgs/jedi/spkg-install.in b/build/pkgs/jedi/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/jedi/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/backports_shutil_get_terminal_size/type b/build/pkgs/jedi/type similarity index 100% rename from build/pkgs/backports_shutil_get_terminal_size/type rename to build/pkgs/jedi/type diff --git a/build/pkgs/jinja2/SPKG.rst b/build/pkgs/jinja2/SPKG.rst new file mode 100644 index 00000000000..9c2eb9e4355 --- /dev/null +++ b/build/pkgs/jinja2/SPKG.rst @@ -0,0 +1,39 @@ +Jinja2 +====== + +Description +----------- + +Jinja2 is a library for Python 2.4 and onwards that is designed to be +flexible, fast and secure. + +If you have any exposure to other text-based template languages, such as +Smarty or Django, you should feel right at home with Jinja2. It's both +designer and developer friendly by sticking to Python's principles and +adding functionality useful for templating environments. + +License +------- + +Modified BSD License + + +Upstream Contact +---------------- + +Author: Pocoo Team Homepage: http://jinja.pocoo.org/ + +Dependencies +------------ + +- Python (>= 2.4) +- setuptools (or distribute) +- Pygments (according to 'spkg/standard/deps') +- docutils (dito, as a note only) + + +Special Update/Build Instructions +--------------------------------- + +None. (Just make sure its prerequisites are new enough in Sage, to avoid +downloads during the build / installation.) diff --git a/build/pkgs/jinja2/SPKG.txt b/build/pkgs/jinja2/SPKG.txt deleted file mode 100644 index 96b65425ac3..00000000000 --- a/build/pkgs/jinja2/SPKG.txt +++ /dev/null @@ -1,46 +0,0 @@ -= Jinja2 = - -== Description == - -Jinja2 is a library for Python 2.4 and onwards that is designed to be -flexible, fast and secure. - -If you have any exposure to other text-based template languages, such -as Smarty or Django, you should feel right at home with Jinja2. It's -both designer and developer friendly by sticking to Python's -principles and adding functionality useful for templating -environments. - -== License == - -Modified BSD License - -== Upstream Contact == - -Author: Pocoo Team -Homepage: http://jinja.pocoo.org/ - -== Dependencies == - - * Python (>= 2.4) - * setuptools (or distribute) - * Pygments (according to 'spkg/standard/deps') - * docutils (dito, as a note only) - -== Special Update/Build Instructions == - -None. (Just make sure its prerequisites are new enough in Sage, to avoid -downloads during the build / installation.) - -== Changelog == - -=== jinja2-2.5.5 (Leif Leonhardy, December 3rd, 2010) === - * #10423: Upgrade to version 2.5.5, as Sphinx (1.0.4) requires a version >=2.2 - (cf. #10350). - * Some clean-up, dependencies added. - -=== jinja2-2.1.1 (Tim Dumol, September 6th, 2009) === - * Upgrade to version 2. - -=== jinja2-1.2 (Mike Hansen, September 15th, 2008) === - * Initial version. diff --git a/build/pkgs/jinja2/dependencies b/build/pkgs/jinja2/dependencies index 13f8a75c0fc..e8739c35b16 100644 --- a/build/pkgs/jinja2/dependencies +++ b/build/pkgs/jinja2/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) markupsafe docutils | setuptools pip +$(PYTHON) markupsafe docutils | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/jinja2/spkg-install b/build/pkgs/jinja2/spkg-install deleted file mode 100644 index e480ef54ecd..00000000000 --- a/build/pkgs/jinja2/spkg-install +++ /dev/null @@ -1,21 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo "Error: SAGE_LOCAL undefined - exiting ..." - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -success() { - if [ $? -ne 0 ]; then - echo $1 - exit 1 - fi -} - -# Install new version -cd src - -sdh_pip_install . -success 'Error installing Jinja2' - -### The Jinja2 docs use Sphinx which requires Jinja2... do not build! -# make -C docs html diff --git a/build/pkgs/jinja2/spkg-install.in b/build/pkgs/jinja2/spkg-install.in new file mode 100644 index 00000000000..b1318539310 --- /dev/null +++ b/build/pkgs/jinja2/spkg-install.in @@ -0,0 +1,6 @@ +cd src + +sdh_pip_install . + +### The Jinja2 docs use Sphinx which requires Jinja2... do not build! +# make -C docs html diff --git a/build/pkgs/jmol/SPKG.rst b/build/pkgs/jmol/SPKG.rst new file mode 100644 index 00000000000..8c021b6fe8e --- /dev/null +++ b/build/pkgs/jmol/SPKG.rst @@ -0,0 +1,42 @@ + +Jmol for Sage +============= + +Description +----------- + +This provides files necessary for Jmol(java) and JSmol (javascript) to +operate from the command line and the Notebook. It does not contain the +Notebook javascript library jmol_lib.js or changes to Notebook or Sage +code. + +License +------- + +GPLv2+ + + +Upstream Contact +---------------- + +- Bob Hanson +- e-mail: hansonr@stolaf.edu +- Homepage: https://www.stolaf.edu/people/hansonr/ +- Development page: https://github.com/BobHanson/Jmol-SwingJS +- Download page: https://sourceforge.net/projects/jmol/files/Jmol/ + +Dependencies +------------ + +No build-time dependencies. + +The commandline jmol requires java at runtime. + + +Special Build Instructions +-------------------------- + +To avoid depending on ``unzip`` at build time, we have to repack the +tarball, see ``spkg-src``. We take the opportunity to remove some +unnecessary subdirectories, see +http://wiki.jmol.org/index.php/Jmol_JavaScript_Object#In_detail diff --git a/build/pkgs/jmol/SPKG.txt b/build/pkgs/jmol/SPKG.txt deleted file mode 100644 index 4f7e555186c..00000000000 --- a/build/pkgs/jmol/SPKG.txt +++ /dev/null @@ -1,33 +0,0 @@ -= Jmol for Sage = - -== Description == - -This provides files necessary for Jmol(java) and JSmol -(javascript) to operate from the command line and the -Notebook. It does not contain the Notebook javascript -library jmol_lib.js or changes to Notebook or Sage code. - -== License == - -GPLv2+ - -== Upstream Contact == - - * Bob Hanson - * e-mail: hansonr@stolaf.edu - * Homepage: https://www.stolaf.edu/people/hansonr/ - * Development page: https://github.com/BobHanson/Jmol-SwingJS - * Download page: https://sourceforge.net/projects/jmol/files/Jmol/ - -== Dependencies == - -No build-time dependencies. - -The commandline jmol requires java at runtime. - -== Special Build Instructions == - -To avoid depending on `unzip` at build time, we have to repack the tarball, see -`spkg-src`. We take the opportunity to remove some unnecessary subdirectories, -see http://wiki.jmol.org/index.php/Jmol_JavaScript_Object#In_detail - diff --git a/build/pkgs/jmol/spkg-install b/build/pkgs/jmol/spkg-install.in similarity index 100% rename from build/pkgs/jmol/spkg-install rename to build/pkgs/jmol/spkg-install.in diff --git a/build/pkgs/jmol/spkg-legacy-uninstall b/build/pkgs/jmol/spkg-legacy-uninstall.in similarity index 100% rename from build/pkgs/jmol/spkg-legacy-uninstall rename to build/pkgs/jmol/spkg-legacy-uninstall.in diff --git a/build/pkgs/jsonschema/SPKG.rst b/build/pkgs/jsonschema/SPKG.rst new file mode 100644 index 00000000000..57e9a4c353f --- /dev/null +++ b/build/pkgs/jsonschema/SPKG.rst @@ -0,0 +1,23 @@ +jsonschema +========== + +Description +----------- + +jsonschema is an implementation of JSON Schema for Python + +License +------- + +MIT License + + +Upstream Contact +---------------- + +Home page: http://github.com/Julian/jsonschema + +Dependencies +------------ + +Python, Setuptools diff --git a/build/pkgs/jsonschema/SPKG.txt b/build/pkgs/jsonschema/SPKG.txt deleted file mode 100644 index 168b4052940..00000000000 --- a/build/pkgs/jsonschema/SPKG.txt +++ /dev/null @@ -1,19 +0,0 @@ -= jsonschema = - -== Description == - -jsonschema is an implementation of JSON Schema for Python - -== License == - -MIT License - -== Upstream Contact == - -Home page: http://github.com/Julian/jsonschema - -== Dependencies == - -Python, Setuptools - - diff --git a/build/pkgs/jsonschema/dependencies b/build/pkgs/jsonschema/dependencies index c95949f63bb..8b3637a0a0e 100644 --- a/build/pkgs/jsonschema/dependencies +++ b/build/pkgs/jsonschema/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) vcversioner functools32 | setuptools pip +$(PYTHON) vcversioner | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/jsonschema/spkg-install b/build/pkgs/jsonschema/spkg-install.in similarity index 100% rename from build/pkgs/jsonschema/spkg-install rename to build/pkgs/jsonschema/spkg-install.in diff --git a/build/pkgs/jupymake/SPKG.rst b/build/pkgs/jupymake/SPKG.rst new file mode 100644 index 00000000000..538dc6e445a --- /dev/null +++ b/build/pkgs/jupymake/SPKG.rst @@ -0,0 +1,28 @@ +jupymake +======== + +Description +----------- + +The Python module JuPyMake provides an interface to polymake. + +License +------- + +- GPL v2 + + +Upstream Contact +---------------- + + https://github.com/polymake/JuPyMake + +Dependencies +------------ + +- pip +- polymake + + +Special Update/Build Instructions +--------------------------------- diff --git a/build/pkgs/jupymake/SPKG.txt b/build/pkgs/jupymake/SPKG.txt deleted file mode 100644 index f5a98bf06fc..00000000000 --- a/build/pkgs/jupymake/SPKG.txt +++ /dev/null @@ -1,20 +0,0 @@ -= jupymake = - -== Description == - -The Python module JuPyMake provides an interface to polymake. - -== License == - - * GPL v2 - -== Upstream Contact == - - https://github.com/polymake/JuPyMake - -== Dependencies == - - * pip - * polymake - -== Special Update/Build Instructions == diff --git a/build/pkgs/jupymake/dependencies b/build/pkgs/jupymake/dependencies index d2db8334004..bcbd220117e 100644 --- a/build/pkgs/jupymake/dependencies +++ b/build/pkgs/jupymake/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) polymake | pip +$(PYTHON) polymake | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/jupymake/spkg-install b/build/pkgs/jupymake/spkg-install deleted file mode 100644 index 4892b22fc93..00000000000 --- a/build/pkgs/jupymake/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -#Install new version -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing JuPyMake." - exit 1 -fi diff --git a/build/pkgs/jupymake/spkg-install.in b/build/pkgs/jupymake/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/jupymake/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/jupyter_client/SPKG.rst b/build/pkgs/jupyter_client/SPKG.rst new file mode 100644 index 00000000000..a9348a28d55 --- /dev/null +++ b/build/pkgs/jupyter_client/SPKG.rst @@ -0,0 +1,14 @@ +jupyter_client +============== + +Description +----------- + +Jupyter protocol implementation and client libraries + +jupyter_client contains the reference implementation of the Jupyter +protocol. It also provides client and kernel management APIs for working +with kernels. + +It also provides the jupyter kernelspec entrypoint for installing +kernelspecs for use with Jupyter frontends. diff --git a/build/pkgs/jupyter_client/SPKG.txt b/build/pkgs/jupyter_client/SPKG.txt deleted file mode 100644 index 3fedb1e4efd..00000000000 --- a/build/pkgs/jupyter_client/SPKG.txt +++ /dev/null @@ -1,12 +0,0 @@ -= jupyter_client = - -== Description == - -Jupyter protocol implementation and client libraries - -jupyter_client contains the reference implementation of the Jupyter -protocol. It also provides client and kernel management APIs for working -with kernels. - -It also provides the jupyter kernelspec entrypoint for installing -kernelspecs for use with Jupyter frontends. diff --git a/build/pkgs/jupyter_client/dependencies b/build/pkgs/jupyter_client/dependencies index afc59f3fe5d..ed50223fe7e 100644 --- a/build/pkgs/jupyter_client/dependencies +++ b/build/pkgs/jupyter_client/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) jupyter_core | pip pyzmq dateutil +$(PYTHON) jupyter_core | $(PYTHON_TOOLCHAIN) pyzmq dateutil ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/gap_jupyter/spkg-install b/build/pkgs/jupyter_client/spkg-install.in similarity index 100% rename from build/pkgs/gap_jupyter/spkg-install rename to build/pkgs/jupyter_client/spkg-install.in diff --git a/build/pkgs/jupyter_core/SPKG.rst b/build/pkgs/jupyter_core/SPKG.rst new file mode 100644 index 00000000000..934ee810110 --- /dev/null +++ b/build/pkgs/jupyter_core/SPKG.rst @@ -0,0 +1,7 @@ +jupyter_core +============ + +Description +----------- + +Jupyter core package. A base package on which Jupyter projects rely. diff --git a/build/pkgs/jupyter_core/SPKG.txt b/build/pkgs/jupyter_core/SPKG.txt deleted file mode 100644 index dbed147e7af..00000000000 --- a/build/pkgs/jupyter_core/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= jupyter_core = - -== Description == - -Jupyter core package. A base package on which Jupyter projects rely. diff --git a/build/pkgs/jupyter_core/dependencies b/build/pkgs/jupyter_core/dependencies index 541b33eec62..5dcd07f89fa 100644 --- a/build/pkgs/jupyter_core/dependencies +++ b/build/pkgs/jupyter_core/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip traitlets +$(PYTHON) | $(PYTHON_TOOLCHAIN) traitlets ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/gmpy2/spkg-install b/build/pkgs/jupyter_core/spkg-install.in similarity index 100% rename from build/pkgs/gmpy2/spkg-install rename to build/pkgs/jupyter_core/spkg-install.in diff --git a/build/pkgs/kenzo/SPKG.rst b/build/pkgs/kenzo/SPKG.rst new file mode 100644 index 00000000000..faa055b87ee --- /dev/null +++ b/build/pkgs/kenzo/SPKG.rst @@ -0,0 +1,25 @@ +Kenzo +===== + +Description +----------- + +Kenzo is a package to compute properties (mainly homology groups) of +topological spaces. It allows defining spaces created from others by +constuctions like loop spaces, classifying spaces and so on. + +License +------- + +GPL + + +Upstream Contact +---------------- + +- https://github.com/gheber/kenzo + +Dependencies +------------ + +- ECL (Embedded Common Lisp) diff --git a/build/pkgs/kenzo/SPKG.txt b/build/pkgs/kenzo/SPKG.txt deleted file mode 100644 index e0f96ea81c6..00000000000 --- a/build/pkgs/kenzo/SPKG.txt +++ /dev/null @@ -1,21 +0,0 @@ -= Kenzo = - -== Description == - -Kenzo is a package to compute properties (mainly homology groups) -of topological spaces. It allows defining spaces created from others -by constuctions like loop spaces, classifying spaces and so on. - - -== License == - -GPL - -== Upstream Contact == - - * https://github.com/gheber/kenzo - -== Dependencies == - - * ECL (Embedded Common Lisp) - diff --git a/build/pkgs/kenzo/checksums.ini b/build/pkgs/kenzo/checksums.ini index abd092f806f..22ef2a25127 100644 --- a/build/pkgs/kenzo/checksums.ini +++ b/build/pkgs/kenzo/checksums.ini @@ -1,4 +1,5 @@ -tarball=kenzo-1.1.7-r3.tar.gz -sha1=92433fee5f83d575483bbbd9730c2cb094c292a4 -md5=7dc2207eb8071a2710df825ac9409324 -cksum=3315029495 +tarball=kenzo-1.1.9.tar.gz +upstream_url=https://github.com/miguelmarco/kenzo/releases/download/1.1.9/kenzo-1.1.9.tar.gz +sha1=f153b0c172b6c11851a5f248947b61b0bf5be527 +md5=98adc197c6f23716a6ddd115115ab4f6 +cksum=880933361 diff --git a/build/pkgs/kenzo/package-version.txt b/build/pkgs/kenzo/package-version.txt index 6ba101eb19d..512a1faa680 100644 --- a/build/pkgs/kenzo/package-version.txt +++ b/build/pkgs/kenzo/package-version.txt @@ -1 +1 @@ -1.1.7-r3 +1.1.9 diff --git a/build/pkgs/kenzo/spkg-install b/build/pkgs/kenzo/spkg-install deleted file mode 100644 index cd64e0213c4..00000000000 --- a/build/pkgs/kenzo/spkg-install +++ /dev/null @@ -1,17 +0,0 @@ -cd src - - - -# create a short lisp file with the instructions to load kenzo from ecl -# This will compile the lisp files since it is the first time they are loaded - -ecl < compile.lisp - -echo "moving kenzo--all-systems.fasb to $SAGE_LOCAL/lib/ecl/kenzo.fas" -mv kenzo--all-systems.fasb $SAGE_LOCAL/lib/ecl/kenzo.fas - - -if [ $? -ne 0 ]; then - echo >&2 "Error installing Kenzo." - exit 1 -fi diff --git a/build/pkgs/kenzo/spkg-install.in b/build/pkgs/kenzo/spkg-install.in new file mode 100644 index 00000000000..3c8e5946547 --- /dev/null +++ b/build/pkgs/kenzo/spkg-install.in @@ -0,0 +1,19 @@ +cd src + +# create a short lisp file with the instructions to load kenzo from ecl +# This will compile the lisp files since it is the first time they are loaded + +ecl < compile.lisp + +# Install Kenzo into ECL's library directory (installation procedure +# copied from Maxima's spkg-install.in file): +ECLLIB=`ecl -eval "(princ (SI:GET-LIBRARY-PATHNAME))" -eval "(quit)"` +echo +echo "Now installing Kenzo as '$ECLLIB/kenzo.fas'..." +cp -f kenzo--all-systems.fasb "$ECLLIB/kenzo.fas" \ + || sdh_die "Failed to install 'kenzo--all-systems.fasb' as '$ECLLIB/kenzo.fas'." + +if [ $? -ne 0 ]; then + echo >&2 "Error installing Kenzo." + exit 1 +fi diff --git a/build/pkgs/kiwisolver/SPKG.rst b/build/pkgs/kiwisolver/SPKG.rst new file mode 100644 index 00000000000..8b8e86dd28b --- /dev/null +++ b/build/pkgs/kiwisolver/SPKG.rst @@ -0,0 +1,37 @@ +kiwisolver +========== + +Description +----------- + +From https://pypi.org/project/kiwisolver/ + +A fast implementation of the Cassowary constraint solver + +Kiwi is an efficient C++ implementation of the Cassowary constraint +solving algorithm. Kiwi is an implementation of the algorithm based on +the seminal Cassowary paper. It is not a refactoring of the original C++ +solver. Kiwi has been designed from the ground up to be lightweight and +fast. Kiwi ranges from 10x to 500x faster than the original Cassowary +solver with typical use cases gaining a 40x improvement. Memory savings +are consistently > 5x. + +In addition to the C++ solver, Kiwi ships with hand-rolled Python +bindings. + +License +------- + +Modified BSD License + + +Upstream Contact +---------------- + +https://github.com/nucleic/kiwi + +Dependencies +------------ + +- python +- setuptools diff --git a/build/pkgs/kiwisolver/SPKG.txt b/build/pkgs/kiwisolver/SPKG.txt deleted file mode 100644 index 537b9aae123..00000000000 --- a/build/pkgs/kiwisolver/SPKG.txt +++ /dev/null @@ -1,31 +0,0 @@ -= kiwisolver = - -== Description == - -From https://pypi.org/project/kiwisolver/ - -A fast implementation of the Cassowary constraint solver - -Kiwi is an efficient C++ implementation of the Cassowary constraint -solving algorithm. Kiwi is an implementation of the algorithm based -on the seminal Cassowary paper. It is not a refactoring of the -original C++ solver. Kiwi has been designed from the ground up to be -lightweight and fast. Kiwi ranges from 10x to 500x faster than the -original Cassowary solver with typical use cases gaining a 40x -improvement. Memory savings are consistently > 5x. - -In addition to the C++ solver, Kiwi ships with hand-rolled Python -bindings. - -== License == - -Modified BSD License - -== Upstream Contact == - -https://github.com/nucleic/kiwi - -== Dependencies == - - * python - * setuptools diff --git a/build/pkgs/kiwisolver/dependencies b/build/pkgs/kiwisolver/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/kiwisolver/dependencies +++ b/build/pkgs/kiwisolver/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/html5lib/spkg-install b/build/pkgs/kiwisolver/spkg-install.in similarity index 100% rename from build/pkgs/html5lib/spkg-install rename to build/pkgs/kiwisolver/spkg-install.in diff --git a/build/pkgs/latte_int/SPKG.rst b/build/pkgs/latte_int/SPKG.rst new file mode 100644 index 00000000000..6691a363287 --- /dev/null +++ b/build/pkgs/latte_int/SPKG.rst @@ -0,0 +1,24 @@ +LattE_Integrale +=============== + +Description +----------- + +LattE (Lattice point Enumeration) Integrale solves the problems of +counting lattice points in and integration over convex polytopes. + +License +------- + +GPLv2 + + +Upstream Contact +---------------- + +Matthias Köppe, UC Davis, CA, USA + +Dependencies +------------ + +GMP (MPIR), 4ti2, NTL, cddlib. diff --git a/build/pkgs/latte_int/SPKG.txt b/build/pkgs/latte_int/SPKG.txt deleted file mode 100644 index 24a6da7e78b..00000000000 --- a/build/pkgs/latte_int/SPKG.txt +++ /dev/null @@ -1,19 +0,0 @@ -= LattE_Integrale = - -== Description == - -LattE (Lattice point Enumeration) Integrale solves the problems of counting -lattice points in and integration over convex polytopes. - -== License == - -GPLv2 - -== Upstream Contact == - -Matthias Köppe, UC Davis, CA, USA - -== Dependencies == - -GMP (MPIR), 4ti2, NTL, cddlib. - diff --git a/build/pkgs/latte_int/spkg-check b/build/pkgs/latte_int/spkg-check deleted file mode 100644 index 9f3393f4ab6..00000000000 --- a/build/pkgs/latte_int/spkg-check +++ /dev/null @@ -1,24 +0,0 @@ -if [ -z "$SAGE_LOCAL" ] ; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -CUR=`pwd` - -if [ "x$SAGE_DEBUG" = xyes ] ; then - CFLAGS="$CFLAGS -g -O0" # No optimisation, aids debugging. -else - CFLAGS="$CFLAGS -g -O2" # Normal optimisation. -fi - -export CFLAGS - -dirsr="$CUR"/src -echo "Testing in $dirsr" -cd "$dirsr" -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error: tests failed" - exit 1 -fi diff --git a/build/pkgs/latte_int/spkg-check.in b/build/pkgs/latte_int/spkg-check.in new file mode 100644 index 00000000000..7bdcf72a10c --- /dev/null +++ b/build/pkgs/latte_int/spkg-check.in @@ -0,0 +1,10 @@ +if [ "x$SAGE_DEBUG" = xyes ] ; then + CFLAGS="$CFLAGS -g -O0" # No optimisation, aids debugging. +else + CFLAGS="$CFLAGS -g -O2" # Normal optimisation. +fi + +export CFLAGS + +cd src +$MAKE check diff --git a/build/pkgs/latte_int/spkg-install b/build/pkgs/latte_int/spkg-install.in similarity index 100% rename from build/pkgs/latte_int/spkg-install rename to build/pkgs/latte_int/spkg-install.in diff --git a/build/pkgs/lcalc/SPKG.rst b/build/pkgs/lcalc/SPKG.rst new file mode 100644 index 00000000000..3dd99db88fe --- /dev/null +++ b/build/pkgs/lcalc/SPKG.rst @@ -0,0 +1,122 @@ +lcalc +===== + +Description +----------- + +Michael Rubinstein's L-function calculator. + +License +------- + +- LGPL V2+ + + +Upstream contact +---------------- + +Michael Rubinstein + +Sources: http://oto.math.uwaterloo.ca/~mrubinst/L_function_public/L.html + +Newer beta version 1.3 (not yet in Sage): +http://code.google.com/p/l-calc/ + +Dependencies +------------ + +- GMP/MPIR +- MPFR +- PARI +- GNU patch + + +Special Update/Build Instructions +--------------------------------- + +- There is some garbage in the upstream sources which should be + removed:: + + src/include/.Lexplicit_formula.h.swp + src/include/.Lvalue.h.swp + src/include/._.DS_Store + src/include/.DS_Store + src/include/Lexplicit_formula.h.swap.crap + src/include/Lvalue.h.bak + src/src/Makefile.old + src/src/.Makefile.old.swp + src/src/._.DS_Store + src/src/.DS_Store + src/src/.Lcommandline.ggo.swp + src/src/libLfunction.a + +- We (and apparently also upstream) currently don't build Lcalc's tests + (see Makefile), hence there's no spkg-check. + This might change in newer upstream versions. + +- The original Makefile uses $(CC) to compile C++ (also using + $(CCFLAGS)), + which it defines to 'g++', and hardcodes 'g++' when linking the + shared + library. (It should use $(CXX) instead, which might \*default\* to + 'g++'.) + We now (lcalc-1.23.p10) patch the Makefile also to use $(CXX) for + compiling + and linking C++; $(CXX) now \*defaults\* to 'g++', and $(CC) to + 'gcc', but + both can be overridden by simply setting their respective environment + variables. (Same for $(INSTALL_DIR) btw.) + +Patches +------- + +- Makefile.patch: + + We change a lot there, since Lcalc doesn't have a 'configure' script, + and hence the Makefile is supposed to be edited to customize Lcalc + (build + options, locations of headers and libraries etc.). + Besides that, we + + - put CXXFLAGS into Lcalc's "CCFLAGS" used for compiling C++, + - remove some stuff involving LDFLAGS1 and LDFLAGS2, setting just + LDFLAGS, + - use $(MAKE) instead of 'make' in the crude build receipts, + - use CXXFLAG64 when linking the shared library, + - now use $(CXX) for compiling and linking C++, which \*defaults\* to + 'g++', + but can be overridden by setting the environment variable of the same + name. ($(CC) now \*defaults\* to 'gcc', although currently not really + used as far as I can see.) + - $(INSTALL_DIR) can now be overridden by simply setting the + environment + variable of the same name. + +- Lcommon.h.patch: + + Uncomment the definition of lcalc_to_double(const long double& x). + (Necessary for GCC >= 4.6.0, cf. #10892.) + Comment from there: + The reason is the following code horror from + src/src/include/Lcommon.h: + [...] + But somebody who is familiar with the codebase should really rewrite + lcalc + to not redefine the double() cast, thats just fragile and will sooner + or + later again fail inside some system headers. + +- pari-2.7.patch: + + Various changes to port to newer versions of PARI. + +- time.h.patch: + + (Patches src/include/Lcommandline_numbertheory.h) + Include also in Lcommandline_numbertheory.h (at least + required + on Cygwin, cf. #9845). + This should get reported upstream. + +- lcalc-1.23_default_parameters_1.patch: Make Lcalc (1.23) build with + GCC 4.9 diff --git a/build/pkgs/lcalc/SPKG.txt b/build/pkgs/lcalc/SPKG.txt deleted file mode 100644 index 2958ec6d12c..00000000000 --- a/build/pkgs/lcalc/SPKG.txt +++ /dev/null @@ -1,91 +0,0 @@ -= lcalc = - -== Description == - -Michael Rubinstein's L-function calculator. - -== License == - - * LGPL V2+ - -== Upstream contact == - -Michael Rubinstein - -Sources: http://oto.math.uwaterloo.ca/~mrubinst/L_function_public/L.html - -Newer beta version 1.3 (not yet in Sage): -http://code.google.com/p/l-calc/ - -== Dependencies == - - * GMP/MPIR - * MPFR - * PARI - * GNU patch - -== Special Update/Build Instructions == - - * There is some garbage in the upstream sources which should be removed: - src/include/.Lexplicit_formula.h.swp - src/include/.Lvalue.h.swp - src/include/._.DS_Store - src/include/.DS_Store - src/include/Lexplicit_formula.h.swap.crap - src/include/Lvalue.h.bak - src/src/Makefile.old - src/src/.Makefile.old.swp - src/src/._.DS_Store - src/src/.DS_Store - src/src/.Lcommandline.ggo.swp - src/src/libLfunction.a - * We (and apparently also upstream) currently don't build Lcalc's tests - (see Makefile), hence there's no spkg-check. - This might change in newer upstream versions. - * The original Makefile uses $(CC) to compile C++ (also using $(CCFLAGS)), - which it defines to 'g++', and hardcodes 'g++' when linking the shared - library. (It should use $(CXX) instead, which might *default* to 'g++'.) - We now (lcalc-1.23.p10) patch the Makefile also to use $(CXX) for compiling - and linking C++; $(CXX) now *defaults* to 'g++', and $(CC) to 'gcc', but - both can be overridden by simply setting their respective environment - variables. (Same for $(INSTALL_DIR) btw.) - -== Patches == - - * Makefile.patch: - We change a lot there, since Lcalc doesn't have a 'configure' script, - and hence the Makefile is supposed to be edited to customize Lcalc (build - options, locations of headers and libraries etc.). - Besides that, we - - put CXXFLAGS into Lcalc's "CCFLAGS" used for compiling C++, - - remove some stuff involving LDFLAGS1 and LDFLAGS2, setting just LDFLAGS, - - use $(MAKE) instead of 'make' in the crude build receipts, - - use CXXFLAG64 when linking the shared library, - - now use $(CXX) for compiling and linking C++, which *defaults* to 'g++', - but can be overridden by setting the environment variable of the same - name. ($(CC) now *defaults* to 'gcc', although currently not really - used as far as I can see.) - - $(INSTALL_DIR) can now be overridden by simply setting the environment - variable of the same name. - - * Lcommon.h.patch: - Uncomment the definition of lcalc_to_double(const long double& x). - (Necessary for GCC >= 4.6.0, cf. #10892.) - Comment from there: - The reason is the following code horror from src/src/include/Lcommon.h: - [...] - But somebody who is familiar with the codebase should really rewrite lcalc - to not redefine the double() cast, thats just fragile and will sooner or - later again fail inside some system headers. - - * pari-2.7.patch: - Various changes to port to newer versions of PARI. - - * time.h.patch: - (Patches src/include/Lcommandline_numbertheory.h) - Include also in Lcommandline_numbertheory.h (at least required - on Cygwin, cf. #9845). - This should get reported upstream. - - * lcalc-1.23_default_parameters_1.patch: Make Lcalc (1.23) build with - GCC 4.9 diff --git a/build/pkgs/lcalc/distros/fedora.txt b/build/pkgs/lcalc/distros/fedora.txt index 899c5b75404..510a0a01673 100644 --- a/build/pkgs/lcalc/distros/fedora.txt +++ b/build/pkgs/lcalc/distros/fedora.txt @@ -1,3 +1,3 @@ ## Is not accepted by spkg-configure on Fedora-32 -# L-function-devel -# L-function +L-function-devel +L-function diff --git a/build/pkgs/lcalc/spkg-build b/build/pkgs/lcalc/spkg-build deleted file mode 100644 index cca39a77296..00000000000 --- a/build/pkgs/lcalc/spkg-build +++ /dev/null @@ -1,31 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -# If SAGE_DEBUG is set to 'yes', add debugging information. Since both -# the Sun and GNU compilers accept -g to give debugging information, -# there is no need to do anything specific to one compiler or the other. -if [ "x$SAGE_DEBUG" = xyes ]; then - echo "Code will be built with debugging information present. Unset 'SAGE_DEBUG'" - echo "or set it to 'no' if you don't want that." - - CFLAGS="$CFLAGS -O0 -g" - CXXFLAGS="$CXXFLAGS -O0 -g" -else - echo "No debugging information will be used during the build of this package." - echo "Set 'SAGE_DEBUG' to 'yes' if you want debugging information present (-g added)." -fi - -# Export everything. Probably not necessary in most cases. -export CFLAGS -export CXXFLAGS - -export DEFINES="" - -cd src/src # Now we are in src/src. - -# Build everything: -echo "Now building lcalc, example programs and the shared library..." -sdh_make diff --git a/build/pkgs/lcalc/spkg-build.in b/build/pkgs/lcalc/spkg-build.in new file mode 100644 index 00000000000..b477bd0d153 --- /dev/null +++ b/build/pkgs/lcalc/spkg-build.in @@ -0,0 +1,29 @@ +# If SAGE_DEBUG is set to 'yes', add debugging information. Since both +# the Sun and GNU compilers accept -g to give debugging information, +# there is no need to do anything specific to one compiler or the other. +if [ "x$SAGE_DEBUG" = xyes ]; then + echo "Code will be built with debugging information present. Unset 'SAGE_DEBUG'" + echo "or set it to 'no' if you don't want that." + + CFLAGS="$CFLAGS -O0 -g" + CXXFLAGS="$CXXFLAGS -O0 -g" +else + echo "No debugging information will be used during the build of this package." + echo "Set 'SAGE_DEBUG' to 'yes' if you want debugging information present (-g added)." +fi + +# Using pari in a C++17 file with "using namespace std doesn't +# work due to a conflict between std::rank and pari's rank +CXXFLAGS=$(echo "${CXXFLAGS}" | sed "s/-std=c++17//g") + +# Export everything. Probably not necessary in most cases. +export CFLAGS +export CXXFLAGS + +export DEFINES="" + +cd src/src # Now we are in src/src. + +# Build everything: +echo "Now building lcalc, example programs and the shared library..." +sdh_make diff --git a/build/pkgs/lcalc/spkg-install b/build/pkgs/lcalc/spkg-install deleted file mode 100644 index 7685fc1db9d..00000000000 --- a/build/pkgs/lcalc/spkg-install +++ /dev/null @@ -1,14 +0,0 @@ -cd src/src - -if [ "$UNAME" = "Darwin" ]; then - export LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names" -fi - -export CXXFLAGS -mkdir -p "$SAGE_DESTDIR_LOCAL"/{bin,include,lib} -sdh_make_install INSTALL_DIR="$SAGE_DESTDIR_LOCAL" - -if [ "$UNAME" = "Darwin" ]; then - install_name_tool -id "${SAGE_LOCAL}/lib/libLfunction.dylib" \ - "${SAGE_DESTDIR_LOCAL}/lib/libLfunction.dylib" || exit $? -fi diff --git a/build/pkgs/lcalc/spkg-install.in b/build/pkgs/lcalc/spkg-install.in new file mode 100644 index 00000000000..9000051aa58 --- /dev/null +++ b/build/pkgs/lcalc/spkg-install.in @@ -0,0 +1,18 @@ +cd src/src + +if [ "$UNAME" = "Darwin" ]; then + export LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names" +fi + +# Using pari in a C++17 file with "using namespace std doesn't +# work due to a conflict between std::rank and pari's rank +CXXFLAGS=$(echo "${CXXFLAGS}" | sed "s/-std=c++17//g") + +export CXXFLAGS +mkdir -p "$SAGE_DESTDIR_LOCAL"/{bin,include,lib} +sdh_make_install INSTALL_DIR="$SAGE_DESTDIR_LOCAL" + +if [ "$UNAME" = "Darwin" ]; then + install_name_tool -id "${SAGE_LOCAL}/lib/libLfunction.dylib" \ + "${SAGE_DESTDIR_LOCAL}/lib/libLfunction.dylib" || exit $? +fi diff --git a/build/pkgs/lcalc/spkg-legacy-uninstall b/build/pkgs/lcalc/spkg-legacy-uninstall.in similarity index 100% rename from build/pkgs/lcalc/spkg-legacy-uninstall rename to build/pkgs/lcalc/spkg-legacy-uninstall.in diff --git a/build/pkgs/libatomic_ops/SPKG.rst b/build/pkgs/libatomic_ops/SPKG.rst new file mode 100644 index 00000000000..131e2629409 --- /dev/null +++ b/build/pkgs/libatomic_ops/SPKG.rst @@ -0,0 +1,30 @@ +libatomic_ops +============= + +Description +----------- + +A part of the Boehm-Demers-Weiser conservative garbage collector. + +License +------- + +- Permissive BSD + GPL 2.0+ + + +Upstream Contact +---------------- + +- Webpage: http://www.hboehm.info/gc/ +- Email List: bdwgc@lists.opendylan.org + +Dependencies +------------ + +None. + + +Special Update/Build Instructions +--------------------------------- + +None. diff --git a/build/pkgs/libatomic_ops/SPKG.txt b/build/pkgs/libatomic_ops/SPKG.txt deleted file mode 100644 index 9f1cd0ffd91..00000000000 --- a/build/pkgs/libatomic_ops/SPKG.txt +++ /dev/null @@ -1,22 +0,0 @@ -= libatomic_ops = - -== Description == - -A part of the Boehm-Demers-Weiser conservative garbage collector. - -== License == - -* Permissive BSD + GPL 2.0+ - -== Upstream Contact == - -Webpage: http://www.hboehm.info/gc/ -Email List: bdwgc@lists.opendylan.org - -== Dependencies == - -None. - -== Special Update/Build Instructions == - -None. diff --git a/build/pkgs/libatomic_ops/checksums.ini b/build/pkgs/libatomic_ops/checksums.ini index e9e87dadcd6..35390b81443 100644 --- a/build/pkgs/libatomic_ops/checksums.ini +++ b/build/pkgs/libatomic_ops/checksums.ini @@ -1,4 +1,5 @@ tarball=libatomic_ops-VERSION.tar.gz -sha1=ebed1891250cc8e2c952b88fc07e1db2a213f7e2 -md5=395c0ceb46db60be209bbd3c0d6cd04d -cksum=1257402450 +sha1=ad1c9cd6cc22e042a784e34baa360874083e5f60 +md5=90a78a84d9c28ce11f331c25289bfbd0 +cksum=1553525211 +upstream_url=https://github.com/ivmai/libatomic_ops/releases/download/vVERSION/libatomic_ops-VERSION.tar.gz diff --git a/build/pkgs/libatomic_ops/distros/cygwin.txt b/build/pkgs/libatomic_ops/distros/cygwin.txt new file mode 100644 index 00000000000..56dbd90c363 --- /dev/null +++ b/build/pkgs/libatomic_ops/distros/cygwin.txt @@ -0,0 +1 @@ +libatomic_ops-devel diff --git a/build/pkgs/libatomic_ops/distros/debian.txt b/build/pkgs/libatomic_ops/distros/debian.txt new file mode 100644 index 00000000000..de99e84f2b2 --- /dev/null +++ b/build/pkgs/libatomic_ops/distros/debian.txt @@ -0,0 +1 @@ +libatomic-ops-dev diff --git a/build/pkgs/libatomic_ops/distros/fedora.txt b/build/pkgs/libatomic_ops/distros/fedora.txt new file mode 100644 index 00000000000..3417f891c11 --- /dev/null +++ b/build/pkgs/libatomic_ops/distros/fedora.txt @@ -0,0 +1 @@ +libatomic_ops libatomic_ops-devel diff --git a/build/pkgs/libatomic_ops/package-version.txt b/build/pkgs/libatomic_ops/package-version.txt index e81e85b8104..dd812d3580c 100644 --- a/build/pkgs/libatomic_ops/package-version.txt +++ b/build/pkgs/libatomic_ops/package-version.txt @@ -1 +1 @@ -7.6.2 +7.6.10 diff --git a/build/pkgs/libatomic_ops/spkg-check b/build/pkgs/libatomic_ops/spkg-check deleted file mode 100644 index 704fbad24c7..00000000000 --- a/build/pkgs/libatomic_ops/spkg-check +++ /dev/null @@ -1,18 +0,0 @@ -cd src - -if [ "$SAGE_DEBUG" = "yes" ]; then - export CFLAGS="-O0 -g $CFLAGS" -else - export CFLAGS="-O2 -g $CFLAGS" -fi - -if [ "$SAGE64" = "yes" ]; then - export CFLAGS="-m64 $CFLAGS" -fi - -$MAKE check - -if [ $? -ne 0 ]; then - echo "Error testing libatomic_ops." - exit 1 -fi diff --git a/build/pkgs/libatomic_ops/spkg-check.in b/build/pkgs/libatomic_ops/spkg-check.in new file mode 100644 index 00000000000..1e19e3eb37f --- /dev/null +++ b/build/pkgs/libatomic_ops/spkg-check.in @@ -0,0 +1,13 @@ +cd src + +if [ "$SAGE_DEBUG" = "yes" ]; then + export CFLAGS="-O0 -g $CFLAGS" +else + export CFLAGS="-O2 -g $CFLAGS" +fi + +if [ "$SAGE64" = "yes" ]; then + export CFLAGS="-m64 $CFLAGS" +fi + +$MAKE check diff --git a/build/pkgs/libatomic_ops/spkg-install b/build/pkgs/libatomic_ops/spkg-install.in similarity index 100% rename from build/pkgs/libatomic_ops/spkg-install rename to build/pkgs/libatomic_ops/spkg-install.in diff --git a/build/pkgs/libbraiding/SPKG.rst b/build/pkgs/libbraiding/SPKG.rst new file mode 100644 index 00000000000..f4075593ab9 --- /dev/null +++ b/build/pkgs/libbraiding/SPKG.rst @@ -0,0 +1,25 @@ +LIBBRAIDING +=========== + +Description +----------- + +libbraiding is a library to compute several properties of braids, +including centralizer and conjugacy check. + +License +------- + +GPLv3+ + + +SPKG Maintainers +---------------- + +- Miguel Marco + + +Upstream Contact +---------------- + +Miguel Marco (mmarco@unizar.es) diff --git a/build/pkgs/libbraiding/SPKG.txt b/build/pkgs/libbraiding/SPKG.txt deleted file mode 100644 index 4afec494530..00000000000 --- a/build/pkgs/libbraiding/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= LIBBRAIDING = - -== Description == - -libbraiding is a library to compute several properties of braids, including centralizer and conjugacy check. - -== License == - -GPLv3+ - -== SPKG Maintainers == - -* Miguel Marco - -== Upstream Contact == - -Miguel Marco (mmarco@unizar.es) diff --git a/build/pkgs/libbraiding/distros/conda.txt b/build/pkgs/libbraiding/distros/conda.txt new file mode 100644 index 00000000000..3767599b368 --- /dev/null +++ b/build/pkgs/libbraiding/distros/conda.txt @@ -0,0 +1 @@ +libbraiding diff --git a/build/pkgs/libbraiding/distros/debian.txt b/build/pkgs/libbraiding/distros/debian.txt new file mode 100644 index 00000000000..fd1db82a131 --- /dev/null +++ b/build/pkgs/libbraiding/distros/debian.txt @@ -0,0 +1 @@ +libbraiding-dev diff --git a/build/pkgs/libbraiding/distros/fedora.txt b/build/pkgs/libbraiding/distros/fedora.txt new file mode 100644 index 00000000000..3767599b368 --- /dev/null +++ b/build/pkgs/libbraiding/distros/fedora.txt @@ -0,0 +1 @@ +libbraiding diff --git a/build/pkgs/libbraiding/distros/gentoo.txt b/build/pkgs/libbraiding/distros/gentoo.txt new file mode 100644 index 00000000000..80272c19728 --- /dev/null +++ b/build/pkgs/libbraiding/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/libbraiding diff --git a/build/pkgs/libbraiding/spkg-configure.m4 b/build/pkgs/libbraiding/spkg-configure.m4 new file mode 100644 index 00000000000..81f4d57f225 --- /dev/null +++ b/build/pkgs/libbraiding/spkg-configure.m4 @@ -0,0 +1,30 @@ +SAGE_SPKG_CONFIGURE([libbraiding], [ + # Since libbraiding is a C++ library with no pkg-config file, + # the best we can do here is compile and run a test program + # linked against it. + AC_LANG_PUSH(C++) + SAVED_LIBS=$LIBS + LIBS="$LIBS -lbraiding" + AC_MSG_CHECKING([if we can link against libbraiding]) + AC_RUN_IFELSE([ + AC_LANG_PROGRAM([ + #include + #include + using namespace Braiding; + ],[ + // Mimic BraidGroup(2)([1,1]).thurston_type() in SageMath. + // thurstontype == 1 corresponds to "periodic" + if (thurstontype(2, {1,1}) == 1) { return 0; } else { return 1; } + ]) + ], + [ + AC_MSG_RESULT([yes]) + sage_spkg_install_libbraiding=no + ], + [ + AC_MSG_RESULT([no]) + sage_spkg_install_libbraiding=yes + ]) + LIBS=$SAVED_LIBS + AC_LANG_POP +]) diff --git a/build/pkgs/libbraiding/spkg-install b/build/pkgs/libbraiding/spkg-install.in similarity index 100% rename from build/pkgs/libbraiding/spkg-install rename to build/pkgs/libbraiding/spkg-install.in diff --git a/build/pkgs/libffi/SPKG.rst b/build/pkgs/libffi/SPKG.rst new file mode 100644 index 00000000000..33e4e17f4b5 --- /dev/null +++ b/build/pkgs/libffi/SPKG.rst @@ -0,0 +1,60 @@ +libffi +====== + +Description +----------- + +Compilers for high level languages generate code that follow certain +conventions. These conventions are necessary, in part, for separate +compilation to work. One such convention is the "calling convention". +The "calling convention" is essentially a set of assumptions made by the +compiler about where function arguments will be found on entry to a +function. A "calling convention" also specifies where the return value +for a function is found. + +Some programs may not know at the time of compilation what arguments are +to be passed to a function. For instance, an interpreter may be told at +run-time about the number and types of arguments used to call a given +function. Libffi can be used in such programs to provide a bridge from +the interpreter program to compiled code. + +The libffi library provides a portable, high level programming interface +to various calling conventions. This allows a programmer to call any +function specified by a call interface description at run time. + +FFI stands for Foreign Function Interface. A foreign function interface +is the popular name for the interface that allows code written in one +language to call code written in another language. The libffi library +really only provides the lowest, machine dependent layer of a fully +featured foreign function interface. A layer must exist above libffi +that handles type conversions for values passed between the two +languages. + +License +------- + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Upstream Contact +---------------- + +- https://sourceware.org/libffi/ +- https://github.com/libffi/libffi diff --git a/build/pkgs/libffi/SPKG.txt b/build/pkgs/libffi/SPKG.txt deleted file mode 100644 index aea62b6fbc7..00000000000 --- a/build/pkgs/libffi/SPKG.txt +++ /dev/null @@ -1,55 +0,0 @@ -= libffi = - -== Description == - -Compilers for high level languages generate code that follow certain -conventions. These conventions are necessary, in part, for separate -compilation to work. One such convention is the "calling convention". -The "calling convention" is essentially a set of assumptions made by the -compiler about where function arguments will be found on entry to a -function. A "calling convention" also specifies where the return value -for a function is found. - -Some programs may not know at the time of compilation what arguments are -to be passed to a function. For instance, an interpreter may be told at -run-time about the number and types of arguments used to call a given -function. Libffi can be used in such programs to provide a bridge from -the interpreter program to compiled code. - -The libffi library provides a portable, high level programming interface -to various calling conventions. This allows a programmer to call any -function specified by a call interface description at run time. - -FFI stands for Foreign Function Interface. A foreign function interface -is the popular name for the interface that allows code written in one -language to call code written in another language. The libffi library -really only provides the lowest, machine dependent layer of a fully -featured foreign function interface. A layer must exist above libffi -that handles type conversions for values passed between the two -languages. - -== License == - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -``Software''), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -== Upstream Contact == - -https://sourceware.org/libffi/ -https://github.com/libffi/libffi diff --git a/build/pkgs/libffi/distros/cygwin.txt b/build/pkgs/libffi/distros/cygwin.txt new file mode 100644 index 00000000000..31d794ff28d --- /dev/null +++ b/build/pkgs/libffi/distros/cygwin.txt @@ -0,0 +1 @@ +libffi-devel diff --git a/build/pkgs/libffi/distros/fedora.txt b/build/pkgs/libffi/distros/fedora.txt new file mode 100644 index 00000000000..8d342f491c1 --- /dev/null +++ b/build/pkgs/libffi/distros/fedora.txt @@ -0,0 +1 @@ +libffi libffi-devel diff --git a/build/pkgs/libffi/distros/slackware.txt b/build/pkgs/libffi/distros/slackware.txt new file mode 100644 index 00000000000..eb88b305fdc --- /dev/null +++ b/build/pkgs/libffi/distros/slackware.txt @@ -0,0 +1 @@ +libffi diff --git a/build/pkgs/libffi/spkg-install b/build/pkgs/libffi/spkg-install.in similarity index 100% rename from build/pkgs/libffi/spkg-install rename to build/pkgs/libffi/spkg-install.in diff --git a/build/pkgs/libgd/SPKG.rst b/build/pkgs/libgd/SPKG.rst new file mode 100644 index 00000000000..804855e3a1e --- /dev/null +++ b/build/pkgs/libgd/SPKG.rst @@ -0,0 +1,38 @@ +gd +== + +Description +----------- + +GD is an open source code library for the dynamic creation of images by +programmers. GD is written in C, and "wrappers" are available for Perl, +PHP and other languages. GD creates PNG, JPEG, GIF, WebP, XPM, BMP +images, among other formats. GD is commonly used to generate charts, +graphics, thumbnails, and most anything else, on the fly. While not +restricted to use on the web, the most common applications of GD involve +website development. + +License +------- + +- Custom (BSD-ish) + + +Upstream Contact +---------------- + +- Pierre Joye (http://blog.thepimp.net) +- http://libgd.bitbucket.org/ + +Dependencies +------------ + +- libpng +- freetype +- iconv + + +Special Update/Build Instructions +--------------------------------- + +See spkg-src script. diff --git a/build/pkgs/libgd/SPKG.txt b/build/pkgs/libgd/SPKG.txt deleted file mode 100644 index ba400fdb876..00000000000 --- a/build/pkgs/libgd/SPKG.txt +++ /dev/null @@ -1,30 +0,0 @@ -= gd = - -== Description == - -GD is an open source code library for the dynamic creation of images -by programmers. GD is written in C, and "wrappers" are available for -Perl, PHP and other languages. GD creates PNG, JPEG, GIF, WebP, XPM, BMP -images, among other formats. GD is commonly used to generate charts, -graphics, thumbnails, and most anything else, on the fly. -While not restricted to use on the web, the most common applications of -GD involve website development. - -== License == - -* Custom (BSD-ish) - -== Upstream Contact == - -* Pierre Joye (http://blog.thepimp.net) -* http://libgd.bitbucket.org/ - -== Dependencies == - -* libpng -* freetype -* iconv - -== Special Update/Build Instructions == - -See spkg-src script. diff --git a/build/pkgs/libgd/distros/alpine.txt b/build/pkgs/libgd/distros/alpine.txt new file mode 100644 index 00000000000..3f310cfdeb0 --- /dev/null +++ b/build/pkgs/libgd/distros/alpine.txt @@ -0,0 +1 @@ +gd diff --git a/build/pkgs/libgd/distros/cygwin.txt b/build/pkgs/libgd/distros/cygwin.txt new file mode 100644 index 00000000000..3094bd88c2e --- /dev/null +++ b/build/pkgs/libgd/distros/cygwin.txt @@ -0,0 +1 @@ +libgd-devel diff --git a/build/pkgs/libgd/distros/fedora.txt b/build/pkgs/libgd/distros/fedora.txt new file mode 100644 index 00000000000..d27cc48549e --- /dev/null +++ b/build/pkgs/libgd/distros/fedora.txt @@ -0,0 +1 @@ +gd gd-devel diff --git a/build/pkgs/libgd/distros/gentoo.txt b/build/pkgs/libgd/distros/gentoo.txt new file mode 100644 index 00000000000..52aa3ac72a3 --- /dev/null +++ b/build/pkgs/libgd/distros/gentoo.txt @@ -0,0 +1 @@ +media-libs/gd diff --git a/build/pkgs/libgd/distros/homebrew.txt b/build/pkgs/libgd/distros/homebrew.txt new file mode 100644 index 00000000000..3f310cfdeb0 --- /dev/null +++ b/build/pkgs/libgd/distros/homebrew.txt @@ -0,0 +1 @@ +gd diff --git a/build/pkgs/libgd/distros/opensuse.txt b/build/pkgs/libgd/distros/opensuse.txt new file mode 100644 index 00000000000..3f310cfdeb0 --- /dev/null +++ b/build/pkgs/libgd/distros/opensuse.txt @@ -0,0 +1 @@ +gd diff --git a/build/pkgs/libgd/distros/slackware.txt b/build/pkgs/libgd/distros/slackware.txt new file mode 100644 index 00000000000..5d28bb87ae1 --- /dev/null +++ b/build/pkgs/libgd/distros/slackware.txt @@ -0,0 +1,3 @@ +gd +# shared library dependencies of gd +fontconfig libXpm libX11 libxcb libXau libXdmcp diff --git a/build/pkgs/libgd/spkg-install b/build/pkgs/libgd/spkg-install.in similarity index 100% rename from build/pkgs/libgd/spkg-install rename to build/pkgs/libgd/spkg-install.in diff --git a/build/pkgs/libgd/spkg-legacy-uninstall b/build/pkgs/libgd/spkg-legacy-uninstall.in similarity index 100% rename from build/pkgs/libgd/spkg-legacy-uninstall rename to build/pkgs/libgd/spkg-legacy-uninstall.in diff --git a/build/pkgs/libhomfly/SPKG.rst b/build/pkgs/libhomfly/SPKG.rst new file mode 100644 index 00000000000..b7da49a70c8 --- /dev/null +++ b/build/pkgs/libhomfly/SPKG.rst @@ -0,0 +1,30 @@ +LIBHOMFLY +========= + +Description +----------- + +libhomfly is a library to compute the homfly polynomial of knots and +links. + +License +------- + +Public domain + + +SPKG Maintainers +---------------- + +- Miguel Marco + + +Upstream Contact +---------------- + +Miguel Marco (mmarco@unizar.es) + +Dependencies +------------ + +- gc diff --git a/build/pkgs/libhomfly/SPKG.txt b/build/pkgs/libhomfly/SPKG.txt deleted file mode 100644 index 895be6c645e..00000000000 --- a/build/pkgs/libhomfly/SPKG.txt +++ /dev/null @@ -1,21 +0,0 @@ -= LIBHOMFLY = - -== Description == - -libhomfly is a library to compute the homfly polynomial of knots and links. - -== License == - -Public domain - -== SPKG Maintainers == - -* Miguel Marco - -== Upstream Contact == - -Miguel Marco (mmarco@unizar.es) - -== Dependencies == - -* gc diff --git a/build/pkgs/libhomfly/spkg-check b/build/pkgs/libhomfly/spkg-check.in similarity index 100% rename from build/pkgs/libhomfly/spkg-check rename to build/pkgs/libhomfly/spkg-check.in diff --git a/build/pkgs/libhomfly/spkg-install b/build/pkgs/libhomfly/spkg-install.in similarity index 100% rename from build/pkgs/libhomfly/spkg-install rename to build/pkgs/libhomfly/spkg-install.in diff --git a/build/pkgs/libogg/SPKG.rst b/build/pkgs/libogg/SPKG.rst new file mode 100644 index 00000000000..8c0da263050 --- /dev/null +++ b/build/pkgs/libogg/SPKG.rst @@ -0,0 +1,63 @@ +libogg +====== + +Description +----------- + +libogg is the official reference library for the Ogg multimedia +container format, and the native file and stream format for the Xiph.org +multimedia codecs. As with all Xiph.org technology is it an open format +free for anyone to use. + +Website: http://www.xiph.org/ogg + +License +------- + +Copyright (c) 2002, Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Upstream Contact +---------------- + +The Xiph.org mailing lists - see http://lists.xiph.org/mailman/listinfo + +Dependencies +------------ + +This spkg provides dependencies for + +- the Sage library + + +Special Update/Build Instructions +--------------------------------- + +- No changes went into src. diff --git a/build/pkgs/libogg/SPKG.txt b/build/pkgs/libogg/SPKG.txt deleted file mode 100644 index 7f0011fa9d5..00000000000 --- a/build/pkgs/libogg/SPKG.txt +++ /dev/null @@ -1,59 +0,0 @@ -= libogg = - -== Description == - -libogg is the official reference library for the Ogg multimedia container format, and the native file and -stream format for the Xiph.org multimedia codecs. As with all Xiph.org technology is it an open format free -for anyone to use. - -Website: http://www.xiph.org/ogg - -== License == - -Copyright (c) 2002, Xiph.org Foundation - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -- Neither the name of the Xiph.org Foundation nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -== Upstream Contact == - -The Xiph.org mailing lists - see http://lists.xiph.org/mailman/listinfo - -== Dependencies == - -This spkg provides dependencies for - - * the Sage library - -== Special Update/Build Instructions == - - * No changes went into src. - -== Changelog == - -=== libogg-1.1.4 (Wilfried Huss, October 19th, 2009) === - * ligogg-1.1.4 diff --git a/build/pkgs/libogg/spkg-install b/build/pkgs/libogg/spkg-install.in similarity index 100% rename from build/pkgs/libogg/spkg-install rename to build/pkgs/libogg/spkg-install.in diff --git a/build/pkgs/libpng/SPKG.rst b/build/pkgs/libpng/SPKG.rst new file mode 100644 index 00000000000..583872b5a50 --- /dev/null +++ b/build/pkgs/libpng/SPKG.rst @@ -0,0 +1,68 @@ +libpng +====== + +Description +----------- + +libpng is the official PNG reference library. It supports almost all PNG +features, is extensible, and has been extensively tested for over 13 +years. The home site for development versions (i.e., may be buggy or +subject to change or include experimental features) is +http://libpng.sourceforge.net/, and the place to go for questions about +the library is the png-mng-implement mailing list. + +Website: http://www.libpng.org/pub/png/libpng.html + +License +------- + +The libpng license - see +http://www.libpng.org/pub/png/src/libpng-LICENSE.txt + + +Upstream Contact +---------------- + +The png mailing lists - see +http://www.libpng.org/pub/png/pngmisc.html#lists + +Dependencies +------------ + +This spkg depends on: + +- libz + + +Special Update/Build Instructions +--------------------------------- + +- On old versions of Darwin, the symbolic links libpng.\* created by + libpng16 may + interfere with a system-wide libPng.dylib. + + -- the following is very likely to be obsolete in 2014 --- + + This system-wide library is likely to be a different version and on + top of that, the symbols exported there are prefixed with "_cg" + (for "Core Graphics"). So even if by chance the functionalities of + the two libraries were interchangeable, libraries or applications + looking for one and being presented the other won't find the symbols + they expect. Note the uppercase "P" which could prevent this + conflict; unfortunately, the default filesystem used by Apple is + case-insensitive. + + Note there would be no problem if the system-wide library was not + looked for when Sage is being built or run, but that's not the case + either; it is at least looked for by the "ImageIO" framework: + + - when Python is built with Mac OS extensions, fixed in #4008; + - when Mercurial is built because it uses $EDITOR, cf. #4678; + - when R is built and it finds ``-lpng``, cf. #4409 and #11696. + + -- this is no longer done, as of #27186 --- + + As not all of these problems are easily dealt with and new ones may + arise, we chose to delete the $SAGE_LOCAL/lib/libpng.\* symlinks. + Therefore, some packages like Tachyon, which by default look for + ``-lpng`` are patched to look for ``-lpng16`` instead. diff --git a/build/pkgs/libpng/SPKG.txt b/build/pkgs/libpng/SPKG.txt deleted file mode 100644 index 177fd285291..00000000000 --- a/build/pkgs/libpng/SPKG.txt +++ /dev/null @@ -1,56 +0,0 @@ -= libpng = - -== Description == - -libpng is the official PNG reference library. It supports almost all PNG -features, is extensible, and has been extensively tested for over 13 years. -The home site for development versions (i.e., may be buggy or subject to -change or include experimental features) is http://libpng.sourceforge.net/, -and the place to go for questions about the library is the png-mng-implement -mailing list. - -Website: http://www.libpng.org/pub/png/libpng.html - -== License == - -The libpng license - see http://www.libpng.org/pub/png/src/libpng-LICENSE.txt - -== Upstream Contact == - -The png mailing lists - see http://www.libpng.org/pub/png/pngmisc.html#lists - -== Dependencies == - -This spkg depends on: - - * libz - -== Special Update/Build Instructions == - - * On old versions of Darwin, the symbolic links libpng.* created by libpng16 may - interfere with a system-wide libPng.dylib. - - --- the following is very likely to be obsolete in 2014 --- - - This system-wide library is likely to be a different version and on - top of that, the symbols exported there are prefixed with "_cg" - (for "Core Graphics"). So even if by chance the functionalities of - the two libraries were interchangeable, libraries or applications - looking for one and being presented the other won't find the symbols - they expect. Note the uppercase "P" which could prevent this - conflict; unfortunately, the default filesystem used by Apple is - case-insensitive. - - Note there would be no problem if the system-wide library was not - looked for when Sage is being built or run, but that's not the case - either; it is at least looked for by the "ImageIO" framework: - - when Python is built with Mac OS extensions, fixed in #4008; - - when Mercurial is built because it uses $EDITOR, cf. #4678; - - when R is built and it finds -lpng, cf. #4409 and #11696. - - --- this is no longer done, as of #27186 --- - - As not all of these problems are easily dealt with and new ones may - arise, we chose to delete the $SAGE_LOCAL/lib/libpng.* symlinks. - Therefore, some packages like Tachyon, which by default look for - -lpng are patched to look for -lpng16 instead. diff --git a/build/pkgs/libpng/distros/homebrew.txt b/build/pkgs/libpng/distros/homebrew.txt new file mode 100644 index 00000000000..30c33ac62a1 --- /dev/null +++ b/build/pkgs/libpng/distros/homebrew.txt @@ -0,0 +1 @@ +libpng diff --git a/build/pkgs/libpng/distros/slackware.txt b/build/pkgs/libpng/distros/slackware.txt new file mode 100644 index 00000000000..30c33ac62a1 --- /dev/null +++ b/build/pkgs/libpng/distros/slackware.txt @@ -0,0 +1 @@ +libpng diff --git a/build/pkgs/libpng/spkg-configure.m4 b/build/pkgs/libpng/spkg-configure.m4 index 974c6138370..4a54a815fc8 100644 --- a/build/pkgs/libpng/spkg-configure.m4 +++ b/build/pkgs/libpng/spkg-configure.m4 @@ -8,9 +8,13 @@ SAGE_SPKG_CONFIGURE([libpng], [ AC_MSG_RESULT([no]) dnl First try checking for libpng with pkg-config PKG_CHECK_MODULES([LIBPNG], [libpng >= 1.2], [], [ - dnl Fallback to manually grubbing around for headers and libs - AC_CHECK_HEADERS([png.h], [break], [sage_spkg_install_libpng=yes]) - AC_SEARCH_LIBS([png_get_io_ptr], [png], [], [sage_spkg_install_libpng=yes]) + sage_spkg_install_libpng=yes + dnl Yes, we *could* fallback to manually grubbing around for headers and libs as follows: + dnl AC_CHECK_HEADERS([png.h], [break], [sage_spkg_install_libpng=yes]) + dnl AC_SEARCH_LIBS([png_get_io_ptr], [png], [], [sage_spkg_install_libpng=yes]) + dnl But 'matplotlib' and 'sagelib' rely on pkg-config to locate libpng. + dnl So we would have to tell them about the libpng that we found, + dnl for example by creating a facade .pc file like we do for BLAS. ]) fi ]) diff --git a/build/pkgs/libpng/spkg-install b/build/pkgs/libpng/spkg-install.in similarity index 100% rename from build/pkgs/libpng/spkg-install rename to build/pkgs/libpng/spkg-install.in diff --git a/build/pkgs/libsemigroups/SPKG.rst b/build/pkgs/libsemigroups/SPKG.rst new file mode 100644 index 00000000000..cf3b81d8de7 --- /dev/null +++ b/build/pkgs/libsemigroups/SPKG.rst @@ -0,0 +1,20 @@ +libffi +====== + +Description +----------- + +C++ library for semigroups and monoids; used in GAP's package +Semigroups. + +License +------- + +GPL-3.0 + + +Upstream Contact +---------------- + +http://james-d-mitchell.github.io/libsemigroups +https://github.com/james-d-mitchell/libsemigroups diff --git a/build/pkgs/libsemigroups/SPKG.txt b/build/pkgs/libsemigroups/SPKG.txt deleted file mode 100644 index 3d4c9478a9b..00000000000 --- a/build/pkgs/libsemigroups/SPKG.txt +++ /dev/null @@ -1,15 +0,0 @@ -= libffi = - -== Description == - -C++ library for semigroups and monoids; -used in GAP's package Semigroups. - -== License == - -GPL-3.0 - -== Upstream Contact == - -http://james-d-mitchell.github.io/libsemigroups -https://github.com/james-d-mitchell/libsemigroups diff --git a/build/pkgs/libsemigroups/spkg-check b/build/pkgs/libsemigroups/spkg-check.in similarity index 100% rename from build/pkgs/libsemigroups/spkg-check rename to build/pkgs/libsemigroups/spkg-check.in diff --git a/build/pkgs/libsemigroups/spkg-install b/build/pkgs/libsemigroups/spkg-install.in similarity index 100% rename from build/pkgs/libsemigroups/spkg-install rename to build/pkgs/libsemigroups/spkg-install.in diff --git a/build/pkgs/libtheora/SPKG.rst b/build/pkgs/libtheora/SPKG.rst new file mode 100644 index 00000000000..b964288c8e2 --- /dev/null +++ b/build/pkgs/libtheora/SPKG.rst @@ -0,0 +1,67 @@ +libtheora +========= + +Description +----------- + +libtheora is the official reference library for the Theora video codec. +Theora is a free and open video compression format from the Xiph.org +Foundation. + +Website: http://www.xiph.org/theora + +License +------- + +Copyright (c) 2002, Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Upstream Contact +---------------- + +The Xiph.org mailing lists - see http://lists.xiph.org/mailman/listinfo + +Dependencies +------------ + +This spkg depends on + +- libogg +- libpng + +This spkg provides dependencies for + +- the Sage library + + +Special Update/Build Instructions +--------------------------------- + +- No changes went into src. diff --git a/build/pkgs/libtheora/SPKG.txt b/build/pkgs/libtheora/SPKG.txt deleted file mode 100644 index 9526cf4fb1d..00000000000 --- a/build/pkgs/libtheora/SPKG.txt +++ /dev/null @@ -1,63 +0,0 @@ -= libtheora = - -== Description == - -libtheora is the official reference library for the Theora video codec. -Theora is a free and open video compression format from the Xiph.org Foundation. - -Website: http://www.xiph.org/theora - -== License == - -Copyright (c) 2002, Xiph.org Foundation - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -- Neither the name of the Xiph.org Foundation nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -== Upstream Contact == - -The Xiph.org mailing lists - see http://lists.xiph.org/mailman/listinfo - -== Dependencies == - -This spkg depends on - - * libogg - * libpng - -This spkg provides dependencies for - - * the Sage library - -== Special Update/Build Instructions == - - * No changes went into src. - -== Changelog == - -=== libtheora-1.1.1 (Wilfried Huss, October 19th, 2009) === - * libtheora-1.1.1 diff --git a/build/pkgs/libtheora/spkg-install b/build/pkgs/libtheora/spkg-install deleted file mode 100644 index fad8631f91a..00000000000 --- a/build/pkgs/libtheora/spkg-install +++ /dev/null @@ -1,32 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -unset RM - -cd src - -./configure \ - --prefix="$SAGE_LOCAL" \ - --libdir="$SAGE_LOCAL/lib" \ - --with-ogg="$SAGE_LOCAL" -if [ $? -ne 0 ]; then - echo "Error configuring libtheora" - exit 1 -fi - -$MAKE -if [ $? -ne 0 ]; then - echo "Error building libtheora" - exit 1 -fi - -$MAKE -j1 install -if [ $? -ne 0 ]; then - echo "Error installing libtheora" - exit 1 -fi - -cp examples/.libs/png2theora $SAGE_LOCAL/bin diff --git a/build/pkgs/libtheora/spkg-install.in b/build/pkgs/libtheora/spkg-install.in new file mode 100644 index 00000000000..b1f58b44681 --- /dev/null +++ b/build/pkgs/libtheora/spkg-install.in @@ -0,0 +1,24 @@ +cd src + +./configure \ + --prefix="$SAGE_LOCAL" \ + --libdir="$SAGE_LOCAL/lib" \ + --with-ogg="$SAGE_LOCAL" +if [ $? -ne 0 ]; then + echo "Error configuring libtheora" + exit 1 +fi + +$MAKE +if [ $? -ne 0 ]; then + echo "Error building libtheora" + exit 1 +fi + +$MAKE -j1 install +if [ $? -ne 0 ]; then + echo "Error installing libtheora" + exit 1 +fi + +cp examples/.libs/png2theora $SAGE_LOCAL/bin diff --git a/build/pkgs/lidia/SPKG.rst b/build/pkgs/lidia/SPKG.rst new file mode 100644 index 00000000000..a2edd189623 --- /dev/null +++ b/build/pkgs/lidia/SPKG.rst @@ -0,0 +1,32 @@ +lidia +===== + +Description +----------- + +A library for computational number theory. + +Abandoned upstream and has disappeared from the web at TU Darmstadt. + +We use as our new upstream a version minimally maintained for the LattE +project. + +https://www.math.ucdavis.edu/~latte/software/packages/lidia/current/lidia-2.3.0+latte-patches-2014-10-04.tar.gz + +License +------- + +lidia is released under the GPL, or so it is claimed. See +https://groups.google.com/forum/#!msg/sage-devel/kTxgPSqrbUM/5Txj3_IKhlQJ +and https://lists.debian.org/debian-legal/2007/07/msg00120.html + + +Upstream Contact +---------------- + +Matthias Köppe, UC Davis, CA, USA + +Dependencies +------------ + +GMP. diff --git a/build/pkgs/lidia/SPKG.txt b/build/pkgs/lidia/SPKG.txt deleted file mode 100644 index 54d712d6998..00000000000 --- a/build/pkgs/lidia/SPKG.txt +++ /dev/null @@ -1,29 +0,0 @@ -= lidia = - -== Description == - -A library for computational number theory. - -Abandoned upstream and has disappeared from the web at TU Darmstadt. - -We use as our new upstream a version minimally maintained for the -LattE project. - -https://www.math.ucdavis.edu/~latte/software/packages/lidia/current/lidia-2.3.0+latte-patches-2014-10-04.tar.gz - -== License == -lidia is released under the GPL, or so it is claimed. -See https://groups.google.com/forum/#!msg/sage-devel/kTxgPSqrbUM/5Txj3_IKhlQJ -and https://lists.debian.org/debian-legal/2007/07/msg00120.html - -== Upstream Contact == - -Matthias Köppe, UC Davis, CA, USA - -== Dependencies == - -GMP. - -=== lidia-2.3.0+latte-patches-2014-10-04 (Matthias Köppe, 2015-05-01) === - * First attempt at a Sage package. - diff --git a/build/pkgs/lidia/spkg-check b/build/pkgs/lidia/spkg-check deleted file mode 100644 index be939c3fe15..00000000000 --- a/build/pkgs/lidia/spkg-check +++ /dev/null @@ -1,8 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src -$MAKE check diff --git a/build/pkgs/lidia/spkg-check.in b/build/pkgs/lidia/spkg-check.in new file mode 100644 index 00000000000..27cd9419538 --- /dev/null +++ b/build/pkgs/lidia/spkg-check.in @@ -0,0 +1,2 @@ +cd src +$MAKE check diff --git a/build/pkgs/lidia/spkg-install b/build/pkgs/lidia/spkg-install.in similarity index 100% rename from build/pkgs/lidia/spkg-install rename to build/pkgs/lidia/spkg-install.in diff --git a/build/pkgs/lie/SPKG.rst b/build/pkgs/lie/SPKG.rst new file mode 100644 index 00000000000..115261d7446 --- /dev/null +++ b/build/pkgs/lie/SPKG.rst @@ -0,0 +1,53 @@ +LiE +=== + +Description +----------- + +LiE is the name of a software package that enables mathematicians and +physicists to perform computations of a Lie group theoretic nature. It +focuses on the representation theory of complex semisimple (reductive) +Lie groups and algebras, and on the structure of their Weyl groups and +root systems. + +LiE does not compute directly with elements of the Lie groups and +algebras themselves; it rather computes with weights, roots, characters +and similar objects. Some specialities of LiE are: tensor product +decompositions, branching to subgroups, Weyl group orbits, reduced +elements in Weyl groups, distinguished coset representatives and much +more. These operations have been compiled into the program which results +in fast execution: typically one or two orders of magnitude faster than +similar programs written in a general purpose program. + +The LiE programming language makes it possible to customise and extend +the package with more mathematical functions. A user manual is provided +containing many examples. + +LiE establishes an interactive environment from which commands can be +given that involve basic programming primitives and powerful built-in +functions. These commands are read by an interpreter built into the +package and passed to the core of the system. This core consists of +programs representing some 100 mathematical functions. The interpreter +offers on-line facilities which explain operations and functions, and +which give background information about Lie group theoretical concepts +and about currently valid definitions and values. + +(from http://www-math.univ-poitiers.fr/~maavl/LiE/description.html ) + +License +------- + +GNU Lesser General Public License (LGPL), version unspecified + + +Upstream Contact +---------------- + +- Marc van Leeuwen, http://www-math.univ-poitiers.fr/~maavl/ + +Dependencies +------------ + +- readline +- ncurses +- bison (not included in this package or in Sage!) diff --git a/build/pkgs/lie/SPKG.txt b/build/pkgs/lie/SPKG.txt deleted file mode 100644 index dba5cbdc69f..00000000000 --- a/build/pkgs/lie/SPKG.txt +++ /dev/null @@ -1,48 +0,0 @@ -= LiE = - -== Description == - -LiE is the name of a software package that enables mathematicians and -physicists to perform computations of a Lie group theoretic nature. It -focuses on the representation theory of complex semisimple (reductive) -Lie groups and algebras, and on the structure of their Weyl groups and -root systems. - -LiE does not compute directly with elements of the Lie groups and -algebras themselves; it rather computes with weights, roots, characters -and similar objects. Some specialities of LiE are: tensor product -decompositions, branching to subgroups, Weyl group orbits, reduced -elements in Weyl groups, distinguished coset representatives and much -more. These operations have been compiled into the program which results -in fast execution: typically one or two orders of magnitude faster than -similar programs written in a general purpose program. - -The LiE programming language makes it possible to customise and extend -the package with more mathematical functions. A user manual is provided -containing many examples. - -LiE establishes an interactive environment from which commands can be -given that involve basic programming primitives and powerful built-in -functions. These commands are read by an interpreter built into the -package and passed to the core of the system. This core consists of -programs representing some 100 mathematical functions. The interpreter -offers on-line facilities which explain operations and functions, and -which give background information about Lie group theoretical concepts -and about currently valid definitions and values. - -(from http://www-math.univ-poitiers.fr/~maavl/LiE/description.html ) - -== License == - -GNU Lesser General Public License (LGPL), version unspecified - -== Upstream Contact == - - * Marc van Leeuwen, http://www-math.univ-poitiers.fr/~maavl/ - -== Dependencies == - - * readline - * ncurses - * bison (not included in this package or in Sage!) - diff --git a/build/pkgs/lie/spkg-install b/build/pkgs/lie/spkg-install.in similarity index 100% rename from build/pkgs/lie/spkg-install rename to build/pkgs/lie/spkg-install.in diff --git a/build/pkgs/linbox/SPKG.rst b/build/pkgs/linbox/SPKG.rst new file mode 100644 index 00000000000..93c854986ed --- /dev/null +++ b/build/pkgs/linbox/SPKG.rst @@ -0,0 +1,57 @@ +LinBox +====== + +Description +----------- + +From http://linalg.org/: LinBox is a C++ template library for exact, +high-performance linear algebra computation with dense, sparse, and +structured matrices over the integers and over finite fields. + +License +------- + +LGPL V2 or later + + +Upstream Contact +---------------- + +- +- + + +SPKG Repository +--------------- + + https://bitbucket.org/malb/linbox-spkg + +Dependencies +------------ + +- GNU patch +- GMP/MPIR +- MPFR +- NTL +- fpLLL +- IML +- M4RI +- M4RIE +- Givaro +- FFLAS/FFPACK +- ATLAS (non-OSX)/The Accelerate FrameWork (on OSX) +- ATLAS (non-MacOS X) / The Accelerate FrameWork (on MacOS X), or GSL's + CBLAS + + +Special Update/Build Instructions +--------------------------------- + +TODO: + +- spkg-check is disabled for now, should work in the next release + after 1.3.2. + +- Check whether ``make fullcheck`` works/builds, is worth running, and + doesn't + take ages. (Version 1.1.6 doesn't seem to have such a target.) diff --git a/build/pkgs/linbox/SPKG.txt b/build/pkgs/linbox/SPKG.txt deleted file mode 100644 index ef08537f536..00000000000 --- a/build/pkgs/linbox/SPKG.txt +++ /dev/null @@ -1,209 +0,0 @@ -= LinBox = - -== Description == - -From http://linalg.org/: LinBox is a C++ template library for exact, -high-performance linear algebra computation with dense, sparse, and -structured matrices over the integers and over finite fields. - -== License == - -LGPL V2 or later - -== Upstream Contact == - - * - * - -== SPKG Repository == - - https://bitbucket.org/malb/linbox-spkg - -== Dependencies == - - * GNU patch - * GMP/MPIR - * MPFR - * NTL - * fpLLL - * IML - * M4RI - * M4RIE - * Givaro - * FFLAS/FFPACK - * ATLAS (non-OSX)/The Accelerate FrameWork (on OSX) - * ATLAS (non-MacOS X) / The Accelerate FrameWork (on MacOS X), or GSL's CBLAS - -== Special Update/Build Instructions == - -TODO: - - spkg-check is disabled for now, should work in the next release - after 1.3.2. - - Check whether `make fullcheck` works/builds, is worth running, and doesn't - take ages. (Version 1.1.6 doesn't seem to have such a target.) - -== Changelog == - -=== linbox-1.3.2.p0 (Jean-Pierre Flori, 25 November 2012) === - * Trac #13755: let LinBox build with MPIR >= 2.5.0. - -=== linbox-1.3.2 (Martin Albrecht, Volker Braun, 15 August 2012) === - * Trac: 12883: New upstream release - * split off fflas/ffpack SPKG - * The whole -fpermissive stuff isn't required any more - -=== linbox-1.1.6.p11 (Jeroen Demeyer, 19 June 2012) === - * #13118: Don't look at compiler versions, just use the -fpermissive - flag whenever the compiler supports it. - -=== linbox-1.1.6.p10 (Jeroen Demeyer, 25 May 2012) === - * #12762 review: Remove the touching of linbox.pyx, since - Cython knows the dependency of linbox.pyx on linbox-sage.h - * Only add the -fpermissive workaround on GCC-4.7.x, not other - compilers. - -=== linbox-1.1.6.p9 (Leif Leonhardy, April 7th 2012) === - * #12762: Temporarily add `-fpermissive` to `CXXFLAGS` if we're compiling - with `g++` 4.7.x, since the LinBox sources currently don't conform to - C++11, so GCC 4.7.x would otherwise reject them. - * Exit if the build failed. - * Use `CFLAG64` if it is set (and `SAGE64=yes`). - * Clean up `spkg-install`, add some messages. - * Add an `spkg-check` file, which currently runs `make check`. (There's also - a `fullcheck` target.) - * Change patch to disable the commentator, as default parameters were missing - with `-DDISABLE_COMMENTATOR`, such that the test suite wouldn't build. - Also, one must not unconditionally use `extern` for the global (dummy) - commentator since this is C++, and doing so also breaks the test suite. - * Fix (i.e. patch) the sources such that the test suite (`make check`) builds, - also with GCC 4.7.0. - * Add the "Special Update/Build Instructions" section. - -=== linbox-1.1.6.p8 (William Stein, 18 March 2012) === - * Trac #10281: Multimodular echelon form over cyclotomic fields fails - -=== linbox-1.1.6.p7 (Jeroen Demeyer, 5 March 2012) === - * Trac #12629: *always* disable the commentator. There are problems - om some systems (e.g. OS X 10.4 with GCC 4.6.3) when parts of LinBox - are compiled with the commentator and parts without. - * Backport patch disable_commentator.patch from LinBox-1.2.2 to enable - LinBox to be built with the commentator disabled. - * Remove all -I and -L compiler flags from spkg-install, ./configure - should detect these. - * Use $MAKE instead of make. - * Use patch for patching. - -=== linbox-1.1.6.p6 (Simon King, December 10th, 2011) === - * #12131: Use --libdir, to make the package work on openSUSE. - -=== linbox-1.1.6.p5 (Martin Albrecht, October 10th, 2011) === - * removed spkg-rebuild - * removed spkg-debian and the dist directory - * removed "linbox" from .hgignore - * added patch for file file patches/commentator.C - -=== linbox-1.1.6.p4 (Martin Albrecht, August 23rd, 2011) === - * add NonZeroRandIter to modular-float.h (fixed in 1.1.7) - -=== linbox-1.1.6.p3 (Jaap Spies, Jan 25th, 2010) === - -=== linbox-1.1.6.p2 (William Stein, ?) === - * ???? - -=== linbox-1.1.6.p1 (William Stein, Sept 21, 2009) === - * Use systemwide lapack on windows. - -=== linbox-1.1.6.p0 (Mike Hansen, June 20th, 2008) === - * Applied Peter Jeremy's FreeBSD fix at #5870. - -=== linbox-1.1.6 (Clement Pernet, Sept 18th, 2008) === - * Upgrade to 1.1.6 release upstream - * including the fixes of bugs related to cygwin (gcc-3.4, linking parameter - order,...) - -=== linbox-1.1.6rc1 (Clement Pernet, Aug 12th, 2008) === - * Fix bug in Charpoly and revert to the "good" algorithm. See #3671 - * upstream linbox-1.1.6rc1 - * uniformize source directory name (linbox->src) - -=== linbox-1.1.6.p0 (Michael Abshoff, July 21st, 2008) === - * Integrate patch by Clement Pernet fixing #3671 - * miscellaneous cleanup - -=== linbox-1.1.6 (Clement Pernet, June 14th, 2008) === - * Upstream 1.1.6 linbox version - * merge former linbox_wrap in linbox/interface - * no more gmp++ in LinBox - * several bug fixes - -=== linbox-1.1.5.p6 (Michael Abshoff, May 18th, 2008) === - * fix 64 bit OSX support - -=== linbox-1.1.5.p5 (Michael Abshoff/William Stein, May 16, 2008) === - * add support for cygwin - -=== linbox-1.1.5.p4 (Michael Abshoff, April 15th, 2008) === - * reenable optimization on all platforms because the detection was broken (fixes #3041) - -=== linbox-1.1.5.p3 (Michael Abshoff, April 15th, 2008) === - * apply gcc 4.3 build patch - -=== linbox-1.1.5.p2 (Michael Abshoff, April 9th, 2008) === - * Apply Clement Pernet's commentator fix (#2833) - * clean up spkg-install some more and remove unneeded and faulty gmp++ copying - -=== linbox-1.1.5.p1 (Clement Pernet, April 4th, 2008) === - * Revert charpoly method to LUK, waiting to investigate further the bug in ArithProg method (ticket #2804) - -=== linbox-1.1.5 (Clement Pernet, April 2nd, 2008) === - * Remove every patch - * Put upstream final 1.1.5 release of LinBox - * Remove useless patches in dist/debian/linbox-debian - -=== linbox-1.1.5rc2.p7 (Michael Abshoff, April 1st, 2008) === - * Copyright files for Debian packages (Tim Abbott, #2199) - * linbox updates for Debian gfortran transition (Tim Abbott, #2758) - -=== linbox-1.1.5rc2.p6 (Michael Abshoff, March 22nd, 2008) === - * integrate Debian build infrastructure (Tim Abbott, #2647) - * clean up SPKG.txt - * commit all outstanding changes - -=== linbox-1.1.5rc2.p5 (William Stein, March 17th, 2008) === - * bump version number to force rebuild on upgrade due to updated Givaro - -=== linbox-1.1.5rc2.p4 (Clement Pernet) === - * revert to a better commentator.h, which now works on PPC, and still uses static - -=== linbox-1.1.5rc2.p3 (Clement Pernet, William Stein, March 10th, 2008) === - * fix the bug with static_initialization of commentator on PPC. (ticket 2463) - -=== linbox-1.1.5rc2.p2 (Michael Abshoff, March 10th, 2008) === - * remove buggy case in libcblas detect (#2458) - -=== linbox-1.1.5rc2.p1 (Clement Pernet, March 4th, 2008) === - * Update full rank submatrix wrapper - * Set default alg for charpoly to ArithProg, thus avoiding Darwin-static initialization gcc bug (which still has to be addressed) - -=== linbox-1.1.5rc2.p0 (Michael Abshoff, March 3rd, 2008) === - * Apply Clement Pernet's PID_Integer patch (fixed #915) - -=== linbox-1.1.5rc2 (Clement Pernet, March 2nd, 2008) === - * updated to upstream 1.1.5rc2 - * added additional funcionality in linbox_wrap - -=== 2007-12-16 (Michael Abshoff) === - * detect internal ATLAS and link against it per default on non-OSX - -=== 2007-11-13 (Michael Abshoff) === - * Apply Clement Pernet's charpoly leak fix, i.e. the dreaded BLAS:MatrixHom - -=== 2007-10-29: (Michael Abshoff) === - * added fix for #1026 - * add "-g" to CXXFLAGS and CFLAGS for better valgrind output - * add .hgignore - * add all files under patches and linbox_wrap to hg repo - -=== 2007-09-03 (Michael Abshoff) === - * merged LinBox ChangeSet 2803, which fixes #498 without the performance regression of the initial workaround - 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/fix-ksh-pkgconfig.patch b/build/pkgs/linbox/patches/fix-ksh-pkgconfig.patch new file mode 100644 index 00000000000..e0cb575b1a1 --- /dev/null +++ b/build/pkgs/linbox/patches/fix-ksh-pkgconfig.patch @@ -0,0 +1,28 @@ +From 52c78df67a08de074991a93b57946b7bd5ea7196 Mon Sep 17 00:00:00 2001 +From: Dima Pasechnik +Date: Fri, 8 May 2020 15:53:25 +0100 +Subject: [PATCH 1/1] remove redundant 1st and last lines + +they remain if the script is run under ksh, leading to broken .pc file +--- + linbox.pc.in | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/linbox.pc.in b/linbox.pc.in +index f54285e..eb6835b 100644 +--- a/linbox.pc.in ++++ b/linbox.pc.in +@@ -1,4 +1,3 @@ +-/------------------ linbox.pc ------------------------ + prefix=@prefix@ + exec_prefix=@prefix@ + libdir=@libdir@ +@@ -11,4 +10,4 @@ Version: @VERSION@ + Requires: fflas-ffpack >= 2.4.0, givaro >= 4.1.0 + Libs: -L${libdir} -llinbox @LINBOXSAGE_LIBS@ @NTL_LIBS@ @MPFR_LIBS@ @FPLLL_LIBS@ @IML_LIBS@ @FLINT_LIBS@ @OCL_LIBS@ + Cflags: @DEFAULT_CFLAGS@ -DDISABLE_COMMENTATOR -I${includedir} @NTL_CFLAGS@ @MPFR_CFLAGS@ @FPLLL_CFLAGS@ @IML_CFLAGS@ @FLINT_CFLAGS@ +-\------------------------------------------------------- ++ +-- +2.26.2 + diff --git a/build/pkgs/linbox/patches/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/linbox/spkg-check b/build/pkgs/linbox/spkg-check deleted file mode 100644 index ef201aa9dbf..00000000000 --- a/build/pkgs/linbox/spkg-check +++ /dev/null @@ -1,20 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src/ - -# We don't have to set up any environment variables here since the -# Makefiles already have them from 'configure'. (Hopefully.) - -echo "Building and running LinBox's test suite..." -# There's also a 'fullcheck' target mentioned in the online installation -# instructions, but it's apparently not in our current version (1.1.6). -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error: LinBox's test suite failed." - exit 1 -fi -echo "LinBox's test suite apparently passed without errors." diff --git a/build/pkgs/linbox/spkg-check.in b/build/pkgs/linbox/spkg-check.in new file mode 100644 index 00000000000..5bb0bac20de --- /dev/null +++ b/build/pkgs/linbox/spkg-check.in @@ -0,0 +1,8 @@ +cd src/ + +# We don't have to set up any environment variables here since the +# Makefiles already have them from 'configure'. (Hopefully.) + +# There's also a 'fullcheck' target mentioned in the online installation +# instructions, but it's apparently not in our current version (1.1.6). +$MAKE check diff --git a/build/pkgs/linbox/spkg-install b/build/pkgs/linbox/spkg-install.in similarity index 100% rename from build/pkgs/linbox/spkg-install rename to build/pkgs/linbox/spkg-install.in diff --git a/build/pkgs/lrcalc/SPKG.rst b/build/pkgs/lrcalc/SPKG.rst new file mode 100644 index 00000000000..80f8bb08448 --- /dev/null +++ b/build/pkgs/lrcalc/SPKG.rst @@ -0,0 +1,22 @@ +lrcalc +====== + +Description +----------- + +Littlewood-Richardson Calculator + +http://math.rutgers.edu/~asbuch/lrcalc/ + +License +------- + +GNU General Public License V2+ + + +Upstream Contact +---------------- + +Anders S. Buch (asbuch@math.rutgers.edu) + +https://bitbucket.org/asbuch/lrcalc diff --git a/build/pkgs/lrcalc/SPKG.txt b/build/pkgs/lrcalc/SPKG.txt deleted file mode 100644 index 2ac951c59fd..00000000000 --- a/build/pkgs/lrcalc/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= lrcalc = - -== Description == - -Littlewood-Richardson Calculator - -http://math.rutgers.edu/~asbuch/lrcalc/ - -== License == - -GNU General Public License V2+ - -== Upstream Contact == - -Anders S. Buch (asbuch@math.rutgers.edu) - -https://bitbucket.org/asbuch/lrcalc diff --git a/build/pkgs/lrcalc/distros/gentoo.txt b/build/pkgs/lrcalc/distros/gentoo.txt new file mode 100644 index 00000000000..776c334a9a5 --- /dev/null +++ b/build/pkgs/lrcalc/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/lrcalc diff --git a/build/pkgs/lrcalc/spkg-check b/build/pkgs/lrcalc/spkg-check deleted file mode 100644 index 636c00f6c16..00000000000 --- a/build/pkgs/lrcalc/spkg-check +++ /dev/null @@ -1,14 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -$MAKE check -if [ $? -ne 0 ]; then - echo "Error testing lrcalc." - exit 1 -fi - diff --git a/build/pkgs/lrcalc/spkg-check.in b/build/pkgs/lrcalc/spkg-check.in new file mode 100644 index 00000000000..917c8eb7209 --- /dev/null +++ b/build/pkgs/lrcalc/spkg-check.in @@ -0,0 +1,3 @@ +cd src + +$MAKE check diff --git a/build/pkgs/lrcalc/spkg-install b/build/pkgs/lrcalc/spkg-install deleted file mode 100644 index 46b765298c6..00000000000 --- a/build/pkgs/lrcalc/spkg-install +++ /dev/null @@ -1,8 +0,0 @@ -cd src - -# Use newer version of config.guess and config.sub (see Trac #19725) -cp "$SAGE_ROOT"/config/config.* . - -sdh_configure -sdh_make -sdh_make_install diff --git a/build/pkgs/lrcalc/spkg-install.in b/build/pkgs/lrcalc/spkg-install.in new file mode 100644 index 00000000000..d6665bfb427 --- /dev/null +++ b/build/pkgs/lrcalc/spkg-install.in @@ -0,0 +1,9 @@ +cd src + +# Use newer version of config.guess and config.sub (see Trac #19725) +cp "$SAGE_ROOT"/config/config.guess . +cp "$SAGE_ROOT"/config/config.sub . + +sdh_configure +sdh_make +sdh_make_install diff --git a/build/pkgs/lrslib/SPKG.rst b/build/pkgs/lrslib/SPKG.rst new file mode 100644 index 00000000000..852810abb5e --- /dev/null +++ b/build/pkgs/lrslib/SPKG.rst @@ -0,0 +1,39 @@ +lrslib +====== + +Description +----------- + +lrslib implements the linear reverse search algorithm of Avis and +Fukuda. + +See the homepage (http://cgm.cs.mcgill.ca/~avis/C/lrs.html) for details. + +We use an autotoolized version from +https://github.com/mkoeppe/lrslib/tree/autoconfiscation + +License +------- + +lrslib is released under a GPL v2+ license. + + +Upstream Contact +---------------- + +David Avis, avis at cs dot mcgill dot edu. + +Dependencies +------------ + +To build and install the "plrs" binary, a multi-thread version of lrs, +need to first install the full Boost package ("sage -i boost"). + +If the package finds an MPI C++ compiler script (mpic++), it also builds +and installs the "mplrs" binary, a distributed version of lrs using MPI. + +(Sage currently does not make use of plrs and mplrs.) + + +Special Update/Build Instructions +--------------------------------- diff --git a/build/pkgs/lrslib/SPKG.txt b/build/pkgs/lrslib/SPKG.txt deleted file mode 100644 index a940babecbb..00000000000 --- a/build/pkgs/lrslib/SPKG.txt +++ /dev/null @@ -1,30 +0,0 @@ -= lrslib = - -== Description == - -lrslib implements the linear reverse search algorithm of Avis and Fukuda. - -See the homepage (http://cgm.cs.mcgill.ca/~avis/C/lrs.html) for details. - -We use an autotoolized version from https://github.com/mkoeppe/lrslib/tree/autoconfiscation - -== License == -lrslib is released under a GPL v2+ license. - -== Upstream Contact == - -David Avis, avis at cs dot mcgill dot edu. - -== Dependencies == - -To build and install the "plrs" binary, a multi-thread version of lrs, -need to first install the full Boost package ("sage -i boost"). - -If the package finds an MPI C++ compiler script (mpic++), it also -builds and installs the "mplrs" binary, a distributed version of lrs -using MPI. - -(Sage currently does not make use of plrs and mplrs.) - -== Special Update/Build Instructions == - diff --git a/build/pkgs/lrslib/spkg-check b/build/pkgs/lrslib/spkg-check deleted file mode 100644 index db30b3b632b..00000000000 --- a/build/pkgs/lrslib/spkg-check +++ /dev/null @@ -1,14 +0,0 @@ -cd src/ - -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -$MAKE check -if [ $? -ne 0 ]; then - echo "Error in testing lrs" - exit 1 -fi - diff --git a/build/pkgs/lrslib/spkg-check.in b/build/pkgs/lrslib/spkg-check.in new file mode 100644 index 00000000000..917c8eb7209 --- /dev/null +++ b/build/pkgs/lrslib/spkg-check.in @@ -0,0 +1,3 @@ +cd src + +$MAKE check diff --git a/build/pkgs/lrslib/spkg-install b/build/pkgs/lrslib/spkg-install.in similarity index 100% rename from build/pkgs/lrslib/spkg-install rename to build/pkgs/lrslib/spkg-install.in diff --git a/build/pkgs/m4ri/SPKG.rst b/build/pkgs/m4ri/SPKG.rst new file mode 100644 index 00000000000..6b71b777848 --- /dev/null +++ b/build/pkgs/m4ri/SPKG.rst @@ -0,0 +1,36 @@ +M4RI +==== + +Description +----------- + +M4RI: Library for matrix multiplication, reduction and inversion over +GF(2). (See also m4ri/README for a brief overview.) + +License +------- + +- GNU General Public License Version 2 or later (see src/COPYING) + + +Upstream Contact +---------------- + +- Authors: Martin Albrecht et al. +- Email: +- Website: https://bitbucket.org/malb/m4ri + +Dependencies +------------ + +- libPNG + + +Special Update/Build Instructions +--------------------------------- + +- Delete the upstream Mercurial repositories (file m4ri/.hgtags, + directory m4ri/.hg). +- Delete the directory m4ri/autom4te.cache (if present). +- Delete m4ri.vcproj (and perhaps other unnecessary baggage). +- Touch m4ri/configure to make sure it is newer than its sources. diff --git a/build/pkgs/m4ri/SPKG.txt b/build/pkgs/m4ri/SPKG.txt deleted file mode 100644 index 1922b3a98cb..00000000000 --- a/build/pkgs/m4ri/SPKG.txt +++ /dev/null @@ -1,138 +0,0 @@ -= M4RI = - -== Description == - -M4RI: Library for matrix multiplication, reduction and inversion over -GF(2). (See also m4ri/README for a brief overview.) - -== License == - - * GNU General Public License Version 2 or later (see src/COPYING) - -== Upstream Contact == - - * Authors: Martin Albrecht et al. - * Email: - * Website: https://bitbucket.org/malb/m4ri - -== Dependencies == - - * libPNG - -== Special Update/Build Instructions == - * Delete the upstream Mercurial repositories (file m4ri/.hgtags, directory m4ri/.hg). - * Delete the directory m4ri/autom4te.cache (if present). - * Delete m4ri.vcproj (and perhaps other unnecessary baggage). - * Touch m4ri/configure to make sure it is newer than its sources. - -== Releases/Changelog == - -=== libm4ri-20130416 (Martin Albrecht, 22 March 2013) === - * #14335: new upstream release - -=== libm4ri-20121224 (Martin Albrecht, 21 December 2012) === - * #13858: new upstream release - -=== libm4ri-20120613 (Martin Albrecht, 7 June 2012) === - * #12840: new upstream release - * remove old headers before installing new ones - -=== libm4ri-20111004.p2 (John Palmieri, 23 March 2012) === - * #12311: Remove the script testcc.sh: - just use the version in the PATH (i.e., in SAGE_ROOT/spkg/bin) - -=== libm4ri-20111004.p1 (Keshav Kini, 2012-03-18) === - * #12694: Normalize directory structure - -=== libm4ri-20111004.p0 (Simon King, December 10th, 2011) === - * #12131: Use --libdir, to make the package work on openSUSE. - -=== libm4ri-20111004 (Martin Albrecht, October 4th, 2011) === - * new upstream release - -=== libm4ri-20110901 (Martin Albrecht, August 29th 2011) === - * new upstream release dealing with CFLAGS better - * dropped dist/ subdir - -=== libm4ri-20110715 (Martin Albrecht, July 6th 2011) === - * split M4RI and M4RIE in separate packages - -=== libm4ri-20100817 (Martin Albrecht, August 18th 2010) === - * Including M4RIE, an extension to M4RI for small extensions of GF(2). - * Enable tuning code to detect "cache sizes" - -=== libm4ri-20100701.p1 (Leif Leonhardy, July 13th 2010) === - * Committed Martin Albrecht's changes of July 13th (minor fixes, see #9475). - * SPKG.txt: - - Added "License" and "Special Update/Build Instructions" sections, - fixed some typos, some additions. - * spkg-install: - - Fixed old typo (CLFAGS), fixed syntax error due to missing spaces that - prevented SAGE_FAT_BINARY working. - - Renamed $SSE2_SUPPORT to $DISABLE_SSE2. - - Removed $SAGE_LOCAL/include from preprocessor search path since M4RI - doesn't depend on any Sage package (similarly for library search path). - - Removed redundant --includedir=... from configure. - - Some restructuring. - * spkg-check: - - Replaced "make" by "$MAKE". - - *Append* "-m64" to $CFLAGS rather than overwrite them if $SAGE64 is "yes". - * Removed extra baggage (see "Special Update/Build Instructions" above). - * (Note: There was no official p0. The above changes are all #9475.) - -=== libm4ri-20100701 (Martin Albrecht, July 11th, 2010) === - * new upstream release - + refactoring (function names now match what the function is doing) - + heuristic algorithm choice for RREF - + OpenMP tuning and fixes - + new option to suppress SSE2 instructions - * respecting SAGE_FAT_BINARY (cf. #9381) - * adding spkg-check (cf. #9281) - -=== libm4ri-20091119 (Martin Albrecht, November 19th, 2009) === - * portability improvements in configure.ac, cf. http://trac.sagemath.org/sage_trac/ticket/7375#comment:6 - -=== libm4ri-20091101 (Martin Albrecht, November 1st, 2009) === - * new upstream release - + switched to LQUP instead of PLUQ for better performance - + because of this and other improvements much better handling of sparse-ish matrices - + overall better performance for elimination - + better performance for mzd_transpose - + dropped the check for the numer of CPUs from configure which was unused and not cross platform - + optional tuning code to calculate cache sizes (not enabled by default) - + some refactoring - + mzd_row_add_offset() fixed a segfault - -=== libm4ri-20090615 (Martin Albrecht, June 15th, 2009) === - * new upstream release with bugfixes and new functionality (nullspace) - -=== libm4ri-20090512 (Martin Albrecht, May 12th, 2009) === - * new upstream release with API changes - -=== libm4ri-20090128 (Martin Albrecht, January 28th, 2009) === - * new upstream release with bug fixes and performance enhancements - -=== libm4ri-20080904 (Martin Albrecht, September 4th, 2008) === - * new upstream release with bug fixes and portability enhancements - -=== libm4ri-20080901 (Martin Albrecht, September 1st, 2008) === - * new upstream release - -=== libm4ri-20080831 (Martin Albrecht, August 31th, 2008) === - * new upstream release - -=== libm4ri-20080624 (Martin Albrecht, August 6th, 2008) === - * new upstream release - -=== libm4ri-20080601 (Martin Albrecht, June 1st, 2008) === - * new upstream release - -=== libm4ri-20080521 (Martin Albrecht, May 21th, 2008) === - * new upstream release - -=== libm4ri-20071224.p3 (Michael Abshoff, May 18th, 2008) === - * add 64 bit OSX build support - -=== libm4ri-20071224.p2 === - * Details lost to history - diff --git a/build/pkgs/m4ri/distros/arch.txt b/build/pkgs/m4ri/distros/arch.txt new file mode 100644 index 00000000000..414704b4ece --- /dev/null +++ b/build/pkgs/m4ri/distros/arch.txt @@ -0,0 +1 @@ +m4ri diff --git a/build/pkgs/m4ri/distros/gentoo.txt b/build/pkgs/m4ri/distros/gentoo.txt new file mode 100644 index 00000000000..44185021448 --- /dev/null +++ b/build/pkgs/m4ri/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/m4ri[png] diff --git a/build/pkgs/m4ri/distros/opensuse.txt b/build/pkgs/m4ri/distros/opensuse.txt new file mode 100644 index 00000000000..b6b04790806 --- /dev/null +++ b/build/pkgs/m4ri/distros/opensuse.txt @@ -0,0 +1 @@ +libm4ri diff --git a/build/pkgs/m4ri/spkg-check b/build/pkgs/m4ri/spkg-check deleted file mode 100644 index 69fe8142a10..00000000000 --- a/build/pkgs/m4ri/spkg-check +++ /dev/null @@ -1,28 +0,0 @@ -unset RM - -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting" - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -INCLUDES="-I$SAGE_LOCAL/include" -LIBDIRS="-L$SAGE_LOCAL/lib" - -CFLAGS="$CFLAGS $INCLUDES $LIBDIRS -g" -CXXFLAGS="$CXXFLAGS $INCLUDES $LIBDIRS -g" - -export CFLAGS -export CXXFLAGS - -cd src - -echo "Testing the M4RI library" - -$MAKE check - -if [ $? -ne 0 ]; then - echo "Error testing M4RI" - exit 1 -fi - diff --git a/build/pkgs/m4ri/spkg-check.in b/build/pkgs/m4ri/spkg-check.in new file mode 100644 index 00000000000..a7eb30457f0 --- /dev/null +++ b/build/pkgs/m4ri/spkg-check.in @@ -0,0 +1,12 @@ +INCLUDES="-I$SAGE_LOCAL/include" +LIBDIRS="-L$SAGE_LOCAL/lib" + +CFLAGS="$CFLAGS $INCLUDES $LIBDIRS -g" +CXXFLAGS="$CXXFLAGS $INCLUDES $LIBDIRS -g" + +export CFLAGS +export CXXFLAGS + +cd src + +$MAKE check diff --git a/build/pkgs/m4ri/spkg-install b/build/pkgs/m4ri/spkg-install.in similarity index 100% rename from build/pkgs/m4ri/spkg-install rename to build/pkgs/m4ri/spkg-install.in diff --git a/build/pkgs/m4rie/SPKG.rst b/build/pkgs/m4rie/SPKG.rst new file mode 100644 index 00000000000..34f5b73f732 --- /dev/null +++ b/build/pkgs/m4rie/SPKG.rst @@ -0,0 +1,27 @@ +M4RIE +===== + +Description +----------- + +M4RIE: Library for matrix multiplication, reduction and inversion over +GF(2^k) for 2 <= k <= 10. + +License +------- + +- GNU General Public License Version 2 or later (see src/COPYING) + + +Upstream Contact +---------------- + +- Authors: Martin Albrecht +- Email: +- Website: http://m4ri.sagemath.org + +Dependencies +------------ + +- M4RI +- Givaro diff --git a/build/pkgs/m4rie/SPKG.txt b/build/pkgs/m4rie/SPKG.txt deleted file mode 100644 index 0f5d1a49f06..00000000000 --- a/build/pkgs/m4rie/SPKG.txt +++ /dev/null @@ -1,57 +0,0 @@ -= M4RIE = - -== Description == - -M4RIE: Library for matrix multiplication, reduction and inversion over -GF(2^k) for 2 <= k <= 10. - -== License == - - * GNU General Public License Version 2 or later (see src/COPYING) - -== Upstream Contact == - - * Authors: Martin Albrecht - * Email: - * Website: http://m4ri.sagemath.org - -== Dependencies == - - * M4RI - * Givaro - -== Releases/Changelog == - -=== libm4rie-20130416 (Martin Albrecht, 22 March 2013) === - * #14336: new upstream release - -=== libm4rie-20120613 (Martin Albrecht, 7 June 2012) === - * #12841: new upstream release - * delete old headers before installing new ones - -=== libm4rie-20111004.p3 (Jeroen Demeyer, 10 April 2012) === - * Trac #12821: don't quote $CC when running testcc.sh - * Use testcflags.sh to add -fPIC -Wall -pedantic -g - * Fix strings to test output of testcc.sh against - -=== libm4rie-20111004.p2 (Jeroen Demeyer, 16 February 2012) === - * Rename source directory "m4rie" to "src". - * Trac #12501: touch src/src/config.h.in to prevent autoheader from - running. - * Trac #12311: remove testcc.sh script, instead use the one from - $SAGE_ROOT/spkg/bin (in the $PATH). - -=== libm4rie-20111004.p1 (Martin Albrecht, January 2nd, 2012) === - * #12245: proper dependencies - -=== libm4rie-20111004.p0 (Simon King, December 10th, 2011) === - * #12131: Use --libdir, to make the package work on openSUSE. - -=== libm4rie-20111004 (Martin Albrecht, October 4th, 2011) === - * new upstream release - -=== libm4rie-20110821 (Martin Albrecht, August 21st, 2011) === - * new upstream release in preparation for Sage inclusion - -=== libm4rie-20110715 (Martin Albrecht, July 15th, 2011) === - * split form libm4ri diff --git a/build/pkgs/m4rie/distros/gentoo.txt b/build/pkgs/m4rie/distros/gentoo.txt new file mode 100644 index 00000000000..2030b99f9e7 --- /dev/null +++ b/build/pkgs/m4rie/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/m4rie diff --git a/build/pkgs/m4rie/patches/use_AM_LDLFAGS.patch b/build/pkgs/m4rie/patches/use_AM_LDLFAGS.patch new file mode 100644 index 00000000000..8dc3959ffc2 --- /dev/null +++ b/build/pkgs/m4rie/patches/use_AM_LDLFAGS.patch @@ -0,0 +1,32 @@ +https://bitbucket.org/malb/m4rie/pull-requests/4/do-not-interfere-with-ldflags-while + +--- a/tests/Makefile.am 2020-04-27 03:46:40.283668933 +0000 ++++ b/tests/Makefile.am 2020-04-27 03:46:53.955804875 +0000 +@@ -2,7 +2,7 @@ + + AM_CFLAGS = ${SIMD_FLAGS} ${OPENMP_CFLAGS} ${DEBUG_FLAGS} ${M4RIE_M4RI_CFLAGS} ${M4RI_CFLAGS} -I${top_srcdir} + LDADD = ${top_builddir}/libm4rie.la -lm4ri -lm +-LDFLAGS = ${M4RIE_M4RI_LDFLAGS} -no-install ++AM_LDFLAGS = ${M4RIE_M4RI_LDFLAGS} -no-install + + EXTRA_DIST = testing.h + +--- a/tests/Makefile.in ++++ b/tests/Makefile.in +@@ -441,7 +441,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ + INSTALL_SCRIPT = @INSTALL_SCRIPT@ + INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ + LD = @LD@ +-LDFLAGS = ${M4RIE_M4RI_LDFLAGS} -no-install ++LDFLAGS = @LDFLAGS@ + LIBOBJS = @LIBOBJS@ + LIBS = @LIBS@ + LIBTOOL = @LIBTOOL@ +@@ -535,6 +535,7 @@ top_srcdir = @top_srcdir@ + AUTOMAKE_OPTIONS = foreign subdir-objects + AM_CFLAGS = ${SIMD_FLAGS} ${OPENMP_CFLAGS} ${DEBUG_FLAGS} ${M4RIE_M4RI_CFLAGS} ${M4RI_CFLAGS} -I${top_srcdir} + LDADD = ${top_builddir}/libm4rie.la -lm4ri -lm ++AM_LDFLAGS = ${M4RIE_M4RI_LDFLAGS} -no-install + EXTRA_DIST = testing.h + all: all-am + diff --git a/build/pkgs/m4rie/spkg-check b/build/pkgs/m4rie/spkg-check deleted file mode 100644 index b927913aa59..00000000000 --- a/build/pkgs/m4rie/spkg-check +++ /dev/null @@ -1,22 +0,0 @@ -unset RM - -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -INCLUDES="-I$SAGE_LOCAL/include" -LIBDIRS="-L$SAGE_LOCAL/lib" - -CFLAGS="$CFLAGS $INCLUDES $LIBDIRS -g" -CXXFLAGS="$CXXFLAGS $INCLUDES $LIBDIRS -g" - -export CFLAGS -export CXXFLAGS - -cd src - -echo "Testing the M4RIE library" - -$MAKE check diff --git a/build/pkgs/m4rie/spkg-check.in b/build/pkgs/m4rie/spkg-check.in new file mode 100644 index 00000000000..90f5da1cf5b --- /dev/null +++ b/build/pkgs/m4rie/spkg-check.in @@ -0,0 +1,14 @@ +INCLUDES="-I$SAGE_LOCAL/include" +LIBDIRS="-L$SAGE_LOCAL/lib" + +CFLAGS="$CFLAGS $INCLUDES $LIBDIRS -g" +CXXFLAGS="$CXXFLAGS $INCLUDES $LIBDIRS -g" + +export CFLAGS +export CXXFLAGS + +cd src + +echo "Testing the M4RIE library" + +$MAKE check diff --git a/build/pkgs/m4rie/spkg-install b/build/pkgs/m4rie/spkg-install.in similarity index 100% rename from build/pkgs/m4rie/spkg-install rename to build/pkgs/m4rie/spkg-install.in diff --git a/build/pkgs/markupsafe/SPKG.rst b/build/pkgs/markupsafe/SPKG.rst new file mode 100644 index 00000000000..b2f117dae5b --- /dev/null +++ b/build/pkgs/markupsafe/SPKG.rst @@ -0,0 +1,23 @@ +markupsafe +========== + +Description +----------- + +Implements a XML/HTML/XHTML Markup safe string for Python + +License +------- + +Simplified BSD + + +Upstream Contact +---------------- + +Home page: http://github.com/mitsuhiko/markupsafe + +Dependencies +------------ + +Python, setuptools diff --git a/build/pkgs/markupsafe/SPKG.txt b/build/pkgs/markupsafe/SPKG.txt deleted file mode 100644 index e0b5a22e19f..00000000000 --- a/build/pkgs/markupsafe/SPKG.txt +++ /dev/null @@ -1,19 +0,0 @@ -= markupsafe = - -== Description == - -Implements a XML/HTML/XHTML Markup safe string for Python - -== License == - -Simplified BSD - -== Upstream Contact == - -Home page: http://github.com/mitsuhiko/markupsafe - -== Dependencies == - -Python, setuptools - - diff --git a/build/pkgs/markupsafe/dependencies b/build/pkgs/markupsafe/dependencies index 2573414ce8a..15df0c4d6d8 100644 --- a/build/pkgs/markupsafe/dependencies +++ b/build/pkgs/markupsafe/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/markupsafe/spkg-install b/build/pkgs/markupsafe/spkg-install.in similarity index 100% rename from build/pkgs/markupsafe/spkg-install rename to build/pkgs/markupsafe/spkg-install.in diff --git a/build/pkgs/mathjax/SPKG.rst b/build/pkgs/mathjax/SPKG.rst new file mode 100644 index 00000000000..94b165256d2 --- /dev/null +++ b/build/pkgs/mathjax/SPKG.rst @@ -0,0 +1,37 @@ +MathJax +======= + +Description +----------- + +MathJax is a JavaScript library for displaying mathematical formulas. +Mathjax is used by both sagenb and ipython notebooks. + +License +------- + +Apache License, version 2.0 + + +Upstream Contact +---------------- + +Home page: http://www.mathjax.org/ + +Dependencies +------------ + +None. + + +Special Update/Build Instructions +--------------------------------- + +None. + +Patches +------- + +- nopng_config.patch: prevent font warning messages since png files are + removed. See section "Trimming II -- not strictly necessary" of + https://github.com/mathjax/MathJax-docs/wiki/Guide%3A-reducing-size-of-a-mathjax-installation diff --git a/build/pkgs/mathjax/SPKG.txt b/build/pkgs/mathjax/SPKG.txt deleted file mode 100644 index ae499963726..00000000000 --- a/build/pkgs/mathjax/SPKG.txt +++ /dev/null @@ -1,29 +0,0 @@ -= MathJax = - -== Description == - -MathJax is a JavaScript library for displaying mathematical formulas. -Mathjax is used by both sagenb and ipython notebooks. - -== License == - -Apache License, version 2.0 - -== Upstream Contact == - -Home page: http://www.mathjax.org/ - -== Dependencies == - -None. - -== Special Update/Build Instructions == - -None. - -== Patches == - -* nopng_config.patch: prevent font warning messages since png files are - removed. See section "Trimming II -- not strictly necessary" of - https://github.com/mathjax/MathJax-docs/wiki/Guide%3A-reducing-size-of-a-mathjax-installation - diff --git a/build/pkgs/mathjax/spkg-install b/build/pkgs/mathjax/spkg-install.in similarity index 100% rename from build/pkgs/mathjax/spkg-install rename to build/pkgs/mathjax/spkg-install.in diff --git a/build/pkgs/matplotlib/SPKG.rst b/build/pkgs/matplotlib/SPKG.rst new file mode 100644 index 00000000000..d559f2ae488 --- /dev/null +++ b/build/pkgs/matplotlib/SPKG.rst @@ -0,0 +1,60 @@ +matplotlib +========== + +Description +----------- + +From the Matplotlib website: matplotlib is a python 2D plotting library +which produces publication quality figures in a variety of hardcopy +formats and interactive environments across platforms. matplotlib can be +used in python scripts, the python and ipython shell (ala matlab or +mathematica), web application servers, and six graphical user interface +toolkits. + +License +------- + +The Matplotlib license - see +http://matplotlib.sourceforge.net/users/license.html: Matplotlib only +uses BSD compatible code, and its license is based on the PSF license. +See the Open Source Initiative licenses page for details on individual +licenses. Non-BSD compatible licenses (eg LGPL) are acceptable in +matplotlib Toolkits. For a discussion of the motivations behind the +licencing choice, see Licenses. + + +Upstream Contact +---------------- + +The matplotlib mailing lists: see +http://sourceforge.net/projects/matplotlib + +Dependencies +------------ + +- python +- numpy +- setuptools (>= 0.7) +- freetype +- patch (used in spkg-install) +- dateutil +- pyparsing +- tornado +- kiwisolver + + +Build Instructions/Changes +-------------------------- + +- NOTE: To drastically cut down on spkg size, we delete the internal + testing images. To do this, we repackage the tarball by removing + the contents of ``lib/matplotlib/tests/baseline_images/*``, this is + done by the ``spkg-src`` script. + +- ``setup.py.patch``: disable loading of Tests. Otherwise, ``setup.py`` + raises an error because it can't find the deleted files + from ``src/lib/matplotlib/tests/baseline_images/*`` + +- NOTE: as of matplotlib-1.0.0 and Sage 4.6, Sage does not use + $HOME/.matplotlib by default. Instead, it sets MPLCONFIGDIR to + a subdirectory in $DOT_SAGE, see src/bin/sage-env diff --git a/build/pkgs/matplotlib/SPKG.txt b/build/pkgs/matplotlib/SPKG.txt deleted file mode 100644 index 5b8e0e6ea63..00000000000 --- a/build/pkgs/matplotlib/SPKG.txt +++ /dev/null @@ -1,51 +0,0 @@ -= matplotlib = - -== Description == - -From the Matplotlib website: matplotlib is a python 2D plotting -library which produces publication quality figures in a variety of -hardcopy formats and interactive environments across -platforms. matplotlib can be used in python scripts, the python and -ipython shell (ala matlab or mathematica), web application servers, -and six graphical user interface toolkits. - -== License == - -The Matplotlib license - see -http://matplotlib.sourceforge.net/users/license.html: Matplotlib only -uses BSD compatible code, and its license is based on the PSF -license. See the Open Source Initiative licenses page for details on -individual licenses. Non-BSD compatible licenses (eg LGPL) are -acceptable in matplotlib Toolkits. For a discussion of the motivations -behind the licencing choice, see Licenses. - -== Upstream Contact == - -The matplotlib mailing lists: see http://sourceforge.net/projects/matplotlib - -== Dependencies == - - * python - * numpy - * setuptools (>= 0.7) - * freetype - * patch (used in spkg-install) - * dateutil - * pyparsing - * tornado - * kiwisolver - -== Build Instructions/Changes == - - * NOTE: To drastically cut down on spkg size, we delete the internal - testing images. To do this, we repackage the tarball by removing - the contents of lib/matplotlib/tests/baseline_images/*, this is - done by the spkg-src script. - - * setup.py.patch: disable loading of Tests. Otherwise, setup.py - raises an error because it can't find the deleted files - from src/lib/matplotlib/tests/baseline_images/* - - * NOTE: as of matplotlib-1.0.0 and Sage 4.6, Sage does not use - $HOME/.matplotlib by default. Instead, it sets MPLCONFIGDIR to - a subdirectory in $DOT_SAGE, see src/bin/sage-env diff --git a/build/pkgs/matplotlib/checksums.ini b/build/pkgs/matplotlib/checksums.ini index 88a657548b4..eb45b5e3d9c 100644 --- a/build/pkgs/matplotlib/checksums.ini +++ b/build/pkgs/matplotlib/checksums.ini @@ -1,4 +1,5 @@ -tarball=matplotlib-VERSION.tar.bz2 -sha1=1e650be3851dd5d57e1c48fe31dd91566ea45cdd -md5=111b2951ce5803ed4805dec4edefcecd -cksum=442239497 +tarball=matplotlib-VERSION.tar.gz +sha1=e407249b202d199c704f2bb40bd48776d6a6f633 +md5=b1de7185687c6f5c092689e3431a69b3 +cksum=1389017280 +upstream_url=https://pypi.io/packages/source/m/matplotlib/matplotlib-VERSION.tar.gz diff --git a/build/pkgs/matplotlib/dependencies b/build/pkgs/matplotlib/dependencies index cbe4dd2f17f..1b838fdc905 100644 --- a/build/pkgs/matplotlib/dependencies +++ b/build/pkgs/matplotlib/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) numpy freetype libpng dateutil pyparsing tornado six cycler | setuptools pip pytz functools32 backports_functools_lru_cache subprocess32 kiwisolver +$(PYTHON) numpy freetype pillow dateutil pyparsing tornado six cycler | $(PYTHON_TOOLCHAIN) pytz kiwisolver ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/matplotlib/make-setup-config.py b/build/pkgs/matplotlib/make-setup-config.py index caca02b71cd..208dd476c8f 100644 --- a/build/pkgs/matplotlib/make-setup-config.py +++ b/build/pkgs/matplotlib/make-setup-config.py @@ -1,15 +1,13 @@ -try: - from configparser import SafeConfigParser # Python 3 -except ImportError: - from ConfigParser import SafeConfigParser # Python 2 +from configparser import ConfigParser import os -config = SafeConfigParser() +config = ConfigParser() config.add_section('directories') config.set('directories', 'basedirlist', os.environ['SAGE_LOCAL']) - +config.add_section('libs') +config.set('libs', 'system_freetype', 'True') ##################################################################### # Sage code -- all this code just sets the graphical_backend variable. diff --git a/build/pkgs/matplotlib/package-version.txt b/build/pkgs/matplotlib/package-version.txt index 0d58460e368..08be6eec65b 100644 --- a/build/pkgs/matplotlib/package-version.txt +++ b/build/pkgs/matplotlib/package-version.txt @@ -1 +1 @@ -2.2.4.p0 +3.3.0.p0 diff --git a/build/pkgs/matplotlib/patches/fix-path-of-degenerate-polygon.patch b/build/pkgs/matplotlib/patches/fix-path-of-degenerate-polygon.patch new file mode 100644 index 00000000000..bedd4a48f08 --- /dev/null +++ b/build/pkgs/matplotlib/patches/fix-path-of-degenerate-polygon.patch @@ -0,0 +1,67 @@ +From 07847dd27dd16296999161a616ea27445c5a49db Mon Sep 17 00:00:00 2001 +From: Elliott Sales de Andrade +Date: Tue, 21 Jul 2020 20:11:50 -0400 +Subject: [PATCH] Backport PR #17982: BF: for degenerate polygons, add + CLOSEPOLY vertex + +--- + lib/matplotlib/patches.py | 18 ++++++++++++++++-- + lib/matplotlib/tests/test_patches.py | 7 +++++++ + 2 files changed, 23 insertions(+), 2 deletions(-) + +diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py +index 5811cba39ad..fdd06f004f7 100644 +--- a/lib/matplotlib/patches.py ++++ b/lib/matplotlib/patches.py +@@ -1063,13 +1063,27 @@ def set_xy(self, xy): + ---------- + xy : (N, 2) array-like + The coordinates of the vertices. ++ ++ Notes ++ ----- ++ Unlike `~.path.Path`, we do not ignore the last input vertex. If the ++ polygon is meant to be closed, and the last point of the polygon is not ++ equal to the first, we assume that the user has not explicitly passed a ++ ``CLOSEPOLY`` vertex, and add it ourselves. + """ + xy = np.asarray(xy) ++ nverts, _ = xy.shape + if self._closed: +- if len(xy) and (xy[0] != xy[-1]).any(): ++ # if the first and last vertex are the "same", then we assume that ++ # the user explicitly passed the CLOSEPOLY vertex. Otherwise, we ++ # have to append one since the last vertex will be "ignored" by ++ # Path ++ if nverts == 1 or nverts > 1 and (xy[0] != xy[-1]).any(): + xy = np.concatenate([xy, [xy[0]]]) + else: +- if len(xy) > 2 and (xy[0] == xy[-1]).all(): ++ # if we aren't closed, and the last vertex matches the first, then ++ # we assume we have an unecessary CLOSEPOLY vertex and remove it ++ if nverts > 2 and (xy[0] == xy[-1]).all(): + xy = xy[:-1] + self._path = Path(xy, closed=self._closed) + self.stale = True +diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py +index 475300b7c2d..3b9d1e0adb3 100644 +--- a/lib/matplotlib/tests/test_patches.py ++++ b/lib/matplotlib/tests/test_patches.py +@@ -7,6 +7,7 @@ + + from matplotlib.patches import Polygon, Rectangle, FancyArrowPatch + from matplotlib.testing.decorators import image_comparison, check_figures_equal ++from matplotlib.transforms import Bbox + import matplotlib.pyplot as plt + from matplotlib import ( + collections as mcollections, colors as mcolors, patches as mpatches, +@@ -556,3 +557,9 @@ def test_rotated_arcs(): + ax.axvline(0, color="k") + ax.set_axis_off() + ax.set_aspect("equal") ++ ++ ++def test_degenerate_polygon(): ++ point = [0, 0] ++ correct_extents = Bbox([point, point]).extents ++ assert np.all(Polygon([point]).get_extents().extents == correct_extents) diff --git a/build/pkgs/matplotlib/patches/install-jquery-ui.patch b/build/pkgs/matplotlib/patches/install-jquery-ui.patch deleted file mode 100644 index ec134594955..00000000000 --- a/build/pkgs/matplotlib/patches/install-jquery-ui.patch +++ /dev/null @@ -1,22 +0,0 @@ -See https://github.com/matplotlib/matplotlib/pull/14587 - -commit 564edad96232e028294d7421b41955029e16816f -Author: Jeroen Demeyer -Date: Thu Jun 20 13:45:09 2019 +0200 - - Install verdored copy of jquery-ui - -diff --git a/setupext.py b/setupext.py -index c75725b..3536cf6 100644 ---- a/setupext.py -+++ b/setupext.py -@@ -871,6 +871,9 @@ class Matplotlib(SetupPackage): - 'backends/web_backend/jquery/js/*.min.js', - 'backends/web_backend/jquery/css/themes/base/*.min.css', - 'backends/web_backend/jquery/css/themes/base/images/*', -+ 'backends/web_backend/jquery-ui-*/*', -+ 'backends/web_backend/jquery-ui-*/*/*', -+ 'backends/web_backend/jquery-ui-*/*/*/*', - 'backends/web_backend/css/*.*', - 'backends/Matplotlib.nib/*', - 'mpl-data/stylelib/*.mplstyle', diff --git a/build/pkgs/matplotlib/patches/python38.patch b/build/pkgs/matplotlib/patches/python38.patch deleted file mode 100644 index 7fdd4262fe4..00000000000 --- a/build/pkgs/matplotlib/patches/python38.patch +++ /dev/null @@ -1,40 +0,0 @@ -See https://github.com/matplotlib/matplotlib/pull/14582 - -commit 4b957fde34c869e40d010d2ec871d3d219e59ee2 -Author: Antony Lee -Date: Fri Jul 20 19:04:25 2018 +0200 - - Backport PR #11722: Remove unnecessary hacks from setup.py. - - - There are no symlinks in the mpl source tree anymore. - - MANIFEST was removed from the root directory in 365a0a2f (11 years - ago). - -diff --git a/setup.py b/setup.py -index 41037e9..b045f89 100644 ---- a/setup.py -+++ b/setup.py -@@ -11,23 +11,6 @@ from setuptools.command.build_ext import build_ext as BuildExtCommand - - import sys - --# distutils is breaking our sdists for files in symlinked dirs. --# distutils will copy if os.link is not available, so this is a hack --# to force copying --import os --try: -- del os.link --except AttributeError: -- pass -- --# This 'if' statement is needed to prevent spawning infinite processes --# on Windows --if __name__ == '__main__': -- # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly -- # update it when the contents of directories change. -- if os.path.exists('MANIFEST'): -- os.remove('MANIFEST') -- - from io import BytesIO - import os - from string import Template diff --git a/build/pkgs/matplotlib/patches/setup.py.patch b/build/pkgs/matplotlib/patches/setup.py.patch deleted file mode 100644 index dc1807551dc..00000000000 --- a/build/pkgs/matplotlib/patches/setup.py.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- a/setup.py -+++ b/setup.py -@@ -85,8 +85,8 @@ mpl_packages = [ - 'Optional subpackages', - setupext.SampleData(), - setupext.Toolkits(), -- setupext.Tests(), -- setupext.Toolkits_Tests(), -+ # setupext.Tests(), -+ # setupext.Toolkits_Tests(), - 'Optional backend extensions', - # These backends are listed in order of preference, the first - # being the most preferred. The first one that looks like it will diff --git a/build/pkgs/matplotlib/spkg-install b/build/pkgs/matplotlib/spkg-install deleted file mode 100644 index 1b25789ffc6..00000000000 --- a/build/pkgs/matplotlib/spkg-install +++ /dev/null @@ -1,18 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -# Write a configuration file to src/setup.cfg -sage-system-python make-setup-config.py - -cd src - -# Finally install -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing matplotlib package." - exit 1 -fi diff --git a/build/pkgs/matplotlib/spkg-install.in b/build/pkgs/matplotlib/spkg-install.in new file mode 100644 index 00000000000..a427dadccd5 --- /dev/null +++ b/build/pkgs/matplotlib/spkg-install.in @@ -0,0 +1,7 @@ +# Write a configuration file to src/setup.cfg +sage-python23 make-setup-config.py + +cd src + +# Finally install +sdh_pip_install . diff --git a/build/pkgs/maxima/SPKG.rst b/build/pkgs/maxima/SPKG.rst new file mode 100644 index 00000000000..4144178786f --- /dev/null +++ b/build/pkgs/maxima/SPKG.rst @@ -0,0 +1,76 @@ +Maxima +====== + +Description +----------- + +Maxima is a system for the manipulation of symbolic and numerical +expressions, including differentiation, integration, Taylor series, +Laplace transforms, ordinary differential equations, systems of linear +equations, polynomials, and sets, lists, vectors, matrices, and tensors. +Maxima yields high precision numeric results by using exact fractions, +arbitrary precision integers, and variable precision floating point +numbers. Maxima can plot functions and data in two and three dimensions. + +For more information, see the Maxima web site + +http://maxima.sourceforge.net + +License +------- + +Maxima is distributed under the GNU General Public License, with some +export restrictions from the U.S. Department of Energy. See the file +COPYING. + + +Upstream Contact +---------------- + +- The Maxima mailing list - see + http://maxima.sourceforge.net/maximalist.html + +Dependencies +------------ + +- ECL (Embedded Common Lisp) + + +Special Update/Build Instructions +--------------------------------- + +1. Go to http://sourceforge.net/projects/maxima/files/Maxima-source/ + and download the source tarball maxima-x.y.z.tar.gz; place it in + the upstream/ directory. + +2. Update package-version.txt and run 'sage --package fix-checksum'. + +3. Make sure the patches still apply cleanly, and update them if + necessary. + +4. Test the resulting package. + +All patch files in the patches/ directory are applied. Descriptions of +these patches are either in the patch files themselves or below. + +- 0001-taylor2-Avoid-blowing-the-stack-when-diff-expand-isn.patch: + Fix for Maxima bug #2520 (abs_integrate fails on abs(sin(x)) and + abs(cos(x))). Introduced in Trac #13364 (Upgrade Maxima to + 5.29.1). + +- build-fasl.patch: Build a fasl library for ecl in addition to an + executable program. Introduced in Trac #16178 (Build maxima fasl + without asdf). + +- infodir.patch: Correct the path to the Info directory. Introduced + in Trac #11348 (maxima test fails when install tree is moved). + +- matrixexp.patch: Fix matrixexp(matrix([%i*%pi])), which broke after + Maxima 5.29.1. Introduced in Trac #13973. + +- maxima.system.patch: Set ``c::*compile-in-constants*`` to t. + Introduced in Trac #11966 (OS X 10.7 Lion: Maxima fails to build). + +- undoing_true_false_printing_patch.patch: Revert an upstream change + causing '?' to be printed around some words. Introduced in Trac + #13364 (Upgrade Maxima to 5.29.1). diff --git a/build/pkgs/maxima/SPKG.txt b/build/pkgs/maxima/SPKG.txt deleted file mode 100644 index 9c8827a4dc0..00000000000 --- a/build/pkgs/maxima/SPKG.txt +++ /dev/null @@ -1,68 +0,0 @@ -= Maxima = - -== Description == - -Maxima is a system for the manipulation of symbolic and numerical -expressions, including differentiation, integration, Taylor series, -Laplace transforms, ordinary differential equations, systems of linear -equations, polynomials, and sets, lists, vectors, matrices, and -tensors. Maxima yields high precision numeric results by using exact -fractions, arbitrary precision integers, and variable precision -floating point numbers. Maxima can plot functions and data in two and -three dimensions. - -For more information, see the Maxima web site - -http://maxima.sourceforge.net - -== License == - -Maxima is distributed under the GNU General Public License, with some -export restrictions from the U.S. Department of Energy. See the file -COPYING. - -== Upstream Contact == - - * The Maxima mailing list - see http://maxima.sourceforge.net/maximalist.html - -== Dependencies == - - * ECL (Embedded Common Lisp) - -== Special Update/Build Instructions == - -1. Go to http://sourceforge.net/projects/maxima/files/Maxima-source/ - and download the source tarball maxima-x.y.z.tar.gz; place it in - the upstream/ directory. - -2. Update package-version.txt and run sage-fix-pkg-checksums. - -3. Make sure the patches still apply cleanly, and update them if - necessary. - -4. Test the resulting package. - -All patch files in the patches/ directory are applied. Descriptions -of these patches are either in the patch files themselves or below. - - * 0001-taylor2-Avoid-blowing-the-stack-when-diff-expand-isn.patch: - Fix for Maxima bug #2520 (abs_integrate fails on abs(sin(x)) and - abs(cos(x))). Introduced in Trac #13364 (Upgrade Maxima to - 5.29.1). - - * build-fasl.patch: Build a fasl library for ecl in addition to an - executable program. Introduced in Trac #16178 (Build maxima fasl - without asdf). - - * infodir.patch: Correct the path to the Info directory. Introduced - in Trac #11348 (maxima test fails when install tree is moved). - - * matrixexp.patch: Fix matrixexp(matrix([%i*%pi])), which broke after - Maxima 5.29.1. Introduced in Trac #13973. - - * maxima.system.patch: Set c::*compile-in-constants* to t. - Introduced in Trac #11966 (OS X 10.7 Lion: Maxima fails to build). - - * undoing_true_false_printing_patch.patch: Revert an upstream change - causing '?' to be printed around some words. Introduced in Trac - #13364 (Upgrade Maxima to 5.29.1). diff --git a/build/pkgs/maxima/patches/bugfix3629.patch b/build/pkgs/maxima/patches/bugfix3629.patch new file mode 100644 index 00000000000..196131410b0 --- /dev/null +++ b/build/pkgs/maxima/patches/bugfix3629.patch @@ -0,0 +1,19 @@ +commit 615b4bf8b13d55a576bc60ad04f7b17d75f49021 +Author: Yasuaki Honda +Date: Sun Apr 26 12:15:14 2020 +0900 + + Fix for Bug #3629, to compile with ECL 20.4.24 + +diff --git a/lisp-utils/defsystem.lisp b/lisp-utils/defsystem.lisp +index dda669d26..b8e96eebe 100644 +--- a/lisp-utils/defsystem.lisp ++++ b/lisp-utils/defsystem.lisp +@@ -4152,7 +4152,7 @@ the system definition, if provided." + #+:ecl + (progn + (ext:package-lock "CL" nil) +- (setf (symbol-function 'lisp:require) ++ (setf (symbol-function 'cl:require) + (symbol-function 'new-require)) + (ext:package-lock "CL" t)) + #+:lispworks diff --git a/build/pkgs/maxima/spkg-install b/build/pkgs/maxima/spkg-install deleted file mode 100644 index f2e16334e5e..00000000000 --- a/build/pkgs/maxima/spkg-install +++ /dev/null @@ -1,69 +0,0 @@ -# Sometimes, ECL gives interactive prompts when something goes wrong -# during the build. Avoid this by redirecting stdin from /dev/null. -# See http://trac.sagemath.org/sage_trac/ticket/11884#comment:34 -exec mcqd2.h -mv mcqd2.h mcqd.h - -case "$UNAME" in -"CYGWIN") - SO_NAME="cygmcqd.dll" - IMPLIB_NAME="libmcqd.dll.a" - MCQD_LDFLAGS="-Wl,--out-implib,$IMPLIB_NAME" - ;; -"Darwin") - SO_NAME="libmcqd.dylib" - ;; -*) - SO_NAME="libmcqd.so" - ;; -esac - -g++ -O3 -c mcqd.cpp -o mcqd.o -g++ -shared mcqd.o -o "$SO_NAME" $MCQD_LDFLAGS - -if [ "$UNAME" = "CYGWIN" ]; then - sdh_install "$SO_NAME" "$SAGE_LOCAL/bin/" - sdh_install "$IMPLIB_NAME" "$SAGE_LOCAL/lib/" -else - sdh_install "$SO_NAME" "$SAGE_LOCAL/lib/" -fi - -if [ "$UNAME" = "Darwin" ]; then - install_name_tool -id ${SAGE_LOCAL}/lib/$SO_NAME \ - "$SAGE_DESTDIR_LOCAL/lib/$SO_NAME" -fi - -sdh_install mcqd.h "$SAGE_LOCAL/include/" diff --git a/build/pkgs/mcqd/spkg-install.in b/build/pkgs/mcqd/spkg-install.in new file mode 100644 index 00000000000..38e13b69bfc --- /dev/null +++ b/build/pkgs/mcqd/spkg-install.in @@ -0,0 +1,34 @@ +cd src +grep -v "std..cout.*current max" mcqd.h > mcqd2.h +mv mcqd2.h mcqd.h + +case "$UNAME" in +"CYGWIN") + SO_NAME="cygmcqd.dll" + IMPLIB_NAME="libmcqd.dll.a" + MCQD_LDFLAGS="-Wl,--out-implib,$IMPLIB_NAME" + ;; +"Darwin") + SO_NAME="libmcqd.dylib" + ;; +*) + SO_NAME="libmcqd.so" + ;; +esac + +$CXX -fPIC -O3 -c mcqd.cpp -o mcqd.o +$CXX -shared mcqd.o -o "$SO_NAME" $MCQD_LDFLAGS + +if [ "$UNAME" = "CYGWIN" ]; then + sdh_install "$SO_NAME" "$SAGE_LOCAL/bin/" + sdh_install "$IMPLIB_NAME" "$SAGE_LOCAL/lib/" +else + sdh_install "$SO_NAME" "$SAGE_LOCAL/lib/" +fi + +if [ "$UNAME" = "Darwin" ]; then + install_name_tool -id ${SAGE_LOCAL}/lib/$SO_NAME \ + "$SAGE_DESTDIR_LOCAL/lib/$SO_NAME" +fi + +sdh_install mcqd.h "$SAGE_LOCAL/include/" diff --git a/build/pkgs/meataxe/SPKG.rst b/build/pkgs/meataxe/SPKG.rst new file mode 100644 index 00000000000..cc6c41d1f6d --- /dev/null +++ b/build/pkgs/meataxe/SPKG.rst @@ -0,0 +1,27 @@ +SharedMeatAxe +============= + +Description +----------- + +SharedMeatAxe 1.0 is an autotoolized shared library version of C MeatAxe +2.4.24, a set of programs for computing with modular representations. +The package comprises a shared library "libmtx", as well as several +executables. + +See http://users.minet.uni-jena.de/~king/SharedMeatAxe/ for the package +documentation. + +Licence +------- + +The Shared Meat-Axe 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. See the file COPYING. + + +Upstream contact +---------------- + +- Simon King diff --git a/build/pkgs/meataxe/SPKG.txt b/build/pkgs/meataxe/SPKG.txt deleted file mode 100644 index 0c073a400f7..00000000000 --- a/build/pkgs/meataxe/SPKG.txt +++ /dev/null @@ -1,22 +0,0 @@ -= SharedMeatAxe = - -== Description == - -SharedMeatAxe 1.0 is an autotoolized shared library version of C MeatAxe 2.4.24, -a set of programs for computing with modular representations. -The package comprises a shared library "libmtx", as well as several -executables. - -See http://users.minet.uni-jena.de/~king/SharedMeatAxe/ for the package -documentation. - -== Licence == - -The Shared Meat-Axe 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. See the file COPYING. - -== Upstream contact == - - * Simon King diff --git a/build/pkgs/meataxe/spkg-check b/build/pkgs/meataxe/spkg-check deleted file mode 100644 index 7c8993bac7a..00000000000 --- a/build/pkgs/meataxe/spkg-check +++ /dev/null @@ -1,8 +0,0 @@ -cd src -$MAKE check - -if [[ $? -ne 0 ]]; then - echo >&2 "Error running the SharedMeataxe test suite." - exit 1 -fi - diff --git a/build/pkgs/meataxe/spkg-check.in b/build/pkgs/meataxe/spkg-check.in new file mode 100644 index 00000000000..27cd9419538 --- /dev/null +++ b/build/pkgs/meataxe/spkg-check.in @@ -0,0 +1,2 @@ +cd src +$MAKE check diff --git a/build/pkgs/meataxe/spkg-install b/build/pkgs/meataxe/spkg-install.in similarity index 100% rename from build/pkgs/meataxe/spkg-install rename to build/pkgs/meataxe/spkg-install.in diff --git a/build/pkgs/meataxe/spkg-postinst b/build/pkgs/meataxe/spkg-postinst.in similarity index 100% rename from build/pkgs/meataxe/spkg-postinst rename to build/pkgs/meataxe/spkg-postinst.in diff --git a/build/pkgs/mistune/SPKG.rst b/build/pkgs/mistune/SPKG.rst new file mode 100644 index 00000000000..e540bd0915f --- /dev/null +++ b/build/pkgs/mistune/SPKG.rst @@ -0,0 +1,23 @@ +mistune +======= + +Description +----------- + +The fastest markdown parser in pure Python + +License +------- + +BSD License + + +Upstream Contact +---------------- + +Home Page: https://github.com/lepture/mistune + +Dependencies +------------ + +Python, Cython, Pip diff --git a/build/pkgs/mistune/SPKG.txt b/build/pkgs/mistune/SPKG.txt deleted file mode 100644 index bc0f9192cae..00000000000 --- a/build/pkgs/mistune/SPKG.txt +++ /dev/null @@ -1,19 +0,0 @@ -= mistune = - -== Description == - -The fastest markdown parser in pure Python - -== License == - -BSD License - -== Upstream Contact == - -Home Page: https://github.com/lepture/mistune - -== Dependencies == - -Python, Cython, Pip - - diff --git a/build/pkgs/mistune/dependencies b/build/pkgs/mistune/dependencies index b078da4ff6a..d3dac75e5f2 100644 --- a/build/pkgs/mistune/dependencies +++ b/build/pkgs/mistune/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) cython | setuptools pip +$(PYTHON) cython | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/mistune/spkg-install b/build/pkgs/mistune/spkg-install.in similarity index 100% rename from build/pkgs/mistune/spkg-install rename to build/pkgs/mistune/spkg-install.in diff --git a/build/pkgs/modular_decomposition/SPKG.rst b/build/pkgs/modular_decomposition/SPKG.rst new file mode 100644 index 00000000000..d61cbdd5b9d --- /dev/null +++ b/build/pkgs/modular_decomposition/SPKG.rst @@ -0,0 +1,33 @@ + +modular decomposition +===================== + +Description +----------- + +This is an implementation of a modular decomposition algorithm. + +http://www.liafa.jussieu.fr/~fm/ (in french) + +License +------- + +GPL + + +Upstream Contact +---------------- + +Fabien de Montgolfier + +http://www.liafa.jussieu.fr/~fm/ + +Dependencies +------------ + +None + +Patches +------- + +None diff --git a/build/pkgs/modular_decomposition/SPKG.txt b/build/pkgs/modular_decomposition/SPKG.txt deleted file mode 100644 index 929836f4336..00000000000 --- a/build/pkgs/modular_decomposition/SPKG.txt +++ /dev/null @@ -1,25 +0,0 @@ -= modular decomposition = - -== Description == - -This is an implementation of a modular decomposition algorithm. - -http://www.liafa.jussieu.fr/~fm/ (in french) - -== License == - -GPL - -== Upstream Contact == - -Fabien de Montgolfier - -http://www.liafa.jussieu.fr/~fm/ - -== Dependencies == - -None - -== Patches == - -None diff --git a/build/pkgs/modular_decomposition/spkg-install b/build/pkgs/modular_decomposition/spkg-install deleted file mode 100644 index ab12192a3c5..00000000000 --- a/build/pkgs/modular_decomposition/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd "src" - -gcc -o "$SAGE_LOCAL/lib/libmodulardecomposition.so" dm.c random.c -fPIC --shared && -mv dm_english.h "$SAGE_LOCAL/include/modular_decomposition.h" - -if [ $? -ne 0 ]; then - echo "An error occurred whilst building modular_decomposition" - exit 1 -fi diff --git a/build/pkgs/modular_decomposition/spkg-install.in b/build/pkgs/modular_decomposition/spkg-install.in new file mode 100644 index 00000000000..4ed957d9061 --- /dev/null +++ b/build/pkgs/modular_decomposition/spkg-install.in @@ -0,0 +1,9 @@ +cd src + +gcc -o "$SAGE_LOCAL/lib/libmodulardecomposition.so" dm.c random.c -fPIC --shared && +mv dm_english.h "$SAGE_LOCAL/include/modular_decomposition.h" + +if [ $? -ne 0 ]; then + echo "An error occurred whilst building modular_decomposition" + exit 1 +fi diff --git a/build/pkgs/mpc/SPKG.rst b/build/pkgs/mpc/SPKG.rst new file mode 100644 index 00000000000..78039c62d3e --- /dev/null +++ b/build/pkgs/mpc/SPKG.rst @@ -0,0 +1,43 @@ +MPC +=== + +Description +----------- + +From http://www.multiprecision.org/mpc: GNU MPC is a C library for the +arithmetic of complex numbers with arbitrarily high precision and +correct rounding of the result. It extends the principles of the +IEEE-754 standard for fixed precision real floating point numbers to +complex numbers, providing well-defined semantics for every operation. +At the same time, speed of operation at high precision is a major design +goal. + +License +------- + +LGPLv3+ for the code and GFDLv1.3+ (with no invariant sections) for the +documentation. + + +Upstream Contact +---------------- + +The MPC website is located at http://www.multiprecision.org/mpc . + +The MPC team can be contacted via the MPC mailing list: + + mpc-discuss@lists.gforge.inria.fr + +Dependencies +------------ + +- MPIR +- MPFR + + +Special Update/Build Instructions +--------------------------------- + +- mpc_mul_faster.patch: Patch from Paul Zimmermann to speed up MPC + multiplication (for small precisions) by reducing overhead in MPFR + operations. diff --git a/build/pkgs/mpc/SPKG.txt b/build/pkgs/mpc/SPKG.txt deleted file mode 100644 index 13dcd59e94c..00000000000 --- a/build/pkgs/mpc/SPKG.txt +++ /dev/null @@ -1,34 +0,0 @@ -= MPC = - -== Description == - -From http://www.multiprecision.org/mpc: -GNU MPC is a C library for the arithmetic of complex numbers with -arbitrarily high precision and correct rounding of the result. It -extends the principles of the IEEE-754 standard for fixed precision real -floating point numbers to complex numbers, providing well-defined -semantics for every operation. At the same time, speed of operation at -high precision is a major design goal. - -== License == - -LGPLv3+ for the code and GFDLv1.3+ (with no invariant sections) -for the documentation. - -== Upstream Contact == - -The MPC website is located at http://www.multiprecision.org/mpc . - -The MPC team can be contact via the MPC mailing list: - mpc-discuss@lists.gforge.inria.fr - -== Dependencies == - - * MPIR - * MPFR - -== Special Update/Build Instructions == - -* mpc_mul_faster.patch: Patch from Paul Zimmermann to speed up MPC - multiplication (for small precisions) by reducing overhead in MPFR - operations. diff --git a/build/pkgs/mpc/distros/cygwin.txt b/build/pkgs/mpc/distros/cygwin.txt new file mode 100644 index 00000000000..279a55fdb8b --- /dev/null +++ b/build/pkgs/mpc/distros/cygwin.txt @@ -0,0 +1 @@ +libmpc-devel diff --git a/build/pkgs/mpc/distros/fedora.txt b/build/pkgs/mpc/distros/fedora.txt index 279a55fdb8b..491a280bb72 100644 --- a/build/pkgs/mpc/distros/fedora.txt +++ b/build/pkgs/mpc/distros/fedora.txt @@ -1 +1 @@ -libmpc-devel +libmpc libmpc-devel diff --git a/build/pkgs/mpc/distros/gentoo.txt b/build/pkgs/mpc/distros/gentoo.txt new file mode 100644 index 00000000000..340ece6273d --- /dev/null +++ b/build/pkgs/mpc/distros/gentoo.txt @@ -0,0 +1 @@ +dev-libs/mpc diff --git a/build/pkgs/mpc/distros/homebrew.txt b/build/pkgs/mpc/distros/homebrew.txt new file mode 100644 index 00000000000..098b049316b --- /dev/null +++ b/build/pkgs/mpc/distros/homebrew.txt @@ -0,0 +1 @@ +libmpc diff --git a/build/pkgs/mpc/spkg-check b/build/pkgs/mpc/spkg-check deleted file mode 100644 index eac95da0bee..00000000000 --- a/build/pkgs/mpc/spkg-check +++ /dev/null @@ -1,13 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error checking MPC." - exit 1 -fi diff --git a/build/pkgs/mpc/spkg-check.in b/build/pkgs/mpc/spkg-check.in new file mode 100644 index 00000000000..917c8eb7209 --- /dev/null +++ b/build/pkgs/mpc/spkg-check.in @@ -0,0 +1,3 @@ +cd src + +$MAKE check diff --git a/build/pkgs/mpc/spkg-configure.m4 b/build/pkgs/mpc/spkg-configure.m4 index 9ec14a7c980..32aca4ada1b 100644 --- a/build/pkgs/mpc/spkg-configure.m4 +++ b/build/pkgs/mpc/spkg-configure.m4 @@ -8,7 +8,7 @@ SAGE_SPKG_CONFIGURE([mpc], [ AC_MSG_RESULT([no]) AC_CHECK_HEADER(mpc.h, [], [sage_spkg_install_mpc=yes]) dnl mpc_cmp_abs appeared in MPC 1.1.0 - AC_SEARCH_LIBS([mpc_cmp_abs], [mpc], [break], [sage_spkg_install_mpc=yes]) + AC_SEARCH_LIBS([mpc_cmp_abs], [mpc], [], [sage_spkg_install_mpc=yes]) fi ], [], [], [ if test x$sage_spkg_install_mpc = xyes; then diff --git a/build/pkgs/mpc/spkg-install b/build/pkgs/mpc/spkg-install.in similarity index 100% rename from build/pkgs/mpc/spkg-install rename to build/pkgs/mpc/spkg-install.in diff --git a/build/pkgs/mpfi/SPKG.rst b/build/pkgs/mpfi/SPKG.rst new file mode 100644 index 00000000000..4e8897812ff --- /dev/null +++ b/build/pkgs/mpfi/SPKG.rst @@ -0,0 +1,43 @@ +MPFI +==== + +Description +----------- + +MPFI is a library for interval arithmetic, which is built upon the MPFR +multiple precision floating-point arithmetic. + +MPFI is intended to be a portable library written in C for arbitrary +precision interval arithmetic with intervals represented using MPFR +reliable floating-point numbers. It is based on the GNU MP library and +on the MPFR library. The purpose of an arbitrary precision interval +arithmetic is on the one hand to get "guaranteed" results, thanks to +interval computation, and on the other hand to obtain accurate results, +thanks to multiple precision arithmetic. The MPFI library is built upon +MPFR in order to benefit from the correct rounding provided, for each +operation or function, by MPFR. Further advantages of using MPFR are its +portability and compliance with the IEEE 754 standard for floating-point +arithmetic. + +License +------- + +This version of MPFI is released under the GNU Lesser General Public +License. It is permitted to link MPFI to non-free programs, as long as +when distributing them the MPFI source code and a means to re-link with +a modified MPFI is provided. + + +Upstream Contact +---------------- + +The MPFI website is located at http://mpfi.gforge.inria.fr/ + +The MPFI team can be contacted via the MPFI mailing list: +mpfi-users@lists.gforge.inria.fr + +Dependencies +------------ + +- GMP +- MPFR diff --git a/build/pkgs/mpfi/SPKG.txt b/build/pkgs/mpfi/SPKG.txt deleted file mode 100644 index d360a94c372..00000000000 --- a/build/pkgs/mpfi/SPKG.txt +++ /dev/null @@ -1,167 +0,0 @@ -= MPFI = - -== Description == - -MPFI is a library for interval arithmetic, which is built upon the MPFR -multiple precision floating-point arithmetic. - -MPFI is intended to be a portable library written in C for arbitrary -precision interval arithmetic with intervals represented using MPFR -reliable floating-point numbers. It is based on the GNU MP library and -on the MPFR library. The purpose of an arbitrary precision interval -arithmetic is on the one hand to get "guaranteed" results, thanks to -interval computation, and on the other hand to obtain accurate results, -thanks to multiple precision arithmetic. The MPFI library is built upon -MPFR in order to benefit from the correct rounding provided, for each -operation or function, by MPFR. Further advantages of using MPFR are -its portability and compliance with the IEEE 754 standard for -floating-point arithmetic. - -== License == - -This version of MPFI is released under the GNU Lesser General Public -License. It is permitted to link MPFI to non-free programs, as long as -when distributing them the MPFI source code and a means to re-link with -a modified MPFI is provided. - -== Upstream Contact == - -The MPFI website is located at http://mpfi.gforge.inria.fr/ - -The MPFI team can be contacted via the MPFI mailing list: -mpfi-users@lists.gforge.inria.fr - -== Dependencies == - - * GMP - * MPFR - -== Changelog == - -=== mpfi-1.5.1 (Jean-Pierre Flori, January 23rd, 2012) === - * #12171: Update MPFI to 1.5.1 - -=== mpfi-1.5.0 (Mike Hansen, December 17th, 2011) === - * #12171: Update MPFI to 1.5.0 - -=== mpfi-1.3.4-cvs20071125.p9 (Simon King, December 10th, 2011) === - * #12131: Use --libdir, to make the package work on openSUSE. - -=== mpfi-1.3.4-cvs20071125.p8 (Jaap Spies, January 26th, 2010) === - * Make 64 bit Open Solaris work - -=== mpfi-1.3.4-cvs20071125.p7 (Michael Abshoff, May 18th, 2008) === - * add 64 bit OSX build support - -=== mpfi-1.3.4-cvs20071125.p6 (William Stein, May 16, 2008) === - * modify configure.ac to work with cygwin. - * NOTE: src/ is *not* pristine! I ran autogen.sh after patching configure.ac. - -=== mpfi-1.3.4-cvs20071125.p5 (Michael Abshoff, Jan. 31st, 2008) === - * remove binary files from src/tests (#2011) - -=== mpfi-1.3.4-cvs20071125.p4 (Michael Abshoff) === - * change configure.ac to detect dylibs on OSX, too. Changed the macros in - configure.ac to: - -# Checks for MPFR lib (Before GMP!) -if ` test "$with_mpfr_lib" ` -then - AC_MSG_CHECKING(MPFR library) - if test -r "$with_mpfr_lib/libmpfr.so" - then - LDADD="$LDADD -L$with_gmp_lib -lmpfr" - else - if test -r "$with_mpfr_lib/libmpfr.dylib" - then - LDADD="$LDADD -L$with_gmp_lib -lmpfr" - else - AC_MSG_ERROR([$with_mpfr_lib/libmpfr.so or libmpfr.dylib not found]) - fi - fi - AC_MSG_RESULT(yes) -else - AC_CHECK_LIB(mpfr, main, , AC_MSG_ERROR([Library MPFR not found])) -fi - -# Checks for GMP lib -if ` test "$with_gmp_lib" ` -then - AC_MSG_CHECKING(GMP library) - if test -r "$with_gmp_lib/libgmp.so" - then - LDADD="$LDADD -L$with_gmp_lib -lgmp" - else - if test -r "$with_gmp_lib/libgmp.dylib" - then - LDADD="$LDADD -L$with_gmp_lib -lgmp" - else - AC_MSG_ERROR([$with_gmp_lib/libgmp.so or libgmp.dylib not found]) - fi - fi - AC_MSG_RESULT(yes) -else - AC_CHECK_LIB(gmp, main, , AC_MSG_ERROR([Library GMP not found])) -fi - - -=== mpfi-1.3.4-cvs20071125.p3 (Michael Abshoff) === - * change configure.ac to check and link against a dynamic gmp.so, mpfr.so - -Clean up the test of the changelog - -1. I have changed two functions: - -First, mpfi_set_str() in mpfi/src/mpfi_io.c. -(The original version was very wrong; I fixed off-by-one errors, fixed -a buffer overflow, and fixed it so that it could return precise -intervals on precisely representable inputs, instead of always making -an interval containing two floats.) The original version of the file -is at mpfi/src/mpfi_io.c.ORIG - -Second, mpfi_diam_rel() in mpfi/src/mpfi.c: I changed line 598 from - if (!mpfr_cmp_ui(centre,0)) -to - if (mpfr_cmp_ui(centre,0)) - -2. I (=William Stein) changed configure.ac to only build src and not tests or -doc, since building tests on some systems fails. Of course, it would be -better to fix building of tests. I put the changed version of configure.ac -in the patches directory. After changing it, I ran - autoreconf --force -in the src directory. - -3. Carl Witty 2007-11-01 -a. Fixed mpfi_ui_sub, by applying a patch posted by Paul Zimmerman to the -MPFI bug tracker. -b. Fixed the infinite loop in mpfi_cmp_sym_pi() (fixes the infinite loop -in mpfi_cos(), reported as MPFI bug# 1868 and Sage issue #389. - -4. Carl Witty 2007-11-25 -MPFI upstream is alive again! I'm switching to the current upstream CVS -version as of today, fetched with: - -cvs -d :pserver:anonymous@scm.gforge.inria.fr:/cvsroot/mpfi login -cvs -d :pserver:anonymous@scm.gforge.inria.fr:/cvsroot/mpfi checkout mpfi -mv mpfi src -cd src -./autogen.sh - -This version includes all the patches from the Sage spkg (or -equivalent code), and fixes several additional bugs. (So I removed -the patches/ directory, and the corresponding lines in spkg-install.) -Also, this new version installs both shared and static libraries. - -5. Carl Witty 2007-11-29 -The previous version did not compile on OSX 10.4 Intel. I think the problem -may be with src/autogen.sh, which makes ltmain.sh, config.sub, and -config.guess be symlinks instead of files; so we get whatever versions -of these files happen to be already installed on the user's computer. - -So I've replaced these three symlinks with the corresponding files, and -we'll see if that fixes the problem. - -6. Carl Witty 2007-12-01 -The previous version required "autoheader" (part of autoconf). -I've copied in a new version of src/missing, and touched src/mpfi_config.h.in; -I believe that either of these changes would suffice to fix the problem. diff --git a/build/pkgs/mpfi/distros/gentoo.txt b/build/pkgs/mpfi/distros/gentoo.txt new file mode 100644 index 00000000000..5aa96b6a5fb --- /dev/null +++ b/build/pkgs/mpfi/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/mpfi diff --git a/build/pkgs/mpfi/distros/homebrew.txt b/build/pkgs/mpfi/distros/homebrew.txt new file mode 100644 index 00000000000..0508439baac --- /dev/null +++ b/build/pkgs/mpfi/distros/homebrew.txt @@ -0,0 +1 @@ +mpfi diff --git a/build/pkgs/mpfi/spkg-check b/build/pkgs/mpfi/spkg-check deleted file mode 100644 index 367012a504d..00000000000 --- a/build/pkgs/mpfi/spkg-check +++ /dev/null @@ -1,14 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo "Error: SAGE_LOCAL undefined - exiting..." - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src -$MAKE check - -if [ $? -ne 0 ]; then - echo "There was a problem during the MPFI tests." - exit 1 -fi - diff --git a/build/pkgs/mpfi/spkg-check.in b/build/pkgs/mpfi/spkg-check.in new file mode 100644 index 00000000000..27cd9419538 --- /dev/null +++ b/build/pkgs/mpfi/spkg-check.in @@ -0,0 +1,2 @@ +cd src +$MAKE check diff --git a/build/pkgs/mpfi/spkg-configure.m4 b/build/pkgs/mpfi/spkg-configure.m4 index 5c4f3c51393..7805ef4a2dd 100644 --- a/build/pkgs/mpfi/spkg-configure.m4 +++ b/build/pkgs/mpfi/spkg-configure.m4 @@ -21,13 +21,13 @@ SAGE_SPKG_CONFIGURE([mpfi], [ [[#include #include ]], [[ - printf("%s\n", MPFI_VERSION_STRING); + fprintf(stderr, "%s\n", MPFI_VERSION_STRING); if (MPFI_VERSION_MAJOR >]] SAGE_MPFI_VERSION_MAJOR[[) return 0; else if (MPFI_VERSION_MAJOR ==]] SAGE_MPFI_VERSION_MAJOR[[ && MPFI_VERSION_MINOR >=]] SAGE_MPFI_VERSION_MINOR[[) return 0; else return 1; - ]])], [AC_MSG_RESULT([.. yes])], [ - AC_MSG_RESULT([.. no]) + ]])], [AC_MSG_RESULT([yes])], [ + AC_MSG_RESULT([no]) sage_spkg_install_mpfi=yes]) AC_LANG_POP(C)], [sage_spkg_install_mpfi=yes]) fi diff --git a/build/pkgs/mpfi/spkg-install b/build/pkgs/mpfi/spkg-install.in similarity index 100% rename from build/pkgs/mpfi/spkg-install rename to build/pkgs/mpfi/spkg-install.in diff --git a/build/pkgs/mpfr/SPKG.rst b/build/pkgs/mpfr/SPKG.rst new file mode 100644 index 00000000000..c2ce702f222 --- /dev/null +++ b/build/pkgs/mpfr/SPKG.rst @@ -0,0 +1,65 @@ +MPFR +==== + +Description +----------- + +The MPFR library is a C library for multiple-precision floating-point +computations with correct rounding. MPFR has continuously been supported +by the INRIA and the current main authors come from the Caramba and AriC +project-teams at Loria (Nancy, France) and LIP (Lyon, France) +respectively; see more on the credit page. MPFR is based on the GMP +multiple-precision library. + +The main goal of MPFR is to provide a library for multiple-precision +floating-point computation which is both efficient and has a +well-defined semantics. It copies the good ideas from the ANSI/IEEE-754 +standard for double-precision floating-point arithmetic (53-bit +significand). + +License +------- + +MPFR is free. It is distributed under the GNU Lesser General Public +License (GNU Lesser GPL), version 3 or later (2.1 or later for MPFR +versions until 2.4.x). The library has been registered in France by the +Agence de Protection des Programmes under the number IDDN FR 001 120020 +00 R P 2000 000 10800, on 15 March 2000. This license guarantees your +freedom to share and change MPFR, to make sure MPFR is free for all its +users. Unlike the ordinary General Public License, the Lesser GPL +enables developers of non-free programs to use MPFR in their programs. +If you have written a new function for MPFR or improved an existing one, +please share your work! + + +Upstream Contact +---------------- + +The MPFR website is located at http://mpfr.org/ + +The MPFR team can be contacted via the MPFR mailing list: mpfr@loria.fr + +Dependencies +------------ + +- GMP/MPIR +- GNU patch + + +Special Update/Build Instructions +--------------------------------- + +- Make sure MPFR's settings of ``CC`` and ``CFLAGS`` still get properly + extracted, + currently from its ``config.log`` in the ``src/`` directory. + +- We should remove the ``configure`` option ``--disable-thread-safe`` + in case + the issues without that have meanwhile been fixed. (Then we should + actually pass ``--enable-thread-safe``.) + +TODO +---- + +- ``--disable-thread-safe`` should be switched to ``--enable-thread-safe``, + need to check that this works on the buildbot machines diff --git a/build/pkgs/mpfr/SPKG.txt b/build/pkgs/mpfr/SPKG.txt deleted file mode 100644 index c2a1bc11fa6..00000000000 --- a/build/pkgs/mpfr/SPKG.txt +++ /dev/null @@ -1,51 +0,0 @@ -= MPFR = - -== Description == - -The MPFR library is a C library for multiple-precision floating-point -computations with correct rounding. MPFR has continuously been supported by -the INRIA and the current main authors come from the Caramba and AriC -project-teams at Loria (Nancy, France) and LIP (Lyon, France) respectively; -see more on the credit page. MPFR is based on the GMP multiple-precision -library. - -The main goal of MPFR is to provide a library for multiple-precision -floating-point computation which is both efficient and has a well-defined -semantics. It copies the good ideas from the ANSI/IEEE-754 standard for -double-precision floating-point arithmetic (53-bit significand). - -== License == - -MPFR is free. It is distributed under the GNU Lesser General Public License -(GNU Lesser GPL), version 3 or later (2.1 or later for MPFR versions until -2.4.x). The library has been registered in France by the Agence de Protection -des Programmes under the number IDDN FR 001 120020 00 R P 2000 000 10800, on -15 March 2000. This license guarantees your freedom to share and change MPFR, -to make sure MPFR is free for all its users. Unlike the ordinary General -Public License, the Lesser GPL enables developers of non-free programs to use -MPFR in their programs. If you have written a new function for MPFR or -improved an existing one, please share your work! - -== Upstream Contact == - -The MPFR website is located at http://mpfr.org/ - -The MPFR team can be contacted via the MPFR mailing list: mpfr@loria.fr - -== Dependencies == - - * GMP/MPIR - * GNU patch - -== Special Update/Build Instructions == - - * Make sure MPFR's settings of `CC` and `CFLAGS` still get properly extracted, - currently from its `config.log` in the `src/` directory. - * We should remove the `configure` option `--disable-thread-safe` in case - the issues without that have meanwhile been fixed. (Then we should - actually pass `--enable-thread-safe`.) - -== TODO == - -* --disable-thread-safe should be switched to --enable-thread-safe, - need to check that this works on the buildbot machines diff --git a/build/pkgs/mpfr/distros/cygwin.txt b/build/pkgs/mpfr/distros/cygwin.txt new file mode 100644 index 00000000000..e02bb1bdbb1 --- /dev/null +++ b/build/pkgs/mpfr/distros/cygwin.txt @@ -0,0 +1 @@ +libmpfr-devel diff --git a/build/pkgs/mpfr/distros/gentoo.txt b/build/pkgs/mpfr/distros/gentoo.txt new file mode 100644 index 00000000000..ecd5c9b60ea --- /dev/null +++ b/build/pkgs/mpfr/distros/gentoo.txt @@ -0,0 +1 @@ +dev-libs/mpfr diff --git a/build/pkgs/mpfr/distros/homebrew.txt b/build/pkgs/mpfr/distros/homebrew.txt new file mode 100644 index 00000000000..5bcf2cdfb19 --- /dev/null +++ b/build/pkgs/mpfr/distros/homebrew.txt @@ -0,0 +1 @@ +mpfr diff --git a/build/pkgs/mpfr/distros/slackware.txt b/build/pkgs/mpfr/distros/slackware.txt new file mode 100644 index 00000000000..5bcf2cdfb19 --- /dev/null +++ b/build/pkgs/mpfr/distros/slackware.txt @@ -0,0 +1 @@ +mpfr diff --git a/build/pkgs/mpfr/spkg-check b/build/pkgs/mpfr/spkg-check deleted file mode 100644 index 28011697218..00000000000 --- a/build/pkgs/mpfr/spkg-check +++ /dev/null @@ -1,23 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -# unset RM since it messes with libtool -unset RM - -# We don't have to (again) set up CFLAGS etc. here, as 'configure' puts -# them into the Makefiles. - -cd src - -echo -echo "Now building and running MPFR's test suite..." -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error building or running MPFR's test suite." - exit 1 -fi -echo -echo "MPFR's test suite passed without errors." diff --git a/build/pkgs/mpfr/spkg-check.in b/build/pkgs/mpfr/spkg-check.in new file mode 100644 index 00000000000..d3e544ebc6f --- /dev/null +++ b/build/pkgs/mpfr/spkg-check.in @@ -0,0 +1,9 @@ +# unset RM since it messes with libtool +unset RM + +# We don't have to (again) set up CFLAGS etc. here, as 'configure' puts +# them into the Makefiles. + +cd src + +$MAKE check diff --git a/build/pkgs/mpfr/spkg-configure.m4 b/build/pkgs/mpfr/spkg-configure.m4 index 0d0ef933027..0c15b56df43 100644 --- a/build/pkgs/mpfr/spkg-configure.m4 +++ b/build/pkgs/mpfr/spkg-configure.m4 @@ -8,7 +8,7 @@ SAGE_SPKG_CONFIGURE([mpfr], [ AC_MSG_RESULT([no]) AC_CHECK_HEADER(mpfr.h, [], [sage_spkg_install_mpfr=yes]) dnl mpfr_free_pool appeared in r11922 (Dec 2017) on MPFR svn - AC_SEARCH_LIBS([mpfr_free_pool], [mpfr], [break], [sage_spkg_install_mpfr=yes]) + AC_SEARCH_LIBS([mpfr_free_pool], [mpfr], [], [sage_spkg_install_mpfr=yes]) fi ], [], [], [ if test x$sage_spkg_install_mpfr = xyes; then diff --git a/build/pkgs/mpfr/spkg-install b/build/pkgs/mpfr/spkg-install.in similarity index 100% rename from build/pkgs/mpfr/spkg-install rename to build/pkgs/mpfr/spkg-install.in diff --git a/build/pkgs/mpfrcx/SPKG.rst b/build/pkgs/mpfrcx/SPKG.rst new file mode 100644 index 00000000000..8a2c3e649ce --- /dev/null +++ b/build/pkgs/mpfrcx/SPKG.rst @@ -0,0 +1,26 @@ +MPFRCX +====== + +Description +----------- + +Mpfrcx is a library for the arithmetic of univariate polynomials over +arbitrary precision real (Mpfr) or complex (Mpc) numbers, without +control on the rounding. For the time being, only the few functions +needed to implement the floating point approach to complex +multiplication are implemented. On the other hand, these comprise +asymptotically fast multiplication routines such as Toom–Cook and the +FFT. + +License +------- + +MPFRCX is distributed under the Gnu Lesser General Public License, +either version 2.1 of the licence, or (at your option) any later version +(LGPLv2.1+). + + +Upstream Contact +---------------- + +The MPFRCX website is located at http://www.multiprecision.org/mpfrcx . diff --git a/build/pkgs/mpfrcx/SPKG.txt b/build/pkgs/mpfrcx/SPKG.txt deleted file mode 100644 index 0188cfda2b9..00000000000 --- a/build/pkgs/mpfrcx/SPKG.txt +++ /dev/null @@ -1,19 +0,0 @@ -= MPFRCX = - -== Description == - -Mpfrcx is a library for the arithmetic of univariate polynomials over arbitrary -precision real (Mpfr) or complex (Mpc) numbers, without control on the -rounding. For the time being, only the few functions needed to implement the -floating point approach to complex multiplication are implemented. On the other -hand, these comprise asymptotically fast multiplication routines such as -Toom–Cook and the FFT. - -== License == - -MPFRCX is distributed under the Gnu Lesser General Public License, either -version 2.1 of the licence, or (at your option) any later version (LGPLv2.1+). - -== Upstream Contact == - -The MPFRCX website is located at http://www.multiprecision.org/mpfrcx . diff --git a/build/pkgs/mpfrcx/spkg-check b/build/pkgs/mpfrcx/spkg-check deleted file mode 100644 index 110b8f8d218..00000000000 --- a/build/pkgs/mpfrcx/spkg-check +++ /dev/null @@ -1,7 +0,0 @@ -cd src - -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error checking MPFRCX." - exit 1 -fi diff --git a/build/pkgs/mpfrcx/spkg-check.in b/build/pkgs/mpfrcx/spkg-check.in new file mode 100644 index 00000000000..917c8eb7209 --- /dev/null +++ b/build/pkgs/mpfrcx/spkg-check.in @@ -0,0 +1,3 @@ +cd src + +$MAKE check diff --git a/build/pkgs/mpfrcx/spkg-install b/build/pkgs/mpfrcx/spkg-install.in similarity index 100% rename from build/pkgs/mpfrcx/spkg-install rename to build/pkgs/mpfrcx/spkg-install.in diff --git a/build/pkgs/mpfrcx/spkg-legacy-uninstall b/build/pkgs/mpfrcx/spkg-legacy-uninstall.in similarity index 100% rename from build/pkgs/mpfrcx/spkg-legacy-uninstall rename to build/pkgs/mpfrcx/spkg-legacy-uninstall.in diff --git a/build/pkgs/mpir/SPKG.rst b/build/pkgs/mpir/SPKG.rst new file mode 100644 index 00000000000..02d897ef779 --- /dev/null +++ b/build/pkgs/mpir/SPKG.rst @@ -0,0 +1,42 @@ +MPIR +==== + +Description +----------- + +MPIR is an open source multiprecision integer library derived from +version 5.0.1 of the GMP (GNU Multi Precision) project (which was +licensed LGPL v2+). + +See http://www.mpir.org + +License +------- + +- LGPL V3+ + + +Upstream Contact +---------------- + +- The Google group mpir-devel +- thempirteam@googlemail.com + +Dependencies +------------ + +- iconv +- GNU patch + + +Special Update/Build Instructions +--------------------------------- + +- TODO: +- Perhaps also modify CXXFLAGS (and/or CPPFLAGS). +- We currently don't use anything of GMP's/MPIR's CC setting, and + matching with the current compiler (``$CC``) is perhaps suboptimal. +- Remove some files / directories not needed for Sage from upstream: +- build.vc\* directories (Microsoft Visual C build files) +- 3.0.0-644faf502c56f97d9accd301965fc57d6ec70868 + was created by running the spkg-src script. diff --git a/build/pkgs/mpir/SPKG.txt b/build/pkgs/mpir/SPKG.txt deleted file mode 100644 index c836e975233..00000000000 --- a/build/pkgs/mpir/SPKG.txt +++ /dev/null @@ -1,30 +0,0 @@ -= MPIR = - -== Description == - -MPIR is an open source multiprecision integer library derived from -version 5.0.1 of the GMP (GNU Multi Precision) project (which was -licensed LGPL v2+). - -See http://www.mpir.org - -== License == - * LGPL V3+ - -== Upstream Contact == - * The Google group mpir-devel - * thempirteam@googlemail.com - -== Dependencies == - * iconv - * GNU patch - -== Special Update/Build Instructions == - * TODO: - - Perhaps also modify CXXFLAGS (and/or CPPFLAGS). - - We currently don't use anything of GMP's/MPIR's CC setting, and matching - with the current compiler (`$CC`) is perhaps suboptimal. - * Remove some files / directories not needed for Sage from upstream: - - build.vc* directories (Microsoft Visual C build files) - * 3.0.0-644faf502c56f97d9accd301965fc57d6ec70868 - was created by running the spkg-src script. diff --git a/build/pkgs/mpir/spkg-check b/build/pkgs/mpir/spkg-check deleted file mode 100644 index 26c8d29ccb1..00000000000 --- a/build/pkgs/mpir/spkg-check +++ /dev/null @@ -1,19 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -# We don't have to set up any environment variables here since the -# Makefiles already have them from 'configure'. - -echo "Now building and running MPIR's test suite..." -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error: The MPIR test suite failed." - exit 1 -fi - -echo "The MPIR test suite passed successfully." diff --git a/build/pkgs/mpir/spkg-check.in b/build/pkgs/mpir/spkg-check.in new file mode 100644 index 00000000000..4727de00ffe --- /dev/null +++ b/build/pkgs/mpir/spkg-check.in @@ -0,0 +1,6 @@ +cd src + +# We don't have to set up any environment variables here since the +# Makefiles already have them from 'configure'. + +$MAKE check diff --git a/build/pkgs/mpir/spkg-configure.m4 b/build/pkgs/mpir/spkg-configure.m4 index c4f9c432e2a..01afff14621 100644 --- a/build/pkgs/mpir/spkg-configure.m4 +++ b/build/pkgs/mpir/spkg-configure.m4 @@ -5,7 +5,7 @@ dnl Implement cases for what to do on different options here AC_CHECK_HEADER(gmp.h, [], [sage_spkg_install_mpir=yes]) AC_CHECK_HEADER(gmpxx.h, [], [sage_spkg_install_mpir=yes]) dnl mpq_cmp_z appeared in GMP 6.1.0 and is used by pynac - AC_SEARCH_LIBS([__gmpq_cmp_z], [gmp], [break], + AC_SEARCH_LIBS([__gmpq_cmp_z], [gmp], [], [sage_spkg_install_mpir=yes]) SAGE_MP_LIBRARY=mpir ;; diff --git a/build/pkgs/mpir/spkg-install b/build/pkgs/mpir/spkg-install.in similarity index 100% rename from build/pkgs/mpir/spkg-install rename to build/pkgs/mpir/spkg-install.in diff --git a/build/pkgs/mpmath/SPKG.rst b/build/pkgs/mpmath/SPKG.rst new file mode 100644 index 00000000000..a947501e4de --- /dev/null +++ b/build/pkgs/mpmath/SPKG.rst @@ -0,0 +1,28 @@ +mpmath +====== + +Description +----------- + +Mpmath is a pure-Python library for multiprecision floating-point +arithmetic. It provides an extensive set of transcendental functions, +unlimited exponent sizes, complex numbers, interval arithmetic, +numerical integration and differentiation, root-finding, linear algebra, +and much more. Almost any calculation can be performed just as well at +10-digit or 1000-digit precision, and in many cases mpmath implements +asymptotically fast algorithms that scale well for extremely high +precision work. If available, mpmath will (optionally) use gmpy to speed +up high precision operations. + + +Upstream Contact +---------------- + +- Author: Fredrik Johansson +- Email: fredrik.johansson@gmail.com +- Website: https://github.com/fredrik-johansson/mpmath/ + +Dependencies +------------ + +- Python diff --git a/build/pkgs/mpmath/SPKG.txt b/build/pkgs/mpmath/SPKG.txt deleted file mode 100644 index e6d841c6097..00000000000 --- a/build/pkgs/mpmath/SPKG.txt +++ /dev/null @@ -1,15 +0,0 @@ -= mpmath = - -== Description == - -Mpmath is a pure-Python library for multiprecision floating-point arithmetic. It provides an extensive set of transcendental functions, unlimited exponent sizes, complex numbers, interval arithmetic, numerical integration and differentiation, root-finding, linear algebra, and much more. Almost any calculation can be performed just as well at 10-digit or 1000-digit precision, and in many cases mpmath implements asymptotically fast algorithms that scale well for extremely high precision work. If available, mpmath will (optionally) use gmpy to speed up high precision operations. - -== Upstream Contact == - - * Author: Fredrik Johansson - * Email: fredrik.johansson@gmail.com - * Website: https://github.com/fredrik-johansson/mpmath/ - -== Dependencies == - - * Python diff --git a/build/pkgs/mpmath/dependencies b/build/pkgs/mpmath/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/mpmath/dependencies +++ b/build/pkgs/mpmath/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/mpmath/spkg-install b/build/pkgs/mpmath/spkg-install deleted file mode 100644 index 0d8433eeb2c..00000000000 --- a/build/pkgs/mpmath/spkg-install +++ /dev/null @@ -1,9 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src/ - -sdh_pip_install . diff --git a/build/pkgs/mpmath/spkg-install.in b/build/pkgs/mpmath/spkg-install.in new file mode 100644 index 00000000000..5308162cf32 --- /dev/null +++ b/build/pkgs/mpmath/spkg-install.in @@ -0,0 +1,3 @@ +cd src/ + +sdh_pip_install . diff --git a/build/pkgs/nauty/SPKG.rst b/build/pkgs/nauty/SPKG.rst new file mode 100644 index 00000000000..76e42233452 --- /dev/null +++ b/build/pkgs/nauty/SPKG.rst @@ -0,0 +1,37 @@ +Nauty +===== + +Description +----------- + +Nauty has various tools for finding the automorphism group of a graph, +generating non-isomorphic graphs with certain properties, etc. + +License +------- + +Since version 2.6, nauty license is GPL-compatible, see + +http://users.cecs.anu.edu.au/~bdm/nauty/COPYRIGHT.txt + +(a copy of this file, called COPYRIGHT, is also present in the tarball) + + +Special Packaging Instruction +----------------------------- + +Upstream distribute tarball named nauty${version}.tar.gz. We cannot deal +with that so rename it nauty-${version}.tar.gz (notice the "-") without +any changes. + + +Upstream Contact +---------------- + +Brendan D. McKay Computer Science Department Australian National +University bdm@cs.anu.edu.au + +Adolfo Piperno Dipartimento di Informatica Sapienza - Università di Roma +piperno@di.uniroma1.it + +See http://cs.anu.edu.au/~bdm/nauty/ Or http://pallini.di.uniroma1.it/ diff --git a/build/pkgs/nauty/SPKG.txt b/build/pkgs/nauty/SPKG.txt deleted file mode 100644 index d471d16a4aa..00000000000 --- a/build/pkgs/nauty/SPKG.txt +++ /dev/null @@ -1,35 +0,0 @@ -= Nauty = - -== Description == - -Nauty has various tools for finding the automorphism group of a graph, -generating non-isomorphic graphs with certain properties, etc. - -== License == - -Since version 2.6, nauty license is GPL-compatible, see - -http://users.cecs.anu.edu.au/~bdm/nauty/COPYRIGHT.txt - -(a copy of this file, called COPYRIGHT, is also present in the tarball) - -== Special Packaging Instruction == - -Upstream distribute tarball named nauty${version}.tar.gz. We cannot -deal with that so rename it nauty-${version}.tar.gz (notice the "-") -without any changes. - -== Upstream Contact == - -Brendan D. McKay -Computer Science Department -Australian National University -bdm@cs.anu.edu.au - -Adolfo Piperno -Dipartimento di Informatica -Sapienza - Università di Roma -piperno@di.uniroma1.it - -See http://cs.anu.edu.au/~bdm/nauty/ -Or http://pallini.di.uniroma1.it/ diff --git a/build/pkgs/nauty/checksums.ini b/build/pkgs/nauty/checksums.ini index c538968b6f7..644f4b75146 100644 --- a/build/pkgs/nauty/checksums.ini +++ b/build/pkgs/nauty/checksums.ini @@ -1,4 +1,5 @@ -tarball=nauty-VERSION.tar.gz -sha1=61d16a63e377fc61f2e00077bc0a537f7e5beb44 -md5=7a89ffd50fc7c690dc9eab321d7780f7 -cksum=1936541247 +tarball=nautyVERSION.tar.gz +sha1=c9fd2b4c99b8c624e430f3f4e1492a4219e3495e +md5=2ead635a417e20a18b3aabee83fac1ef +cksum=718823455 +upstream_url=http://pallini.di.uniroma1.it/nauty27r1.tar.gz diff --git a/build/pkgs/nauty/package-version.txt b/build/pkgs/nauty/package-version.txt index 04a3c70b7bb..d27393ab0ab 100644 --- a/build/pkgs/nauty/package-version.txt +++ b/build/pkgs/nauty/package-version.txt @@ -1 +1 @@ -26r1.p0 +27r1.p0 diff --git a/build/pkgs/nauty/spkg-check b/build/pkgs/nauty/spkg-check deleted file mode 100644 index 4d137575805..00000000000 --- a/build/pkgs/nauty/spkg-check +++ /dev/null @@ -1,18 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting"; - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -# runalltests doesn't exit with a zero status. -# So we check $fails instead. - -$MAKE checks -source ./runalltests - -if [ "${fails}" -ne "0" ]; then - echo >&2 "Error checking nauty." - exit 1 -fi diff --git a/build/pkgs/nauty/spkg-check.in b/build/pkgs/nauty/spkg-check.in new file mode 100644 index 00000000000..347665b46f2 --- /dev/null +++ b/build/pkgs/nauty/spkg-check.in @@ -0,0 +1,11 @@ +cd src + +# runalltests doesn't exit with a zero status. +# So we check $fails instead. + +$MAKE checks +source ./runalltests + +if [ "${fails}" -ne "0" ]; then + exit 1 +fi diff --git a/build/pkgs/nauty/spkg-configure.m4 b/build/pkgs/nauty/spkg-configure.m4 index 6648afd0876..64a038910ef 100644 --- a/build/pkgs/nauty/spkg-configure.m4 +++ b/build/pkgs/nauty/spkg-configure.m4 @@ -1,5 +1,8 @@ +# We don't use the "converseg" program, but we need to ensure that we +# only detect nauty >= 2.6 because we use the digraph6 format from +# that version -- and converseg was added in nauty-2.6. AC_DEFUN([SAGE_TEST_NAUTY_PROGS], [ - m4_foreach([nautyprog], [directg, gentourng, geng, genbg], [ + m4_foreach([nautyprog], [directg, gentourng, geng, genbg, converseg], [ AC_PATH_PROG([$2]nautyprog, [[$1]nautyprog]) AS_IF([test x$[$2]nautyprog = x], [sage_spkg_install_nauty=yes]) ]) diff --git a/build/pkgs/nauty/spkg-install b/build/pkgs/nauty/spkg-install.in similarity index 100% rename from build/pkgs/nauty/spkg-install rename to build/pkgs/nauty/spkg-install.in diff --git a/build/pkgs/nbconvert/SPKG.rst b/build/pkgs/nbconvert/SPKG.rst new file mode 100644 index 00000000000..b3f9dbdb44c --- /dev/null +++ b/build/pkgs/nbconvert/SPKG.rst @@ -0,0 +1,10 @@ +nbconvert +========= + +Description +----------- + +Converting Jupyter Notebooks + +jupyter nbconvert converts notebooks to various other formats via Jinja +templates. diff --git a/build/pkgs/nbconvert/SPKG.txt b/build/pkgs/nbconvert/SPKG.txt deleted file mode 100644 index 362c21c9e5f..00000000000 --- a/build/pkgs/nbconvert/SPKG.txt +++ /dev/null @@ -1,8 +0,0 @@ -= nbconvert = - -== Description == - -Converting Jupyter Notebooks - -jupyter nbconvert converts notebooks to various other formats via Jinja -templates. diff --git a/build/pkgs/nbconvert/dependencies b/build/pkgs/nbconvert/dependencies index c4131d26f55..100e53ee242 100644 --- a/build/pkgs/nbconvert/dependencies +++ b/build/pkgs/nbconvert/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip mistune jinja2 pygments traitlets jupyter_core nbformat entrypoints bleach pandocfilters testpath defusedxml +$(PYTHON) | $(PYTHON_TOOLCHAIN) mistune jinja2 pygments traitlets jupyter_core nbformat entrypoints bleach pandocfilters testpath defusedxml ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/imagesize/spkg-install b/build/pkgs/nbconvert/spkg-install.in similarity index 100% rename from build/pkgs/imagesize/spkg-install rename to build/pkgs/nbconvert/spkg-install.in diff --git a/build/pkgs/nbformat/SPKG.rst b/build/pkgs/nbformat/SPKG.rst new file mode 100644 index 00000000000..de42d912de6 --- /dev/null +++ b/build/pkgs/nbformat/SPKG.rst @@ -0,0 +1,10 @@ +nbformat +======== + +Description +----------- + +The Jupyter Notebook format + +This package contains the base implementation of the Jupyter Notebook +format, and Python APIs for working with notebooks. diff --git a/build/pkgs/nbformat/SPKG.txt b/build/pkgs/nbformat/SPKG.txt deleted file mode 100644 index a6c91aa1966..00000000000 --- a/build/pkgs/nbformat/SPKG.txt +++ /dev/null @@ -1,8 +0,0 @@ -= nbformat = - -== Description == - -The Jupyter Notebook format - -This package contains the base implementation of the Jupyter Notebook -format, and Python APIs for working with notebooks. diff --git a/build/pkgs/nbformat/dependencies b/build/pkgs/nbformat/dependencies index ce52858c220..c6dc6e3aed4 100644 --- a/build/pkgs/nbformat/dependencies +++ b/build/pkgs/nbformat/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip jsonschema +$(PYTHON) | $(PYTHON_TOOLCHAIN) jsonschema ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/ipython_genutils/spkg-install b/build/pkgs/nbformat/spkg-install.in similarity index 100% rename from build/pkgs/ipython_genutils/spkg-install rename to build/pkgs/nbformat/spkg-install.in diff --git a/build/pkgs/ncurses/SPKG.rst b/build/pkgs/ncurses/SPKG.rst new file mode 100644 index 00000000000..be8e3bb5b0b --- /dev/null +++ b/build/pkgs/ncurses/SPKG.rst @@ -0,0 +1,49 @@ +Ncurses +======= + +Description +----------- + +Ncurses (new curses, pronounced "enn-curses") started as a freely +distributable "clone" of System V Release 4.0 (SVr4) curses. It has +outgrown the "clone" description, and now contains many features which +are not in SVr4 curses. Curses is a pun on the term "cursor +optimization". It is a library of functions that manage an application's +display on character-cell terminals (e.g., VT100). + +The name "ncurses" was first used as the name of the curses library in +Pavel Curtis's pcurses, dated 1982. It was apparently developed on a BSD +4.4 system, at Cornell. Parts of pcurses are readily identifiable in +ncurses, including the basics for the terminfo compiler (named compile +in that package): + +- the Caps, used to define the terminfo capabilities +- awk scripts MKcaptab.awk, MKnames.awk +- the library modules used for the terminfo compiler. + +Besides ncurses, parts of pcurses still survive in 2010, in recognizable +form in Solaris. + +Website: http://invisible-island.net/ncurses + +License +------- + +- MIT-style + + +Upstream Contact +---------------- + +- bug-ncurses@gnu.org + +Dependencies +------------ + +None + + +Special Update/Build Instructions +--------------------------------- + +None diff --git a/build/pkgs/ncurses/SPKG.txt b/build/pkgs/ncurses/SPKG.txt deleted file mode 100644 index 9b8f450a1c7..00000000000 --- a/build/pkgs/ncurses/SPKG.txt +++ /dev/null @@ -1,41 +0,0 @@ -= Ncurses = - -== Description == - -Ncurses (new curses, pronounced "enn-curses") started as a freely -distributable "clone" of System V Release 4.0 (SVr4) curses. It has -outgrown the "clone" description, and now contains many features which -are not in SVr4 curses. Curses is a pun on the term "cursor -optimization". It is a library of functions that manage an -application's display on character-cell terminals (e.g., VT100). - -The name "ncurses" was first used as the name of the curses library in -Pavel Curtis's pcurses, dated 1982. It was apparently developed on a -BSD 4.4 system, at Cornell. Parts of pcurses are readily identifiable -in ncurses, including the basics for the terminfo compiler (named -compile in that package): - - * the Caps, used to define the terminfo capabilities - * awk scripts MKcaptab.awk, MKnames.awk - * the library modules used for the terminfo compiler. - -Besides ncurses, parts of pcurses still survive in 2010, in -recognizable form in Solaris. - -Website: http://invisible-island.net/ncurses - -== License == - - * MIT-style - -== Upstream Contact == - - * bug-ncurses@gnu.org - -== Dependencies == - -None - -== Special Update/Build Instructions == - -None diff --git a/build/pkgs/ncurses/distros/cygwin.txt b/build/pkgs/ncurses/distros/cygwin.txt new file mode 100644 index 00000000000..d29f30ce45b --- /dev/null +++ b/build/pkgs/ncurses/distros/cygwin.txt @@ -0,0 +1 @@ +libncurses-devel diff --git a/build/pkgs/ncurses/distros/slackware.txt b/build/pkgs/ncurses/distros/slackware.txt new file mode 100644 index 00000000000..6a470ffa9e3 --- /dev/null +++ b/build/pkgs/ncurses/distros/slackware.txt @@ -0,0 +1 @@ +ncurses diff --git a/build/pkgs/ncurses/spkg-configure.m4 b/build/pkgs/ncurses/spkg-configure.m4 index 8fe619cbefd..c706d4091d5 100644 --- a/build/pkgs/ncurses/spkg-configure.m4 +++ b/build/pkgs/ncurses/spkg-configure.m4 @@ -2,8 +2,8 @@ SAGE_SPKG_CONFIGURE([ncurses], [ dnl First try checking for ncurses with pkg-config PKG_CHECK_MODULES([NCURSES], [ncurses >= 6.0], [], [AC_CHECK_HEADERS([ncurses.h], - [AC_SEARCH_LIBS([wresize], [ncurses tinfo], [break], - [sage_spkg_install_ncurses=yes])], + [AC_SEARCH_LIBS([wresize], [ncurses tinfo], [], + [sage_spkg_install_ncurses=yes])], [sage_spkg_install_ncurses=yes])], [sage_spkg_install_ncurses=yes]) ]) diff --git a/build/pkgs/ncurses/spkg-install b/build/pkgs/ncurses/spkg-install.in similarity index 100% rename from build/pkgs/ncurses/spkg-install rename to build/pkgs/ncurses/spkg-install.in diff --git a/build/pkgs/networkx/SPKG.rst b/build/pkgs/networkx/SPKG.rst new file mode 100644 index 00000000000..74bfdfe037d --- /dev/null +++ b/build/pkgs/networkx/SPKG.rst @@ -0,0 +1,19 @@ +NetworkX +======== + +Description +----------- + +NetworkX (NX) is a Python package for the creation, manipulation, and +study of the structure, dynamics, and functions of complex networks. + +License +------- + +BSD + + +Upstream Contact +---------------- + +https://networkx.github.io/ diff --git a/build/pkgs/networkx/SPKG.txt b/build/pkgs/networkx/SPKG.txt deleted file mode 100644 index ca598bbad47..00000000000 --- a/build/pkgs/networkx/SPKG.txt +++ /dev/null @@ -1,13 +0,0 @@ -= NetworkX = - -== Description == - -NetworkX (NX) is a Python package for the creation, manipulation, and study of the structure, dynamics, and functions of complex networks. - -== License == - -BSD - -== Upstream Contact == - -https://networkx.github.io/ diff --git a/build/pkgs/networkx/checksums.ini b/build/pkgs/networkx/checksums.ini index 1a14641e92e..dae067b78ee 100644 --- a/build/pkgs/networkx/checksums.ini +++ b/build/pkgs/networkx/checksums.ini @@ -1,4 +1,5 @@ tarball=networkx-VERSION.zip -sha1=a68f8faa3726c220b93d68cfa28bed24874ba3fc -md5=82608a3686fb3e61f20cf13bfd3c1b4a -cksum=539980282 +sha1=b440621c1f597b6dff46c1150541a48601a180cc +md5=95617052af8998381ff3bc9c88ffeaa3 +cksum=2271648010 +upstream_url=https://github.com/networkx/networkx/archive/networkx-2.4.zip diff --git a/build/pkgs/networkx/dependencies b/build/pkgs/networkx/dependencies index 872ab236fa2..3d0462a6697 100644 --- a/build/pkgs/networkx/dependencies +++ b/build/pkgs/networkx/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) decorator | pip nose scipy +$(PYTHON) decorator | $(PYTHON_TOOLCHAIN) scipy $(and $(filter-out no,$(SAGE_CHECK_networkx)), nose pytest) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/networkx/package-version.txt b/build/pkgs/networkx/package-version.txt index 8bbe6cf74a1..6b4950e3de2 100644 --- a/build/pkgs/networkx/package-version.txt +++ b/build/pkgs/networkx/package-version.txt @@ -1 +1 @@ -2.2 +2.4 diff --git a/build/pkgs/networkx/spkg-check b/build/pkgs/networkx/spkg-check deleted file mode 100644 index 6b23e506299..00000000000 --- a/build/pkgs/networkx/spkg-check +++ /dev/null @@ -1,22 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -cd src - -if ! command -v nosetests ; then - echo >&2 'Testing networkx requires the package nose to be installed' - exit 1 -fi - -echo "Testing networkx..." - -nosetests networkx -v - -if [ $? -ne 0 ]; then - echo >&2 "Error running self tests." - exit 1 -fi - diff --git a/build/pkgs/networkx/spkg-check.in b/build/pkgs/networkx/spkg-check.in new file mode 100644 index 00000000000..60868aaefc5 --- /dev/null +++ b/build/pkgs/networkx/spkg-check.in @@ -0,0 +1,8 @@ +cd src + +if ! command -v nosetests ; then + echo >&2 'Testing networkx requires the package nose to be installed' + exit 1 +fi + +nosetests networkx -v diff --git a/build/pkgs/networkx/spkg-install b/build/pkgs/networkx/spkg-install deleted file mode 100644 index 56e141a4ec5..00000000000 --- a/build/pkgs/networkx/spkg-install +++ /dev/null @@ -1,11 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -export MAKE=make - -cd src - -sdh_pip_install . diff --git a/build/pkgs/networkx/spkg-install.in b/build/pkgs/networkx/spkg-install.in new file mode 100644 index 00000000000..08eb6a51163 --- /dev/null +++ b/build/pkgs/networkx/spkg-install.in @@ -0,0 +1,5 @@ +export MAKE=make + +cd src + +sdh_pip_install . diff --git a/build/pkgs/nibabel/requirements.txt b/build/pkgs/nibabel/requirements.txt new file mode 100644 index 00000000000..fa06aea51b4 --- /dev/null +++ b/build/pkgs/nibabel/requirements.txt @@ -0,0 +1 @@ +nibabel diff --git a/build/pkgs/nibabel/type b/build/pkgs/nibabel/type index a1b589e38a3..134d9bc32d5 100644 --- a/build/pkgs/nibabel/type +++ b/build/pkgs/nibabel/type @@ -1 +1 @@ -pip +optional diff --git a/build/pkgs/ninja_build/SPKG.rst b/build/pkgs/ninja_build/SPKG.rst new file mode 100644 index 00000000000..a0b88ecf63b --- /dev/null +++ b/build/pkgs/ninja_build/SPKG.rst @@ -0,0 +1,23 @@ +ninja_build +=========== + +Description +----------- + +Ninja is a small build system with a focus on speed. + +License +------- + +Apache License 2.0 + + +Upstream Contact +---------------- + +https://ninja-build.org/ + +Dependencies +------------ + +None diff --git a/build/pkgs/ninja_build/SPKG.txt b/build/pkgs/ninja_build/SPKG.txt deleted file mode 100644 index caa6b5698a9..00000000000 --- a/build/pkgs/ninja_build/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= ninja_build = - -== Description == - -Ninja is a small build system with a focus on speed. - -== License == - -Apache License 2.0 - -== Upstream Contact == - -https://ninja-build.org/ - -== Dependencies == - -None diff --git a/build/pkgs/ninja_build/distros/arch.txt b/build/pkgs/ninja_build/distros/arch.txt new file mode 100644 index 00000000000..63730036fd3 --- /dev/null +++ b/build/pkgs/ninja_build/distros/arch.txt @@ -0,0 +1 @@ +ninja diff --git a/build/pkgs/ninja_build/distros/centos.txt b/build/pkgs/ninja_build/distros/centos.txt new file mode 100644 index 00000000000..c8e097b1ceb --- /dev/null +++ b/build/pkgs/ninja_build/distros/centos.txt @@ -0,0 +1 @@ +ninja-build diff --git a/build/pkgs/ninja_build/distros/cygwin.txt b/build/pkgs/ninja_build/distros/cygwin.txt new file mode 100644 index 00000000000..63730036fd3 --- /dev/null +++ b/build/pkgs/ninja_build/distros/cygwin.txt @@ -0,0 +1 @@ +ninja diff --git a/build/pkgs/ninja_build/distros/gentoo.txt b/build/pkgs/ninja_build/distros/gentoo.txt new file mode 100644 index 00000000000..5d3a8ef090c --- /dev/null +++ b/build/pkgs/ninja_build/distros/gentoo.txt @@ -0,0 +1 @@ +dev-util/ninja diff --git a/build/pkgs/ninja_build/distros/homebrew.txt b/build/pkgs/ninja_build/distros/homebrew.txt new file mode 100644 index 00000000000..63730036fd3 --- /dev/null +++ b/build/pkgs/ninja_build/distros/homebrew.txt @@ -0,0 +1 @@ +ninja diff --git a/build/pkgs/ninja_build/spkg-install b/build/pkgs/ninja_build/spkg-install.in similarity index 100% rename from build/pkgs/ninja_build/spkg-install rename to build/pkgs/ninja_build/spkg-install.in diff --git a/build/pkgs/normaliz/SPKG.rst b/build/pkgs/normaliz/SPKG.rst new file mode 100644 index 00000000000..10d945bf913 --- /dev/null +++ b/build/pkgs/normaliz/SPKG.rst @@ -0,0 +1,39 @@ +normaliz +======== + +Description +----------- + +Normaliz is a tool for computations in affine monoids, vector +configurations, lattice polytopes, and rational cones. + +For more details see http://www.mathematik.uni-osnabrueck.de/normaliz/ + +License +------- + +- GPL v3 + + +Upstream Contact +---------------- + +- normaliz@uos.de +- Winfried Bruns +- Christof Söger +- see also https://www.normaliz.uni-osnabrueck.de/home/contact/ + + and https://github.com/Normaliz + +Dependencies +------------ + +- GMP/MPIR +- boost + + +Special Update/Build Instructions +--------------------------------- + +- The spkg currently disables features that require packages SCIP and + CoCoA, for which we don't have packages (yet). diff --git a/build/pkgs/normaliz/SPKG.txt b/build/pkgs/normaliz/SPKG.txt deleted file mode 100644 index e7fbd64b373..00000000000 --- a/build/pkgs/normaliz/SPKG.txt +++ /dev/null @@ -1,29 +0,0 @@ -= normaliz = - -== Description == - -Normaliz is a tool for computations in affine monoids, vector configurations, lattice polytopes, and rational cones. - -For more details see http://www.mathematik.uni-osnabrueck.de/normaliz/ - -== License == - - * GPL v3 - -== Upstream Contact == - - * normaliz@uos.de - * Winfried Bruns - * Christof Söger - * see also https://www.normaliz.uni-osnabrueck.de/home/contact/ - and https://github.com/Normaliz - -== Dependencies == - - * GMP/MPIR - * boost - -== Special Update/Build Instructions == - - * The spkg currently disables features that require packages SCIP and - CoCoA, for which we don't have packages (yet). diff --git a/build/pkgs/normaliz/spkg-check b/build/pkgs/normaliz/spkg-check deleted file mode 100644 index 91fb6ed416e..00000000000 --- a/build/pkgs/normaliz/spkg-check +++ /dev/null @@ -1,14 +0,0 @@ -cd src/ - -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -$MAKE check -if [ $? -ne 0 ]; then - echo "Error in testing normaliz" - exit 1 -fi - diff --git a/build/pkgs/normaliz/spkg-check.in b/build/pkgs/normaliz/spkg-check.in new file mode 100644 index 00000000000..917c8eb7209 --- /dev/null +++ b/build/pkgs/normaliz/spkg-check.in @@ -0,0 +1,3 @@ +cd src + +$MAKE check diff --git a/build/pkgs/normaliz/spkg-install b/build/pkgs/normaliz/spkg-install.in similarity index 100% rename from build/pkgs/normaliz/spkg-install rename to build/pkgs/normaliz/spkg-install.in diff --git a/build/pkgs/nose/SPKG.rst b/build/pkgs/nose/SPKG.rst new file mode 100644 index 00000000000..b66ffd2a980 --- /dev/null +++ b/build/pkgs/nose/SPKG.rst @@ -0,0 +1,34 @@ +nose +==== + +Description +----------- + +nose extends the test loading and running features of unittest, making +it easier to write, find and run tests. + +License +------- + +GNU LGPL + + +Upstream Contact +---------------- + +Author: Jason Pellerin Home Page: http://readthedocs.org/docs/nose/ + + see also https://github.com/nose-devs/nose + +Dependencies +------------ + +- setuptools / distribute +- Python +- GNU patch (shipped with Sage) + + +Special Update/Build Instructions +--------------------------------- + +None. diff --git a/build/pkgs/nose/SPKG.txt b/build/pkgs/nose/SPKG.txt deleted file mode 100644 index e978029b47a..00000000000 --- a/build/pkgs/nose/SPKG.txt +++ /dev/null @@ -1,26 +0,0 @@ -= nose = - -== Description == - -nose extends the test loading and running features of unittest, making -it easier to write, find and run tests. - -== License == - -GNU LGPL - -== Upstream Contact == - -Author: Jason Pellerin -Home Page: http://readthedocs.org/docs/nose/ - see also https://github.com/nose-devs/nose - -== Dependencies == - - * setuptools / distribute - * Python - * GNU patch (shipped with Sage) - -== Special Update/Build Instructions == - -None. diff --git a/build/pkgs/nose/dependencies b/build/pkgs/nose/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/nose/dependencies +++ b/build/pkgs/nose/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/nose/spkg-check b/build/pkgs/nose/spkg-check deleted file mode 100644 index b30f5885721..00000000000 --- a/build/pkgs/nose/spkg-check +++ /dev/null @@ -1,18 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo "SAGE_LOCAL undefined - exiting..." - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -nosetests functional_tests -if [ $? -ne 0 ]; then - echo "Error running functional_tests." - exit 1 -fi -nosetests unit_tests -if [ $? -ne 0 ]; then - echo "Error running unit_tests." - exit 1 -fi diff --git a/build/pkgs/nose/spkg-check.in b/build/pkgs/nose/spkg-check.in new file mode 100644 index 00000000000..59090720043 --- /dev/null +++ b/build/pkgs/nose/spkg-check.in @@ -0,0 +1,7 @@ +cd src + +if SAGE_PYTHON3=yes; then + python3 setup.py build_tests +fi + +sage-python23 selftest.py diff --git a/build/pkgs/nose/spkg-install b/build/pkgs/nose/spkg-install deleted file mode 100644 index 072300c174a..00000000000 --- a/build/pkgs/nose/spkg-install +++ /dev/null @@ -1,23 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo "SAGE_LOCAL undefined - exiting..." - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -# Helper functions -success() { - if [ $? -ne 0 ]; then - echo "Error: '$1'" - exit 1 - fi -} - -CUR=`pwd` - -cd src - -# Install new version -echo "Installing nose..." -sdh_pip_install . -success 'Error installing nose' -echo diff --git a/build/pkgs/nose/spkg-install.in b/build/pkgs/nose/spkg-install.in new file mode 100644 index 00000000000..fe7461d2f8b --- /dev/null +++ b/build/pkgs/nose/spkg-install.in @@ -0,0 +1,6 @@ +cd src + +# Install new version +echo "Installing nose..." +sdh_pip_install . +echo diff --git a/build/pkgs/notebook/SPKG.rst b/build/pkgs/notebook/SPKG.rst new file mode 100644 index 00000000000..92efe65018a --- /dev/null +++ b/build/pkgs/notebook/SPKG.rst @@ -0,0 +1,8 @@ +notebook +======== + +Description +----------- + +The Jupyter HTML notebook is a web-based notebook environment for +interactive computing. diff --git a/build/pkgs/notebook/SPKG.txt b/build/pkgs/notebook/SPKG.txt deleted file mode 100644 index af2253acd9f..00000000000 --- a/build/pkgs/notebook/SPKG.txt +++ /dev/null @@ -1,6 +0,0 @@ -= notebook = - -== Description == - -The Jupyter HTML notebook is a web-based notebook environment for -interactive computing. diff --git a/build/pkgs/notebook/dependencies b/build/pkgs/notebook/dependencies index 2fef8dc8885..76a05899142 100644 --- a/build/pkgs/notebook/dependencies +++ b/build/pkgs/notebook/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip ipython jupyter_client ipykernel nbconvert nbformat jinja2 tornado terminado send2trash ipaddress prometheus_client +$(PYTHON) | $(PYTHON_TOOLCHAIN) ipython jupyter_client ipykernel nbconvert nbformat jinja2 tornado terminado send2trash prometheus_client ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/notebook/spkg-install b/build/pkgs/notebook/spkg-install.in similarity index 100% rename from build/pkgs/notebook/spkg-install rename to build/pkgs/notebook/spkg-install.in diff --git a/build/pkgs/notedown/SPKG.rst b/build/pkgs/notedown/SPKG.rst new file mode 100644 index 00000000000..793a1d1ef24 --- /dev/null +++ b/build/pkgs/notedown/SPKG.rst @@ -0,0 +1,28 @@ +notedown +======== + +Description +----------- + +Notedown is a simple tool to create IPython notebooks from markdown. + +License +------- + +BSD 2-Clause License + + +Upstream Contact +---------------- + +Author: Aaron O'Leary Home page: https://github.com/aaren/notedown + +Dependencies +------------ + +- Python +- setuptools +- nbformat +- nbconvert +- six +- pandoc_attributes diff --git a/build/pkgs/notedown/SPKG.txt b/build/pkgs/notedown/SPKG.txt deleted file mode 100644 index f22216f0f51..00000000000 --- a/build/pkgs/notedown/SPKG.txt +++ /dev/null @@ -1,23 +0,0 @@ -= notedown = - -== Description == - -Notedown is a simple tool to create IPython notebooks from markdown. - -== License == - -BSD 2-Clause License - -== Upstream Contact == - -Author: Aaron O'Leary -Home page: https://github.com/aaren/notedown - -== Dependencies == - -* Python -* setuptools -* nbformat -* nbconvert -* six -* pandoc_attributes diff --git a/build/pkgs/notedown/dependencies b/build/pkgs/notedown/dependencies index c72c453399f..229a2ffe418 100644 --- a/build/pkgs/notedown/dependencies +++ b/build/pkgs/notedown/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) setuptools | pip nbformat nbconvert six pandoc_attributes +$(PYTHON) $(PYTHON_TOOLCHAIN) | pip nbformat nbconvert six pandoc_attributes ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/notedown/spkg-install b/build/pkgs/notedown/spkg-install deleted file mode 100644 index a93cbc60ea2..00000000000 --- a/build/pkgs/notedown/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing notedown ... exiting" - exit 1 -fi - diff --git a/build/pkgs/notedown/spkg-install.in b/build/pkgs/notedown/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/notedown/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/ntl/SPKG.rst b/build/pkgs/ntl/SPKG.rst new file mode 100644 index 00000000000..cc1ac43b9dd --- /dev/null +++ b/build/pkgs/ntl/SPKG.rst @@ -0,0 +1,35 @@ +NTL +=== + +Description +----------- + +NTL is a high-performance, portable C++ library providing data +structures and algorithms for manipulating signed, arbitrary length +integers, and for vectors, matrices, and polynomials over the integers +and over finite fields. + +Website: http://www.shoup.net/ntl/ + +License +------- + +- GNU LGPLv2.1+ + + +Upstream Contact +---------------- + +- Victor Shoup - for contact info see http://www.shoup.net/ + +Dependencies +------------ + +- gmp +- gf2x + + +Special Update/Build Instructions +--------------------------------- + +- None diff --git a/build/pkgs/ntl/SPKG.txt b/build/pkgs/ntl/SPKG.txt deleted file mode 100644 index 49e02e2a8a1..00000000000 --- a/build/pkgs/ntl/SPKG.txt +++ /dev/null @@ -1,21 +0,0 @@ -= NTL = - -== Description == -NTL is a high-performance, portable C++ library providing data structures and -algorithms for manipulating signed, arbitrary length integers, and for vectors, -matrices, and polynomials over the integers and over finite fields. - -Website: http://www.shoup.net/ntl/ - -== License == - * GNU LGPLv2.1+ - -== Upstream Contact == - * Victor Shoup - for contact info see http://www.shoup.net/ - -== Dependencies == - * gmp - * gf2x - -== Special Update/Build Instructions == - * None diff --git a/build/pkgs/ntl/distros/cygwin.txt b/build/pkgs/ntl/distros/cygwin.txt new file mode 100644 index 00000000000..fe822a6c887 --- /dev/null +++ b/build/pkgs/ntl/distros/cygwin.txt @@ -0,0 +1 @@ +libntl-devel diff --git a/build/pkgs/ntl/distros/gentoo.txt b/build/pkgs/ntl/distros/gentoo.txt new file mode 100644 index 00000000000..2e521e89a76 --- /dev/null +++ b/build/pkgs/ntl/distros/gentoo.txt @@ -0,0 +1 @@ +dev-libs/ntl diff --git a/build/pkgs/ntl/distros/homebrew.txt b/build/pkgs/ntl/distros/homebrew.txt new file mode 100644 index 00000000000..6664b6c5bd4 --- /dev/null +++ b/build/pkgs/ntl/distros/homebrew.txt @@ -0,0 +1,3 @@ +## We cannot use ntl on homebrew because it is built with NTL_THREADS. +## See https://trac.sagemath.org/ticket/29339 +# ntl diff --git a/build/pkgs/ntl/spkg-check b/build/pkgs/ntl/spkg-check.in similarity index 100% rename from build/pkgs/ntl/spkg-check rename to build/pkgs/ntl/spkg-check.in diff --git a/build/pkgs/ntl/spkg-configure.m4 b/build/pkgs/ntl/spkg-configure.m4 index bbf5476a10b..fbf142a3e94 100644 --- a/build/pkgs/ntl/spkg-configure.m4 +++ b/build/pkgs/ntl/spkg-configure.m4 @@ -29,12 +29,17 @@ SAGE_SPKG_CONFIGURE([ntl], [ [[#include #include ]], [[ - printf("%s\n", NTL_VERSION); + fprintf(stderr, "%s\n", NTL_VERSION); if (NTL_MAJOR_VERSION >]] SAGE_NTL_VERSION_MAJOR[[) return 0; else if (NTL_MAJOR_VERSION ==]] SAGE_NTL_VERSION_MAJOR[[ && NTL_MINOR_VERSION >=]] SAGE_NTL_VERSION_MINOR[[) return 0; else return 1; - ]])], [], [sage_spkg_install_ntl=yes]) + ]])], [ + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) + sage_spkg_install_ntl=yes + ]) fi m4_popdef([SAGE_NTL_VERSION_MAJOR]) @@ -42,10 +47,8 @@ SAGE_SPKG_CONFIGURE([ntl], [ ], [], [], [ if test x$sage_spkg_install_ntl = xyes; then AC_SUBST(SAGE_NTL_PREFIX, ['$SAGE_LOCAL']) - AC_MSG_RESULT([using Sage's ntl SPKG]) else AC_SUBST(SAGE_NTL_PREFIX, ['']) - AC_MSG_RESULT([using ntl library from the system]) fi ]) diff --git a/build/pkgs/ntl/spkg-install b/build/pkgs/ntl/spkg-install.in similarity index 100% rename from build/pkgs/ntl/spkg-install rename to build/pkgs/ntl/spkg-install.in diff --git a/build/pkgs/numpy/SPKG.rst b/build/pkgs/numpy/SPKG.rst new file mode 100644 index 00000000000..a70606eb027 --- /dev/null +++ b/build/pkgs/numpy/SPKG.rst @@ -0,0 +1,36 @@ +numpy +===== + +Description +----------- + +This package adds numerical linear algebra and other numerical computing +capabilities to python. + + +Upstream Contact +---------------- + +- Travis Oliphant +- Fernando Perez +- Brian Granger + +Dependencies +------------ + +- GNU patch +- Python +- Lapack +- Blas +- Atlas +- Fortran + + +Special Update/Build Instructions +--------------------------------- + +- Scipy uses numpy's distutils to control its compilation of fortran + code. + + Whenever numpy is updated it is necessary to make sure that scipy + still builds ok. diff --git a/build/pkgs/numpy/SPKG.txt b/build/pkgs/numpy/SPKG.txt deleted file mode 100644 index c7da9b2fdce..00000000000 --- a/build/pkgs/numpy/SPKG.txt +++ /dev/null @@ -1,27 +0,0 @@ -= numpy = - -== Description == - -This package adds numerical linear algebra and other numerical computing -capabilities to python. - -== Upstream Contact == - * Travis Oliphant - * Fernando Perez - * Brian Granger - -== Dependencies == - * GNU patch - * Python - * Lapack - * Blas - * Atlas - * Fortran - -== Special Update/Build Instructions == - * Scipy uses numpy's distutils to control its compilation of fortran code. - Whenever numpy is updated it is necessary to make sure that scipy still builds ok. - - - - diff --git a/build/pkgs/numpy/checksums.ini b/build/pkgs/numpy/checksums.ini index 4ca4526c0af..222887d73db 100644 --- a/build/pkgs/numpy/checksums.ini +++ b/build/pkgs/numpy/checksums.ini @@ -1,4 +1,5 @@ tarball=numpy-VERSION.zip -sha1=49787ce8e31aff103a7b182749abc3c0249b7f72 -md5=dafda51934f645d888866f98424521ae -cksum=3657383006 +sha1=0d6b62fbf723f5d91ab289766a252a30467609f1 +md5=2ccca1881b2766040149629614d22a3f +cksum=3596367842 +upstream_url=https://pypi.io/packages/source/n/numpy/numpy-VERSION.zip diff --git a/build/pkgs/numpy/dependencies b/build/pkgs/numpy/dependencies index 62d58ce643a..f46820d1797 100644 --- a/build/pkgs/numpy/dependencies +++ b/build/pkgs/numpy/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) $(BLAS) gfortran | setuptools pip pkgconfig cython +$(PYTHON) $(BLAS) gfortran | $(PYTHON_TOOLCHAIN) pkgconfig cython ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/numpy/package-version.txt b/build/pkgs/numpy/package-version.txt index 41c11ffb730..66e2ae6c25c 100644 --- a/build/pkgs/numpy/package-version.txt +++ b/build/pkgs/numpy/package-version.txt @@ -1 +1 @@ -1.16.1 +1.19.1 diff --git a/build/pkgs/numpy/patches/0001-ENH-Use-AVX-512-for-np.isnan-np.infinite-np.isinf-an.patch b/build/pkgs/numpy/patches/0001-ENH-Use-AVX-512-for-np.isnan-np.infinite-np.isinf-an.patch new file mode 100644 index 00000000000..791be1864ec --- /dev/null +++ b/build/pkgs/numpy/patches/0001-ENH-Use-AVX-512-for-np.isnan-np.infinite-np.isinf-an.patch @@ -0,0 +1,356 @@ +From 7bde5a589359a096be253738ed7c53330f7ff4ad Mon Sep 17 00:00:00 2001 +From: Raghuveer Devulapalli +Date: Sun, 31 May 2020 09:16:33 -0700 +Subject: [PATCH 1/2] ENH: Use AVX-512 for np.isnan, np.infinite, np.isinf and + np.signbit (#16334) + +* ENH: Use AVX-512 for np.isnan, np.infinite, np.isinf and np.signbit + +* TST: Add tests to validate isnan, isfinite, signbit and isinf ufuncs + +* BENCH: Adding benchmarks for isnan, isinf, isfinite and signbit +--- + benchmarks/benchmarks/bench_avx.py | 6 +- + numpy/core/code_generators/generate_umath.py | 8 +- + numpy/core/include/numpy/npy_common.h | 7 ++ + numpy/core/setup_common.py | 11 ++ + numpy/core/src/umath/loops.c.src | 10 +- + numpy/core/src/umath/loops.h.src | 7 +- + numpy/core/src/umath/simd.inc.src | 116 ++++++++++++++++++- + numpy/core/tests/test_umath.py | 18 +++ + 8 files changed, 173 insertions(+), 10 deletions(-) + +diff --git a/benchmarks/benchmarks/bench_avx.py b/benchmarks/benchmarks/bench_avx.py +index 2a128b3ff..4f915f82a 100644 +--- a/benchmarks/benchmarks/bench_avx.py ++++ b/benchmarks/benchmarks/bench_avx.py +@@ -13,7 +13,11 @@ avx_ufuncs = ['sin', + 'rint', + 'floor', + 'ceil' , +- 'trunc'] ++ 'trunc', ++ 'isnan', ++ 'isfinite', ++ 'isinf', ++ 'signbit'] + stride = [1, 2, 4] + dtype = ['f', 'd'] + +diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py +index 202912c5f..992bdc862 100644 +--- a/numpy/core/code_generators/generate_umath.py ++++ b/numpy/core/code_generators/generate_umath.py +@@ -843,7 +843,7 @@ defdict = { + Ufunc(1, 1, None, + docstrings.get('numpy.core.umath.isnan'), + 'PyUFunc_IsFiniteTypeResolver', +- TD(noobj, out='?'), ++ TD(noobj, simd=[('avx512_skx', 'fd')], out='?'), + ), + 'isnat': + Ufunc(1, 1, None, +@@ -855,19 +855,19 @@ defdict = { + Ufunc(1, 1, None, + docstrings.get('numpy.core.umath.isinf'), + 'PyUFunc_IsFiniteTypeResolver', +- TD(noobj, out='?'), ++ TD(noobj, simd=[('avx512_skx', 'fd')], out='?'), + ), + 'isfinite': + Ufunc(1, 1, None, + docstrings.get('numpy.core.umath.isfinite'), + 'PyUFunc_IsFiniteTypeResolver', +- TD(noobj, out='?'), ++ TD(noobj, simd=[('avx512_skx', 'fd')], out='?'), + ), + 'signbit': + Ufunc(1, 1, None, + docstrings.get('numpy.core.umath.signbit'), + None, +- TD(flts, out='?'), ++ TD(flts, simd=[('avx512_skx', 'fd')], out='?'), + ), + 'copysign': + Ufunc(2, 1, None, +diff --git a/numpy/core/include/numpy/npy_common.h b/numpy/core/include/numpy/npy_common.h +index c2e755958..3cec0c6ff 100644 +--- a/numpy/core/include/numpy/npy_common.h ++++ b/numpy/core/include/numpy/npy_common.h +@@ -64,6 +64,13 @@ + #define NPY_GCC_TARGET_AVX512F + #endif + ++#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX && defined HAVE_LINK_AVX512_SKX ++#define NPY_GCC_TARGET_AVX512_SKX __attribute__((target("avx512f,avx512dq,avx512vl,avx512bw,avx512cd"))) ++#elif defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS ++#define NPY_GCC_TARGET_AVX512_SKX __attribute__((target("avx512f,avx512dq,avx512vl,avx512bw,avx512cd"))) ++#else ++#define NPY_GCC_TARGET_AVX512_SKX ++#endif + /* + * mark an argument (starting from 1) that must not be NULL and is not checked + * DO NOT USE IF FUNCTION CHECKS FOR NULL!! the compiler will remove the check +diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py +index 63c4a76a9..a37514eec 100644 +--- a/numpy/core/setup_common.py ++++ b/numpy/core/setup_common.py +@@ -146,6 +146,10 @@ OPTIONAL_INTRINSICS = [("__builtin_isnan", '5.'), + "stdio.h", "LINK_AVX2"), + ("__asm__ volatile", '"vpaddd %zmm1, %zmm2, %zmm3"', + "stdio.h", "LINK_AVX512F"), ++ ("__asm__ volatile", '"vfpclasspd $0x40, %zmm15, %k6\\n"\ ++ "vmovdqu8 %xmm0, %xmm1\\n"\ ++ "vpbroadcastmb2q %k0, %xmm0\\n"', ++ "stdio.h", "LINK_AVX512_SKX"), + ("__asm__ volatile", '"xgetbv"', "stdio.h", "XGETBV"), + ] + +@@ -164,6 +168,8 @@ OPTIONAL_FUNCTION_ATTRIBUTES = [('__attribute__((optimize("unroll-loops")))', + 'attribute_target_avx2'), + ('__attribute__((target ("avx512f")))', + 'attribute_target_avx512f'), ++ ('__attribute__((target ("avx512f,avx512dq,avx512bw,avx512vl,avx512cd")))', ++ 'attribute_target_avx512_skx'), + ] + + # function attributes with intrinsics +@@ -180,6 +186,11 @@ OPTIONAL_FUNCTION_ATTRIBUTES_WITH_INTRINSICS = [('__attribute__((target("avx2,fm + 'attribute_target_avx512f_with_intrinsics', + '__m512 temp = _mm512_set1_ps(1.0)', + 'immintrin.h'), ++ ('__attribute__((target ("avx512f,avx512dq,avx512bw,avx512vl,avx512cd")))', ++ 'attribute_target_avx512_skx_with_intrinsics', ++ '__mmask8 temp = _mm512_fpclass_pd_mask(_mm512_set1_pd(1.0), 0x01);\ ++ _mm_mask_storeu_epi8(NULL, 0xFF, _mm_broadcastmb_epi64(temp))', ++ 'immintrin.h'), + ] + + # variable attributes tested via "int %s a" % attribute +diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src +index a59a9acf5..dbb8dd48e 100644 +--- a/numpy/core/src/umath/loops.c.src ++++ b/numpy/core/src/umath/loops.c.src +@@ -1863,10 +1863,15 @@ NPY_NO_EXPORT void + * #kind = isnan, isinf, isfinite, signbit# + * #func = npy_isnan, npy_isinf, npy_isfinite, npy_signbit# + **/ ++ ++/**begin repeat2 ++ * #ISA = , _avx512_skx# ++ * #isa = simd, avx512_skx# ++ **/ + NPY_NO_EXPORT void +-@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) ++@TYPE@_@kind@@ISA@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) + { +- if (!run_@kind@_simd_@TYPE@(args, dimensions, steps)) { ++ if (!run_@kind@_@isa@_@TYPE@(args, dimensions, steps)) { + UNARY_LOOP { + const @type@ in1 = *(@type@ *)ip1; + *((npy_bool *)op1) = @func@(in1) != 0; +@@ -1874,6 +1879,7 @@ NPY_NO_EXPORT void + } + npy_clear_floatstatus_barrier((char*)dimensions); + } ++/**end repeat2**/ + /**end repeat1**/ + + NPY_NO_EXPORT void +diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src +index 50a7ccfee..b63d442ef 100644 +--- a/numpy/core/src/umath/loops.h.src ++++ b/numpy/core/src/umath/loops.h.src +@@ -274,8 +274,13 @@ NPY_NO_EXPORT void + * #kind = isnan, isinf, isfinite, signbit, copysign, nextafter, spacing# + * #func = npy_isnan, npy_isinf, npy_isfinite, npy_signbit, npy_copysign, nextafter, spacing# + **/ ++ ++/**begin repeat2 ++ * #ISA = , _avx512_skx# ++ **/ + NPY_NO_EXPORT void +-@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); ++@TYPE@_@kind@@ISA@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); ++/**end repeat2**/ + /**end repeat1**/ + + /**begin repeat1 +diff --git a/numpy/core/src/umath/simd.inc.src b/numpy/core/src/umath/simd.inc.src +index b28c63930..a5308d83a 100644 +--- a/numpy/core/src/umath/simd.inc.src ++++ b/numpy/core/src/umath/simd.inc.src +@@ -1,4 +1,4 @@ +-/* -*- c -*- */ ++ + + /* + * This file is for the definitions of simd vectorized operations. +@@ -295,6 +295,40 @@ run_binary_avx512f_@func@_@TYPE@(char **args, npy_intp const *dimensions, npy_in + } + + ++/**end repeat1**/ ++/**end repeat**/ ++ ++/**begin repeat ++ * #type = npy_float, npy_double, npy_longdouble# ++ * #TYPE = FLOAT, DOUBLE, LONGDOUBLE# ++ * #EXISTS = 1, 1, 0# ++ */ ++ ++/**begin repeat1 ++ * #func = isnan, isfinite, isinf, signbit# ++ */ ++ ++#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS && @EXISTS@ ++static NPY_INLINE NPY_GCC_TARGET_AVX512_SKX void ++AVX512_SKX_@func@_@TYPE@(npy_bool*, @type@*, const npy_intp n, const npy_intp stride); ++#endif ++ ++static NPY_INLINE int ++run_@func@_avx512_skx_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) ++{ ++#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS && @EXISTS@ ++ if (IS_OUTPUT_BLOCKABLE_UNARY(sizeof(npy_bool), 64)) { ++ AVX512_SKX_@func@_@TYPE@((npy_bool*)args[1], (@type@*)args[0], dimensions[0], steps[0]); ++ return 1; ++ } ++ else { ++ return 0; ++ } ++#endif ++ return 0; ++} ++ ++ + /**end repeat1**/ + /**end repeat**/ + +@@ -1973,6 +2007,84 @@ static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@d + #endif + /**end repeat**/ + ++/**begin repeat ++ * #type = npy_float, npy_double# ++ * #TYPE = FLOAT, DOUBLE# ++ * #num_lanes = 16, 8# ++ * #vsuffix = ps, pd# ++ * #mask = __mmask16, __mmask8# ++ * #vtype = __m512, __m512d# ++ * #scale = 4, 8# ++ * #vindextype = __m512i, __m256i# ++ * #vindexload = _mm512_loadu_si512, _mm256_loadu_si256# ++ * #episize = epi32, epi64# ++ */ ++ ++/**begin repeat1 ++ * #func = isnan, isfinite, isinf, signbit# ++ * #IMM8 = 0x81, 0x99, 0x18, 0x04# ++ * #is_finite = 0, 1, 0, 0# ++ * #is_signbit = 0, 0, 0, 1# ++ */ ++#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS ++static NPY_INLINE NPY_GCC_TARGET_AVX512_SKX void ++AVX512_SKX_@func@_@TYPE@(npy_bool* op, @type@* ip, const npy_intp array_size, const npy_intp steps) ++{ ++ const npy_intp stride_ip = steps/(npy_intp)sizeof(@type@); ++ npy_intp num_remaining_elements = array_size; ++ ++ @mask@ load_mask = avx512_get_full_load_mask_@vsuffix@(); ++#if @is_signbit@ ++ @vtype@ signbit = _mm512_set1_@vsuffix@(-0.0); ++#endif ++ ++ /* ++ * Note: while generally indices are npy_intp, we ensure that our maximum ++ * index will fit in an int32 as a precondition for this function via ++ * IS_OUTPUT_BLOCKABLE_UNARY ++ */ ++ ++ npy_int32 index_ip[@num_lanes@]; ++ for (npy_int32 ii = 0; ii < @num_lanes@; ii++) { ++ index_ip[ii] = ii*stride_ip; ++ } ++ @vindextype@ vindex_ip = @vindexload@((@vindextype@*)&index_ip[0]); ++ @vtype@ zeros_f = _mm512_setzero_@vsuffix@(); ++ __m512i ones = _mm512_set1_@episize@(1); ++ ++ while (num_remaining_elements > 0) { ++ if (num_remaining_elements < @num_lanes@) { ++ load_mask = avx512_get_partial_load_mask_@vsuffix@( ++ num_remaining_elements, @num_lanes@); ++ } ++ @vtype@ x1; ++ if (stride_ip == 1) { ++ x1 = avx512_masked_load_@vsuffix@(load_mask, ip); ++ } ++ else { ++ x1 = avx512_masked_gather_@vsuffix@(zeros_f, ip, vindex_ip, load_mask); ++ } ++#if @is_signbit@ ++ x1 = _mm512_and_@vsuffix@(x1,signbit); ++#endif ++ ++ @mask@ fpclassmask = _mm512_fpclass_@vsuffix@_mask(x1, @IMM8@); ++#if @is_finite@ ++ fpclassmask = _mm512_knot(fpclassmask); ++#endif ++ ++ __m128i out =_mm512_maskz_cvts@episize@_epi8(fpclassmask, ones); ++ _mm_mask_storeu_epi8(op, load_mask, out); ++ ++ ip += @num_lanes@*stride_ip; ++ op += @num_lanes@; ++ num_remaining_elements -= @num_lanes@; ++ } ++} ++#endif ++/**end repeat1**/ ++/**end repeat**/ ++ + /**begin repeat + * #type = npy_float, npy_double# + * #TYPE = FLOAT, DOUBLE# +@@ -2066,8 +2178,8 @@ AVX512F_@func@_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *s + } + } + #endif +-/**end repeat**/ + /**end repeat1**/ ++/**end repeat**/ + + /**begin repeat + * #ISA = FMA, AVX512F# +diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py +index 7caa4c7f9..f208d375e 100644 +--- a/numpy/core/tests/test_umath.py ++++ b/numpy/core/tests/test_umath.py +@@ -771,6 +771,24 @@ class TestSpecialFloats: + for dt in ['f', 'd', 'g']: + assert_raises(FloatingPointError, np.reciprocal, np.array(-0.0, dtype=dt)) + ++class TestFPClass: ++ @pytest.mark.parametrize("stride", [-4,-2,-1,1,2,4]) ++ def test_fpclass(self, stride): ++ arr_f64 = np.array([np.nan, -np.nan, np.inf, -np.inf, -1.0, 1.0, -0.0, 0.0, 2.2251e-308, -2.2251e-308], dtype='d') ++ arr_f32 = np.array([np.nan, -np.nan, np.inf, -np.inf, -1.0, 1.0, -0.0, 0.0, 1.4013e-045, -1.4013e-045], dtype='f') ++ nan = np.array([True, True, False, False, False, False, False, False, False, False]) ++ inf = np.array([False, False, True, True, False, False, False, False, False, False]) ++ sign = np.array([False, True, False, True, True, False, True, False, False, True]) ++ finite = np.array([False, False, False, False, True, True, True, True, True, True]) ++ assert_equal(np.isnan(arr_f32[::stride]), nan[::stride]) ++ assert_equal(np.isnan(arr_f64[::stride]), nan[::stride]) ++ assert_equal(np.isinf(arr_f32[::stride]), inf[::stride]) ++ assert_equal(np.isinf(arr_f64[::stride]), inf[::stride]) ++ assert_equal(np.signbit(arr_f32[::stride]), sign[::stride]) ++ assert_equal(np.signbit(arr_f64[::stride]), sign[::stride]) ++ assert_equal(np.isfinite(arr_f32[::stride]), finite[::stride]) ++ assert_equal(np.isfinite(arr_f64[::stride]), finite[::stride]) ++ + # func : [maxulperror, low, high] + avx_ufuncs = {'sqrt' :[1, 0., 100.], + 'absolute' :[0, -100., 100.], +-- +2.26.2 + diff --git a/build/pkgs/numpy/patches/0002-BUG-Update-compiler-check-for-AVX-512F.patch b/build/pkgs/numpy/patches/0002-BUG-Update-compiler-check-for-AVX-512F.patch new file mode 100644 index 00000000000..08dd874fe47 --- /dev/null +++ b/build/pkgs/numpy/patches/0002-BUG-Update-compiler-check-for-AVX-512F.patch @@ -0,0 +1,43 @@ +From 27d2da99c2390fec567feba3cdb0afa20c810863 Mon Sep 17 00:00:00 2001 +From: Raghuveer Devulapalli +Date: Tue, 14 Jul 2020 13:34:55 -0700 +Subject: [PATCH 2/2] BUG: Update compiler check for AVX-512F + +gcc-4.9 is missing a few AVX-512F intrisics, see +https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61878. We use some of these +missing intrinsics to check for compiler support of AVX-512F. +--- + numpy/core/setup_common.py | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py +index a37514eec..7901770e4 100644 +--- a/numpy/core/setup_common.py ++++ b/numpy/core/setup_common.py +@@ -177,6 +177,9 @@ OPTIONAL_FUNCTION_ATTRIBUTES = [('__attribute__((optimize("unroll-loops")))', + # gcc 4.8.4 support attributes but not with intrisics + # tested via "#include<%s> int %s %s(void *){code; return 0;};" % (header, attribute, name, code) + # function name will be converted to HAVE_ preprocessor macro ++# The _mm512_castps_si512 instruction is specific check for AVX-512F support ++# in gcc-4.9 which is missing a subset of intrinsics. See ++# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61878 + OPTIONAL_FUNCTION_ATTRIBUTES_WITH_INTRINSICS = [('__attribute__((target("avx2,fma")))', + 'attribute_target_avx2_with_intrinsics', + '__m256 temp = _mm256_set1_ps(1.0); temp = \ +@@ -184,11 +187,12 @@ OPTIONAL_FUNCTION_ATTRIBUTES_WITH_INTRINSICS = [('__attribute__((target("avx2,fm + 'immintrin.h'), + ('__attribute__((target("avx512f")))', + 'attribute_target_avx512f_with_intrinsics', +- '__m512 temp = _mm512_set1_ps(1.0)', ++ '__m512i temp = _mm512_castps_si512(_mm512_set1_ps(1.0))', + 'immintrin.h'), + ('__attribute__((target ("avx512f,avx512dq,avx512bw,avx512vl,avx512cd")))', + 'attribute_target_avx512_skx_with_intrinsics', + '__mmask8 temp = _mm512_fpclass_pd_mask(_mm512_set1_pd(1.0), 0x01);\ ++ __m512i temp = _mm512_castps_si512(_mm512_set1_ps(1.0));\ + _mm_mask_storeu_epi8(NULL, 0xFF, _mm_broadcastmb_epi64(temp))', + 'immintrin.h'), + ] +-- +2.26.2 + diff --git a/build/pkgs/numpy/patches/numpy-1.10.2-no-hardcode-blas.patch b/build/pkgs/numpy/patches/numpy-1.10.2-no-hardcode-blas.patch deleted file mode 100644 index 05e10fbbc30..00000000000 --- a/build/pkgs/numpy/patches/numpy-1.10.2-no-hardcode-blas.patch +++ /dev/null @@ -1,36 +0,0 @@ - numpy/distutils/system_info.py | 29 +++++------------------------ - 1 file changed, 5 insertions(+), 24 deletions(-) - -diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py -index d7eb49e..aa62b09 100644 ---- a/numpy/distutils/system_info.py -+++ b/numpy/distutils/system_info.py -@@ -1690,7 +1671,7 @@ class blas_info(system_info): - lib = self.has_cblas(info) - if lib is not None: - info['language'] = 'c' -- info['libraries'] = [lib] -+ info['libraries'] = lib - info['define_macros'] = [('HAVE_CBLAS', None)] - self.set_info(**info) - -@@ -1722,16 +1703,16 @@ class blas_info(system_info): - # check for cblas lib, and if not present check for blas lib. - try: - c.link_executable(obj, os.path.join(tmpdir, "a.out"), -- libraries=["cblas"], -+ libraries=info["libraries"], - library_dirs=info['library_dirs'], - extra_postargs=info.get('extra_link_args', [])) -- res = "cblas" -+ res = info["libraries"] - except distutils.ccompiler.LinkError: - c.link_executable(obj, os.path.join(tmpdir, "a.out"), - libraries=["blas"], - library_dirs=info['library_dirs'], - extra_postargs=info.get('extra_link_args', [])) -- res = "blas" -+ res = ["blas"] - except distutils.ccompiler.CompileError: - res = None - finally: diff --git a/build/pkgs/numpy/spkg-install b/build/pkgs/numpy/spkg-install.in similarity index 100% rename from build/pkgs/numpy/spkg-install rename to build/pkgs/numpy/spkg-install.in diff --git a/build/pkgs/numpy/spkg-legacy-uninstall b/build/pkgs/numpy/spkg-legacy-uninstall.in similarity index 100% rename from build/pkgs/numpy/spkg-legacy-uninstall rename to build/pkgs/numpy/spkg-legacy-uninstall.in diff --git a/build/pkgs/openblas/SPKG.rst b/build/pkgs/openblas/SPKG.rst new file mode 100644 index 00000000000..d33fde7fb74 --- /dev/null +++ b/build/pkgs/openblas/SPKG.rst @@ -0,0 +1,33 @@ +OpenBLAS +======== + +Description +----------- + +OpenBLAS is an optimized BLAS library based on GotoBLAS2 1.13 BSD +version. + +License +------- + +3-clause BSD license + + +SPKG Repository +--------------- + +GitHub page: https://github.com/xianyi/OpenBLAS + +Releases: https://github.com/xianyi/OpenBLAS/releases + + +Upstream Contact +---------------- + +- OpenBLAS users mailing list: + + https://groups.google.com/forum/#!forum/openblas-users + +- OpenBLAS developers mailing list: + + https://groups.google.com/forum/#!forum/openblas-dev diff --git a/build/pkgs/openblas/SPKG.txt b/build/pkgs/openblas/SPKG.txt deleted file mode 100644 index b152a3007ec..00000000000 --- a/build/pkgs/openblas/SPKG.txt +++ /dev/null @@ -1,23 +0,0 @@ -= OpenBLAS = - -== Description == - -OpenBLAS is an optimized BLAS library based on GotoBLAS2 1.13 BSD version. - -== License == - -3-clause BSD license - -== SPKG Repository == - -GitHub page: https://github.com/xianyi/OpenBLAS - -Releases: https://github.com/xianyi/OpenBLAS/releases - -== Upstream Contact == - -* OpenBLAS users mailing list: - https://groups.google.com/forum/#!forum/openblas-users - -* OpenBLAS developers mailing list: - https://groups.google.com/forum/#!forum/openblas-dev diff --git a/build/pkgs/openblas/checksums.ini b/build/pkgs/openblas/checksums.ini index effd8e4dfee..d4ea4b200a5 100644 --- a/build/pkgs/openblas/checksums.ini +++ b/build/pkgs/openblas/checksums.ini @@ -1,4 +1,5 @@ tarball=openblas-VERSION.tar.gz -sha1=b5c21c53f6d6b787768f3499ce14e68fe29803c4 -md5=8a110a25b819a4b94e8a9580702b6495 -cksum=1630729677 +sha1=05eb2d08f656dd4cc962f1bcf78b77cc17b22ef2 +md5=28cc19a6acbf636f5aab5f10b9a0dfe1 +cksum=755980968 +upstream_url=https://github.com/xianyi/OpenBLAS/archive/vVERSION.tar.gz diff --git a/build/pkgs/openblas/distros/conda.txt b/build/pkgs/openblas/distros/conda.txt index a3095baabce..1415da09e37 100644 --- a/build/pkgs/openblas/distros/conda.txt +++ b/build/pkgs/openblas/distros/conda.txt @@ -1,2 +1,2 @@ openblas -blas=*=openblas +blas=2.*=openblas diff --git a/build/pkgs/openblas/distros/cygwin.txt b/build/pkgs/openblas/distros/cygwin.txt new file mode 100644 index 00000000000..d2146131e38 --- /dev/null +++ b/build/pkgs/openblas/distros/cygwin.txt @@ -0,0 +1 @@ +liblapack-devel libopenblas diff --git a/build/pkgs/openblas/distros/gentoo.txt b/build/pkgs/openblas/distros/gentoo.txt new file mode 100644 index 00000000000..397f4e33e1a --- /dev/null +++ b/build/pkgs/openblas/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/openblas diff --git a/build/pkgs/openblas/distros/homebrew.txt b/build/pkgs/openblas/distros/homebrew.txt new file mode 100644 index 00000000000..dcc5d064110 --- /dev/null +++ b/build/pkgs/openblas/distros/homebrew.txt @@ -0,0 +1 @@ +openblas diff --git a/build/pkgs/openblas/package-version.txt b/build/pkgs/openblas/package-version.txt index 8eb680e8f51..940ac09aa67 100644 --- a/build/pkgs/openblas/package-version.txt +++ b/build/pkgs/openblas/package-version.txt @@ -1 +1 @@ -0.3.6.p0 +0.3.9 diff --git a/build/pkgs/openblas/spkg-check b/build/pkgs/openblas/spkg-check deleted file mode 100644 index 813f304a272..00000000000 --- a/build/pkgs/openblas/spkg-check +++ /dev/null @@ -1,38 +0,0 @@ -cd src - -# OpenBlas has no proper configure script -# Options are directly passed to make -# And the name static library archive produced depends on them -# And the tests directly link to that archive rather than through a symlink -# Therefore the following is copied from spkg-install -# We could also patch the Makefile to use a generic symlink pointing -# to the archive with a specific name - -if [ `sage-system-python -c "from __future__ import print_function; import platform; print(platform.architecture()[0])"` = "32bit" ]; then - OPENBLAS_CONFIGURE="$OPENBLAS_CONFIGURE BINARY=32" -fi - -if [ "x$SAGE_FAT_BINARY" = "xyes" ]; then - # See https://github.com/xianyi/OpenBLAS/issues/510 - OPENBLAS_CONFIGURE="$OPENBLAS_CONFIGURE TARGET=PRESCOTT" -fi - -echo "Running OpenBLAS testsuite" - -sdh_make tests $OPENBLAS_CONFIGURE -if [ $? -ne 0 ]; then - # First make sure we already didn't set a target - if [[ $OPENBLAS_CONFIGURE == *"TARGET"* ]]; then - sdh_die "Error while running the OpenBlas testsuite ... exiting" - else - # The recommended TARGET is ATOM if CPU fails - # See https://github.com/xianyi/OpenBLAS/issues/1204 - OPENBLAS_CONFIGURE="$OPENBLAS_CONFIGURE TARGET=ATOM" - echo "Error while testing the OpenBLAS testsuite" - echo "Retrying the OpenBAS testsuit with TARGET=ATOM" - sdh_make tests $OPENBLAS_CONFIGURE - if [ $? -ne 0 ]; then - sdh_die "Error while running the OpenBLAS testsuite ... exiting" - fi - fi -fi diff --git a/build/pkgs/openblas/spkg-check.in b/build/pkgs/openblas/spkg-check.in new file mode 100644 index 00000000000..dc49cbf6ee3 --- /dev/null +++ b/build/pkgs/openblas/spkg-check.in @@ -0,0 +1,33 @@ +cd src + +# OpenBlas has no proper configure script +# Options are directly passed to make +# And the name static library archive produced depends on them +# And the tests directly link to that archive rather than through a symlink +# Therefore the following is copied from spkg-install +# We could also patch the Makefile to use a generic symlink pointing +# to the archive with a specific name + +if [ `sage-system-python -c "from __future__ import print_function; import platform; print(platform.architecture()[0])"` = "32bit" ]; then + OPENBLAS_CONFIGURE="$OPENBLAS_CONFIGURE BINARY=32" +fi + +if [ "x$SAGE_FAT_BINARY" = "xyes" ]; then + # See https://github.com/xianyi/OpenBLAS/issues/510 + OPENBLAS_CONFIGURE="$OPENBLAS_CONFIGURE TARGET=PRESCOTT" +fi + +${MAKE:-make} tests $OPENBLAS_CONFIGURE +if [ $? -ne 0 ]; then + # First make sure we already didn't set a target + if [[ $OPENBLAS_CONFIGURE == *"TARGET"* ]]; then + sdh_die "Failures while running the OpenBLAS testsuite ... exiting" + else + # The recommended TARGET is ATOM if CPU fails + # See https://github.com/xianyi/OpenBLAS/issues/1204 + OPENBLAS_CONFIGURE="$OPENBLAS_CONFIGURE TARGET=ATOM" + echo "Failures while testing the OpenBLAS testsuite" + echo "Retrying the OpenBLAS testsuite with TARGET=ATOM" + ${MAKE:-make} tests $OPENBLAS_CONFIGURE + fi +fi diff --git a/build/pkgs/openblas/spkg-configure.m4 b/build/pkgs/openblas/spkg-configure.m4 index c37db649bed..912248c474c 100644 --- a/build/pkgs/openblas/spkg-configure.m4 +++ b/build/pkgs/openblas/spkg-configure.m4 @@ -1,38 +1,121 @@ SAGE_SPKG_CONFIGURE([openblas], [ + dnl CHECK SAGE_SPKG_DEPCHECK([gfortran], [ - PKG_CHECK_MODULES([OPENBLAS], [openblas >= 0.2.20], [ + SAVE_LIBS="$LIBS" + SAVE_CFLAGS="$CFLAGS" + m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MAJOR], [0]) + m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MINOR], [2]) + m4_pushdef([SAGE_OPENBLAS_MIN_VERSION_MICRO], [20]) + m4_pushdef([SAGE_OPENBLAS_MIN_VERSION], [SAGE_OPENBLAS_MIN_VERSION_MAJOR.SAGE_OPENBLAS_MIN_VERSION_MINOR.SAGE_OPENBLAS_MIN_VERSION_MICRO]) + PKG_CHECK_MODULES([OPENBLAS], [openblas >= ]SAGE_OPENBLAS_MIN_VERSION, [ + LIBS="$OPENBLAS_LIBS $LIBS" + CFLAGS="$OPENBLAS_CFLAGS $CFLAGS" PKG_CHECK_VAR([OPENBLASPCDIR], [openblas], [pcfiledir], [ sage_install_blas_pc=yes - AC_SEARCH_LIBS([cblas_dgemm], [openblas], [dnl openblas works as cblas + AC_CHECK_FUNC([cblas_dgemm], [dnl openblas works as cblas sage_install_cblas_pc=yes ], [ - dnl openblas does not work as cblas; try to use system's cblas as is + dnl openblas does not work as cblas; try to use system cblas as is PKG_CHECK_MODULES([CBLAS], [cblas], [], [sage_spkg_install_openblas=yes]) ]) - AC_FC_FREEFORM([AC_FC_FUNC([dgeqrf])]) - AC_SEARCH_LIBS([$dgeqrf], [openblas], [dnl openblas works as lapack + dnl Check all name manglings that AC_FC_FUNC could check based on the + dnl characteristics of the Fortran compiler + m4_foreach([dgeqrf_mangled], [dgeqrf, dgeqrf_, DGEQRF, DGEQRF_], [ + AC_CHECK_FUNC(dgeqrf_mangled, [ + AS_VAR_SET([HAVE_DGEQRF], [yes]) + ]) + ]) + AS_IF([test x$HAVE_DGEQRF = xyes], [dnl openblas works as lapack sage_install_lapack_pc=yes ], [ - dnl openblas does not work as lapack; try to use system's lapack as is + dnl openblas does not work as lapack; try to use system lapack as is PKG_CHECK_MODULES([LAPACK], [lapack], [], [sage_spkg_install_openblas=yes]) ]) ], [ AC_MSG_WARN([Unable to locate the directory of openblas.pc. This should not happen!]) sage_spkg_install_openblas=yes ]) - ], [sage_spkg_install_openblas=yes]) - AS_IF([test x$sage_spkg_install_openblas != xyes], [ - m4_foreach([blaslibnam], [blas, cblas, lapack], [ - AS_IF([test x$sage_install_]blaslibnam[_pc = xyes], [ - AC_CONFIG_LINKS([$SAGE_SRC/lib/pkgconfig/]blaslibnam[.pc:$OPENBLASPCDIR/openblas.pc])]) - ]) + AS_IF([test x$sage_spkg_install_openblas != xyes], [ + AC_SUBST([SAGE_SYSTEM_FACADE_PC_FILES]) + AC_SUBST([SAGE_OPENBLAS_PC_COMMAND], ["\$(LN) -sf \"$OPENBLASPCDIR/openblas.pc\" \"\$(@)\""]) + m4_foreach([blaslibnam], [blas, cblas, lapack], [ + AS_IF([test x$sage_install_]blaslibnam[_pc = xyes], [ + AS_VAR_APPEND([SAGE_SYSTEM_FACADE_PC_FILES], [" \$(SAGE_PKGCONFIG)/]blaslibnam[.pc"]) + ]) + ]) + ]) + ], [ + dnl No openblas.pc + AS_CASE([$host], + [*-*-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. + AC_SEARCH_LIBS([openblas_get_config], [openblas cblas blas], [ + AS_IF([test x"$ac_cv_search_openblas_get_config" != x"none required"], [ + AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_openblas_get_config "]) + ]) + AC_MSG_CHECKING([whether openblas_get_config indicates version >= ]SAGE_OPENBLAS_MIN_VERSION) + AC_LANG_PUSH([C]) + AC_RUN_IFELSE([ + AC_LANG_PROGRAM([[#include + char *openblas_get_config(void); + int version[3]; ]], + [[version[0] = version[1] = version[2] = 0; + /*printf("%s", openblas_get_config());*/ + if (sscanf(openblas_get_config(), "OpenBLAS %d.%d.%d", + version, version+1, version+2) < 1) + return 1; + if ( 10000 * version[0] + + 100 * version[1] + + version[2] + < 10000 * ]]SAGE_OPENBLAS_MIN_VERSION_MAJOR[[ + + 100 * ]]SAGE_OPENBLAS_MIN_VERSION_MINOR[[ + + ]]SAGE_OPENBLAS_MIN_VERSION_MICRO[[) + return 1;]]) + ], [AS_VAR_SET([HAVE_OPENBLAS], [yes])], [AS_VAR_SET([HAVE_OPENBLAS], [no])]) + AC_LANG_POP([C]) + AC_MSG_RESULT([$HAVE_OPENBLAS]) + ]) + ]) + AC_SEARCH_LIBS([cblas_dgemm], [openblas cblas blas], [ + AS_VAR_SET([HAVE_CBLAS_DGEMM], [yes]) + AS_IF([test x"$ac_cv_search_cblas_dgemm" != x"none required"], [ + AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_cblas_dgemm "]) + ]) + ], [], [-lgfortran]) + m4_foreach([dgeqrf_mangled], [dgeqrf, dgeqrf_, DGEQRF, DGEQRF_], [ + AC_SEARCH_LIBS(dgeqrf_mangled, [openblas lapack], [ + AS_VAR_SET([HAVE_DGEQRF], [yes]) + AS_IF([test x"$ac_cv_search_]dgeqrf_mangled[" != x"none required"], [ + AS_VAR_APPEND([OPENBLAS_LIBS], ["$ac_cv_search_]dgeqrf_mangled[ "]) + ]) + ], [], [-lgfortran]) + ]) + AS_IF([test x"$HAVE_OPENBLAS" = xyes -a x"$HAVE_CBLAS_DGEMM" = xyes -a x"$HAVE_DGEQRF" = xyes], [ + AC_SUBST([OPENBLAS_LIBS]) + AC_SUBST([SAGE_SYSTEM_FACADE_PC_FILES]) + AC_SUBST([SAGE_OPENBLAS_PC_COMMAND], [" (echo \"Name: openblas\"; echo \"Description: OpenBLAS\"; echo \"Version: 0.3\"; echo \"Libs: $OPENBLAS_LIBS\") > \"\$(@)\""]) + m4_foreach([blaslibnam], [openblas, blas, cblas, lapack], [ + AS_VAR_APPEND([SAGE_SYSTEM_FACADE_PC_FILES], [" \$(SAGE_PKGCONFIG)/]blaslibnam[.pc"]) + ]) + ], [ + dnl No system BLAS found + sage_spkg_install_openblas=yes + ]) ]) + LIBS="$SAVE_LIBS" + CFLAGS="$SAVE_CFLAGS" ]) ], [ + dnl REQUIRED-CHECK AS_IF([test "x$with_blas" = xopenblas], [ sage_require_openblas=yes sage_require_atlas=no]) ], [ + dnl PRE AC_MSG_CHECKING([BLAS library]) AC_ARG_WITH([blas], [AS_HELP_STRING([--with-blas=openblas], diff --git a/build/pkgs/openblas/spkg-install b/build/pkgs/openblas/spkg-install.in similarity index 100% rename from build/pkgs/openblas/spkg-install rename to build/pkgs/openblas/spkg-install.in diff --git a/build/pkgs/openssl/SPKG.rst b/build/pkgs/openssl/SPKG.rst new file mode 100644 index 00000000000..06dbe35255a --- /dev/null +++ b/build/pkgs/openssl/SPKG.rst @@ -0,0 +1,28 @@ +OpenSSL +======= + +Description +----------- + +From wikipedia: OpenSSL is an open source implementation of the SSL and +TLS protocols. The core library (written in the C programming language) +implements the basic cryptographic functions and provides various +utility functions. Wrappers allowing the use of the OpenSSL library in a +variety of computer languages are available. + +License +------- + +- Custom GPL-incompatible license + + +Upstream Contact +---------------- + +- http://openssl.org/ +- http://openssl.org/support/community.html + +Patches +~~~~~~~ + +- src/config: patched to fix a problem on Solaris. diff --git a/build/pkgs/openssl/SPKG.txt b/build/pkgs/openssl/SPKG.txt deleted file mode 100644 index 0711b536f7b..00000000000 --- a/build/pkgs/openssl/SPKG.txt +++ /dev/null @@ -1,23 +0,0 @@ -= OpenSSL = - -== Description == - -From wikipedia: OpenSSL is an open source implementation of the SSL -and TLS protocols. The core library (written in the C programming -language) implements the basic cryptographic functions and provides -various utility functions. Wrappers allowing the use of the OpenSSL -library in a variety of computer languages are available. - -== License == - - * Custom GPL-incompatible license - -== Upstream Contact == - - * http://openssl.org/ - * http://openssl.org/support/community.html - -=== Patches === - - * src/config: patched to fix a problem on Solaris. - diff --git a/build/pkgs/openssl/checksums.ini b/build/pkgs/openssl/checksums.ini index 9376553b983..e28765c03dc 100644 --- a/build/pkgs/openssl/checksums.ini +++ b/build/pkgs/openssl/checksums.ini @@ -1,4 +1,5 @@ tarball=openssl-VERSION.tar.gz -sha1=e9710abf5e95c48ebf47991b10cbb48c09dae102 -md5=4532712e7bcc9414f5bce995e4e13930 -cksum=149755279 +sha1=b213a293f2127ec3e323fb3cfc0c9807664fd997 +md5=76766e98997660138cdaf13a187bd234 +cksum=115576544 +upstream_url=https://www.openssl.org/source/openssl-VERSION.tar.gz diff --git a/build/pkgs/openssl/distros/homebrew.txt b/build/pkgs/openssl/distros/homebrew.txt new file mode 100644 index 00000000000..fa963ae15cb --- /dev/null +++ b/build/pkgs/openssl/distros/homebrew.txt @@ -0,0 +1 @@ +openssl diff --git a/build/pkgs/openssl/distros/slackware.txt b/build/pkgs/openssl/distros/slackware.txt new file mode 100644 index 00000000000..6ed59c09128 --- /dev/null +++ b/build/pkgs/openssl/distros/slackware.txt @@ -0,0 +1 @@ +openssl openssl-solibs diff --git a/build/pkgs/openssl/package-version.txt b/build/pkgs/openssl/package-version.txt index 3e153f5bb62..4ec6413bea4 100644 --- a/build/pkgs/openssl/package-version.txt +++ b/build/pkgs/openssl/package-version.txt @@ -1 +1 @@ -1.1.1b +1.1.1g diff --git a/build/pkgs/openssl/spkg-check b/build/pkgs/openssl/spkg-check deleted file mode 100644 index 3558e893809..00000000000 --- a/build/pkgs/openssl/spkg-check +++ /dev/null @@ -1,15 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -cd src - -echo "Testing openssl..." -$MAKE test - -if [ $? -ne 0 ]; then - echo >&2 "Error running self tests." - exit 1 -fi diff --git a/build/pkgs/openssl/spkg-check.in b/build/pkgs/openssl/spkg-check.in new file mode 100644 index 00000000000..1d233337483 --- /dev/null +++ b/build/pkgs/openssl/spkg-check.in @@ -0,0 +1,3 @@ +cd src + +$MAKE test diff --git a/build/pkgs/openssl/spkg-install b/build/pkgs/openssl/spkg-install.in similarity index 100% rename from build/pkgs/openssl/spkg-install rename to build/pkgs/openssl/spkg-install.in diff --git a/build/pkgs/p_group_cohomology/SPKG.rst b/build/pkgs/p_group_cohomology/SPKG.rst new file mode 100644 index 00000000000..0351bbe752b --- /dev/null +++ b/build/pkgs/p_group_cohomology/SPKG.rst @@ -0,0 +1,115 @@ +p_group_cohomology +================== + +Description +----------- + +Modular Cohomology Rings of Finite Groups + +The package is located at http://users.fmi.uni-jena.de/cohomology/, +that's to say the tarball p_group_cohomology-x.y.tar.xz can be found +there and the documentation of the package is provided at +http://users.fmi.uni-jena.de/cohomology/documentation/ + +License +------- + +Copyright (C) 2018 Simon A. King Copyright (C) +2011 Simon A. King Copyright (C) 2009 Simon A. +King and + + David J. Green + +Distributed under the terms of the GNU General Public License (GPL), +version 2 or later (at your choice). + + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +The full text of the GPL is available at: + + http://www.gnu.org/licenses/ + +The package includes a data base of cohomology rings of the groups of +order 64 and provides access to a data base of cohomology rings of the +groups of order 128 and 243, located at + + http://cohomology.uni-jena.de/db/ + +These data bases are distributed under the Creative Commons +Attribution-Share Alike 3.0 License. The full text of this licence is +available at + + http://creativecommons.org/licenses/by-sa/3.0/ + + +SPKG Maintainers +---------------- + +Simon A. King + + +Upstream Contact +---------------- + +Simon A. King David J. Green + + +Acknowledgements +---------------- + +The development of the initial version of this SPKG was funded by the +German Science Foundation, DFG project GR 1585/4.1, and was accomplished +at the Friedrich Schiller University Jena. + +Since version 1.0.1, the further work on this SPKG was funded by Marie +Curie grant MTKD-CT-2006-042685 and was pursued at the National +University of Ireland, Galway. Since Novermber 2010, it is moved back to +Jena. + +We thank William Stein for giving us access to various computers on +which we could build test the SPKG and on which some huge computations +could be completed, and acknowledge the support by National Science +Foundation Grant No. DMS-0821725. + +We thank Mathieu Dutour Sikirić for hints on how to use GAP more +efficiently. + +We owe Peter Symonds the idea of using the Poincaré series in a rather +efficient completeness criterion. + +We are greatful to John Palmieri for his help on making +p_group_cohomology work with python-3. + +Dependencies +------------ + +- The SharedMeatAxe needs to be installed, as a build time dependency. + + This can be met by installing the meataxe spkg + +Testing +------- + +Our package provides a very short test suite for David Green's routines +for the computation of minimal projective resolutions. The majority of +this package's tests is formed by doc tests in the Cython code. In fact, +any class, method and function is covered by tests. + +Note that internet access is required for these tests, as it is +attempted to download cohomology rings from a public data base in the +web. + +The script ``spkg-check`` calls ``sage -t --force_lib`` on the files +in ``pGroupCohomology``. + +Documentation +------------- + +The documentation of this package is automatically built, if the +environment variable SAGE_SPKG_INSTALL_DOCS is yes (do "export +SAGE_SPKG_INSTALL_DOCS=yes" on the command line before installation). +The documents are put into +SAGE_ROOT/local/share/doc/p_group_cohomology/. diff --git a/build/pkgs/p_group_cohomology/SPKG.txt b/build/pkgs/p_group_cohomology/SPKG.txt deleted file mode 100644 index ab586478112..00000000000 --- a/build/pkgs/p_group_cohomology/SPKG.txt +++ /dev/null @@ -1,247 +0,0 @@ -= p_group_cohomology = - -== Description == - -Modular Cohomology Rings of Finite Groups - -The package is located at http://users.fmi.uni-jena.de/cohomology/, -that's to say the tarball p_group_cohomology-x.y.tar.xz can be found -there and the documentation of the package is provided at -http://users.fmi.uni-jena.de/cohomology/documentation/ - -== License == - -Copyright (C) 2018 Simon A. King -Copyright (C) 2011 Simon A. King -Copyright (C) 2009 Simon A. King and - David J. Green - -Distributed under the terms of the GNU General Public License (GPL), -version 2 or later (at your choice). - - This code is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - -The full text of the GPL is available at: - - http://www.gnu.org/licenses/ - -The package includes a data base of cohomology rings of the groups of -order 64 and provides access to a data base of cohomology rings of the -groups of order 128 and 243, located at - http://cohomology.uni-jena.de/db/ -These data bases are distributed under the Creative Commons -Attribution-Share Alike 3.0 License. The full text of this licence is -available at - http://creativecommons.org/licenses/by-sa/3.0/ - -== SPKG Maintainers == - -Simon A. King - -== Upstream Contact == - -Simon A. King -David J. Green - -== Acknowledgements == - -The development of the initial version of this SPKG was funded by -the German Science Foundation, DFG project GR 1585/4.1, and was -accomplished at the Friedrich Schiller University Jena. - -Since version 1.0.1, the further work on this SPKG was funded -by Marie Curie grant MTKD-CT-2006-042685 and was pursued at -the National University of Ireland, Galway. Since Novermber 2010, -it is moved back to Jena. - -We thank William Stein for giving us access to various computers -on which we could build test the SPKG and on which some huge computations -could be completed, and acknowledge the support by National Science -Foundation Grant No. DMS-0821725. - -We thank Mathieu Dutour Sikirić for hints on how to use GAP -more efficiently. - -We owe Peter Symonds the idea of using the Poincaré series in a -rather efficient completeness criterion. - -We are greatful to John Palmieri for his help on making p_group_cohomology -work with python-3. - -== Dependencies == - -- The SharedMeatAxe needs to be installed, as a build time dependency. - This can be met by installing the meataxe spkg - -== Testing == - -Our package provides a very short test suite for David Green's routines -for the computation of minimal projective resolutions. The majority of -this package's tests is formed by doc tests in the Cython code. In -fact, any class, method and function is covered by tests. - -Note that internet access is required for these tests, as it is attempted to -download cohomology rings from a public data base in the web. - -The script ``spkg-check`` calls `sage -t --force_lib` on the files -in `pGroupCohomology`. - -== Documentation == - -The documentation of this package is automatically built, if the environment -variable SAGE_SPKG_INSTALL_DOCS is yes (do "export SAGE_SPKG_INSTALL_DOCS=yes" -on the command line before installation). The documents are put into -SAGE_ROOT/local/share/doc/p_group_cohomology/. - -== Changelog == - - * v3.3 (September 2019): - - Python-3 support. - - Cleaner code and tests for the mechanism that updates moved data. - - Use proper isomorphism tests in unit_test_64. - * v3.2 (Simon King, July 2019): - - Detection of graded non-induced isomorphisms of cohomology rings. - - Easier creation of a cohomology ring from a tower of subgroups. - - Kernels and preimage representatives of induced maps. - - Stop hard-coding the MTXLIB environment variable. - * 3.1 (Simon King, December 2018): - - Hilbert series computation by using a new implementation in SageMath. - - Vastly improved computation of filter degree type (now relying on - Hilbert series). - - Use libgap instead of the GAP pexpect interface. - - Sub-package upgrade: modres-1.1 - * 3.0.1 (Simon King, August 2018): - - Add a routine to compute filter regular parameters in small degrees - by enumeration. - - Cope with some changes in Singular. - * 3.0 (Simon King, January/February 2018): - - The MeatAxe has been removed from this package and has been replaced - by "SharedMeatAxe", as an external package. - - David Green's C code for the computation of minimal projective resolutions - is now using autotools and is now providing a library. - - The Python/Cython part of this package is now pip installable. - - Remove some experimental options. - - Drop support for Singular versions < 3-1-0 - - Drop the old test script, as `sage -t` now works for this package. - - Drop the old doc builder, as building the docs is now closer to - Sage's documentation than before. - - Cope with an API change in SageMath. - * 2.1.5 (Simon King, Mai 2015): - - Cope with removal of the ._domain attribute of maps and with changed import locations. - - Improved computation of the nil-radical, including degree-wise computation. - - Methods is_nilpotent and nilpotency_degree for cohomology ring elements. - - Improved computation of Poincaré series. - - Hilbert-driven computations for depth and filter degree type. - - For computing depth, only use filter degree type if it has been previously computed. - * 2.1.4 (Simon King, April 2013): - Computational techniques: - - find_small_last_parameter will now construct a parameter of the cohomology - ring, by studying the restriction to maximal elementary abelian - subgroups. In previous version, we could only find parameters of the ring - approximation. The additional advantage: The computations are easier, since - the complicated relations of the ring approximation do not need to be - considered. - - Compute a complete Gröbner basis, if there was no relation in the previous - degree. This is an essential speed-up for computing the mod-3 cohomology of - the third Janko group. - - Coping with changes in Sage: - - tmp_filename -> tmp_dir - - SAGE_DATA -> SAGE_SHARE - - Replace double underscore by single underscore attributes, to avoid name - mangling - - Change tests according to GAP's changed random generator - - Miscellaneae: - - Increase optimization level. If the gcc version is too old, David - Green's programs won't work in this optimization level. However, - functionality will be tested before finishing installation of the package. - - Remove the indentation parameter of RESL. The protocol output first prints - a short descriptor of the instance whose methods are being called (the descriptor - is only printed when the active instance changes). - - Use utilities from os.path, for better portability - - Unlink symbolic links before saving data - - Use urllib2 - - Some methods changed from temporary_result to permanent_result. But old data - remain legible. Most important example: Construction of parameters. - - Address of Simon King changed from Galway to Jena - * 2.1.3 (Simon King, July 2012): - - Improve the heuristic of choosing between Hilbert-Poincaré and Symonds - criteria. If the computation of parameters in small degrees by lifting - the Dickson invariants using elimination seems too difficult - to the user, then this computation can be interrupted with - Ctrl-c, and then other completion tests (perhaps in higher - degree) are automatically attempted, without the need of further - manual intervention. - - Cope with Cython's new name mangling, by avoiding double underscore - attributes. - - If a "permanent result" is indexed by data in the Gap interface, - then these results can now be pickled, provided that the data - in Gap can be reconstructed from string representation. - - Use a lower bound for the depth, if the actual depth is too difficult - to obtain. - - Switch the public web repository to a new location. - - Fix the creation of symbolic links from a private data base to - a public data base. - - Fix comparison of MTX matrices (comparison with None used to fail). - * 2.1.2 (Simon King, March 2012): - - Some fixes needed with the new version of Cython used by sage-5.0. - - Some fixes needed with Singular 3-1-3. - - Using the coercion framework in a better way. - - Small improvements in the MeatAxe code. - - Include the docs in the spkg. - - Improved construction of dependent parameters for Symonds' test. - * 2.1.1 (Simon King, September 2010): - - Cohomology data are now by default only created in the private - database. - - Data in the public database are accessed via symbolic links - - Code restructured: The cohomology ring constructor is modularised. - - Parallel testing now only if the patch of ticket #10004 is applied. - * 2.1 (Simon King, September 2010): - - Full doctest coverage and a parallel test script. - - Cleaning up code in order to reduce the number of compiler - warnings. - - Builds and tests on little and big endian machines. - - Uses features of Singular-3-1-1, but still works with - Singular-3-1-0. - - Support for setting random seeds. If the same random seed is - used, the resulting ring presentation is computationally - unique and machine independent. - - Kernels/preimages of induced homomorphisms; Essential and Depth - Essential ideal. - - Decorators for methods that cache results that may change if the - ring structure changes, resp. that cache results that will not - change once computed. The cached results are preserved under - pickling. KeyboardInterrupts are cached as well, but a re-computation - can be forced. - - Improved use of the Symonds and the Hilbert-Poincaré criteria, using - algebraically *dependent* parameters. - * 2.0 (Simon King, April 2010): - - Modular cohomology rings for *any* finite groups (not just for - p-groups). This is implemented in a new module - pGroupCohomology.modular_cohomology, that builds on top of the - old pGroupCohomology.cohomology module. - - The build process now uses environment variables such as $MAKE - or $MKDIR, in order to increase portability. - * 1.2.p0 (Dima Pasechnik and Simon King, March 2010): - - Adding .hgignore (ignoring src/db and mtxoriginal). - - Adding a robuster test for the existence of the SmallGroups library. - * 1.2 (Simon King, October 2009): - - Modified printing for cocycles - - Minor bug fixes and code improvements. - - The data base at sage.math has moved. - - New: Persistent Group Cohomology (bar codes), based on ideas of Graham - Ellis and Simon King. - * 1.1 (Simon King August 2009): - - Yoneda cocomplex - - Restricted Massey powers and general Massey products. - * 1.0.2 (Simon King, July 2009): - - Fixing a computation time regression and two minor bugs. - - Changing Simon King's email address - * 1.0.1 (Simon King, July 2009): - - Licensing GPL 2 or later - * 1.0 (Simon King and David Green July 2009): - - First public version diff --git a/build/pkgs/p_group_cohomology/dependencies b/build/pkgs/p_group_cohomology/dependencies index 1b6f9049929..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 | pip 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/p_group_cohomology/spkg-check b/build/pkgs/p_group_cohomology/spkg-check.in similarity index 100% rename from build/pkgs/p_group_cohomology/spkg-check rename to build/pkgs/p_group_cohomology/spkg-check.in diff --git a/build/pkgs/p_group_cohomology/spkg-install b/build/pkgs/p_group_cohomology/spkg-install.in similarity index 100% rename from build/pkgs/p_group_cohomology/spkg-install rename to build/pkgs/p_group_cohomology/spkg-install.in diff --git a/build/pkgs/packaging/SPKG.rst b/build/pkgs/packaging/SPKG.rst new file mode 100644 index 00000000000..ad493c1202e --- /dev/null +++ b/build/pkgs/packaging/SPKG.rst @@ -0,0 +1,7 @@ +packaging +========= + +Description +----------- + +Core utilities for Python packages diff --git a/build/pkgs/packaging/SPKG.txt b/build/pkgs/packaging/SPKG.txt deleted file mode 100644 index 617de3d95a5..00000000000 --- a/build/pkgs/packaging/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= packaging = - -== Description == - -Core utilities for Python packages diff --git a/build/pkgs/packaging/dependencies b/build/pkgs/packaging/dependencies index 30abd8839cd..256333c03d8 100644 --- a/build/pkgs/packaging/dependencies +++ b/build/pkgs/packaging/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip pyparsing six +$(PYTHON) | $(PYTHON_TOOLCHAIN) pyparsing six ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/itsdangerous/spkg-install b/build/pkgs/packaging/spkg-install.in similarity index 100% rename from build/pkgs/itsdangerous/spkg-install rename to build/pkgs/packaging/spkg-install.in diff --git a/build/pkgs/palp/SPKG.rst b/build/pkgs/palp/SPKG.rst new file mode 100644 index 00000000000..d9304000e3b --- /dev/null +++ b/build/pkgs/palp/SPKG.rst @@ -0,0 +1,42 @@ +PALP +==== + +Description +----------- + +A Package for Analyzing Lattice Polytopes (PALP) is a set of C programs +for calculations with lattice polytopes and applications to toric +geometry. + +It contains routines for vertex and facet enumeration, computation of +incidences and symmetries, as well as completion of the set of lattice +points in the convex hull of a given set of points. In addition, there +are procedures specialised to reflexive polytopes such as the +enumeration of reflexive subpolytopes, and applications to toric +geometry and string theory, like the computation of Hodge data and +fibration structures for toric Calabi-Yau varieties. The package is well +tested and optimised in speed as it was used for time consuming tasks +such as the classification of reflexive polyhedra in 4 dimensions and +the creation and manipulation of very large lists of 5-dimensional +polyhedra. + +While originally intended for low-dimensional applications, the +algorithms work in any dimension and our key routine for vertex and +facet enumeration compares well with existing packages. + +License +------- + +- When released, GPL 2 was in force. +- There is a link to a web page, which now points to GPL 3, but would + have pointed to GPL 2 at the time the package was released. + +- Therefore one can deduce the authors were happy for this to be + released under GPL 2 or a later version. + + +Upstream Contact +---------------- + +- Author: Harald Skarke (skarke@maths.ox.ac.uk) +- Home page: http://hep.itp.tuwien.ac.at/~kreuzer/CY/CYpalp.html diff --git a/build/pkgs/palp/SPKG.txt b/build/pkgs/palp/SPKG.txt deleted file mode 100644 index fa55828a258..00000000000 --- a/build/pkgs/palp/SPKG.txt +++ /dev/null @@ -1,78 +0,0 @@ -= PALP = - -== Description == - -A Package for Analyzing Lattice Polytopes (PALP) is a set of C -programs for calculations with lattice polytopes and applications to -toric geometry. - -It contains routines for vertex and facet enumeration, computation of -incidences and symmetries, as well as completion of the set of lattice -points in the convex hull of a given set of points. In addition, there -are procedures specialised to reflexive polytopes such as the -enumeration of reflexive subpolytopes, and applications to toric -geometry and string theory, like the computation of Hodge data and -fibration structures for toric Calabi-Yau varieties. The package is -well tested and optimised in speed as it was used for time consuming -tasks such as the classification of reflexive polyhedra in 4 -dimensions and the creation and manipulation of very large lists of -5-dimensional polyhedra. - -While originally intended for low-dimensional applications, the -algorithms work in any dimension and our key routine for vertex and -facet enumeration compares well with existing packages. - -== License == - - * When released, GPL 2 was in force. - * There is a link to a web page, which now points to GPL 3, but would - have pointed to GPL 2 at the time the package was released. - * Therefore one can deduce the authors were happy for this to be - released under GPL 2 or a later version. - -== Upstream Contact == - - * Author: Harald Skarke (skarke@maths.ox.ac.uk) - * Home page: http://hep.itp.tuwien.ac.at/~kreuzer/CY/CYpalp.html - -== Change log == - -=== palp-2.1.p1 (Dima Pasechnik, 27 January 2013) === - * #13960: as proposed by J.-P. Flori; set the stack size to 8MB - for Cygwin. - -=== palp-2.1.p0 (Volker Braun, 4th June 2012) === - * #12088 Updated to the latest upstream version - * Solaris sed does not understand character classes (:space:), dumb - down even further - * Removed the patched Polynf.c and MoriCone.c - -=== palp-2.0.p2 (Volker Braun, 4th May 2012) === - * #12088 change regex in spkg-install from \s to [[:space:]], only - the latter works on OSX - -=== palp-2.0.p1 (R. Andrew Ohana, 26th February 2012) === - * #7071 Make spkg respect global CC and CFLAGS variables - * made SAGE64 set the '-m64' flag the "proper" way - -=== palp-2.0.p0 (Volker Braun, 19th November 2011) === - * #12055 Update to new upstream version - * Now building multiple versions for different ambient dimensions - * Patched Polynf.c and MoriCone.c that do not use nested functions - (private communication with Harald Skarke) - -=== palp-1.1.p3 (David Kirkby, 24th May 2010) === - * #9025 Add compiler option -m64 if the variable SAGE64 is set to - "yes". The flag is added by 'sed'. - -=== palp-1.1.p2 (Mitesh Patel, 12 Mar 2010) === - - * #8477: Work around apparent GNU make problem when building spkgs in - parallel. - * SPKG.txt cleanup. The description is based on the abstract of this - paper: https://arxiv.org/abs/math/0204356 - -=== palp-1.1.p1 (Tim Abbott, William Stein) === - - * Debian packaging. See 'hg log'. - * Lost to history. diff --git a/build/pkgs/palp/checksums.ini b/build/pkgs/palp/checksums.ini index 28d9e0048e4..5de0d462b06 100644 --- a/build/pkgs/palp/checksums.ini +++ b/build/pkgs/palp/checksums.ini @@ -1,4 +1,5 @@ -tarball=palp-VERSION.tar.bz2 -sha1=2d13769d0d1639adbe23d00147506d15f490102c -md5=4c4ebdb4d56217c4db5c72f5427ab23a -cksum=3324978349 +tarball=palp-VERSION.tar.gz +sha1=99b0d8f7c998549f9f1be6302950659ff01bac77 +md5=b9508b9e08954215c88320d4a5940d91 +cksum=2027098672 +upstream_url=http://hep.itp.tuwien.ac.at/~kreuzer/CY/palp/palp-VERSION.tar.gz diff --git a/build/pkgs/palp/distros/arch.txt b/build/pkgs/palp/distros/arch.txt new file mode 100644 index 00000000000..f037baef346 --- /dev/null +++ b/build/pkgs/palp/distros/arch.txt @@ -0,0 +1 @@ +palp diff --git a/build/pkgs/palp/distros/conda.txt b/build/pkgs/palp/distros/conda.txt new file mode 100644 index 00000000000..f037baef346 --- /dev/null +++ b/build/pkgs/palp/distros/conda.txt @@ -0,0 +1 @@ +palp diff --git a/build/pkgs/palp/distros/debian.txt b/build/pkgs/palp/distros/debian.txt new file mode 100644 index 00000000000..f037baef346 --- /dev/null +++ b/build/pkgs/palp/distros/debian.txt @@ -0,0 +1 @@ +palp diff --git a/build/pkgs/palp/distros/fedora.txt b/build/pkgs/palp/distros/fedora.txt new file mode 100644 index 00000000000..f037baef346 --- /dev/null +++ b/build/pkgs/palp/distros/fedora.txt @@ -0,0 +1 @@ +palp diff --git a/build/pkgs/palp/package-version.txt b/build/pkgs/palp/package-version.txt index 111d1f1c8d6..6a5fe6e8977 100644 --- a/build/pkgs/palp/package-version.txt +++ b/build/pkgs/palp/package-version.txt @@ -1 +1 @@ -2.1.p2 +2.11 diff --git a/build/pkgs/palp/spkg-configure.m4 b/build/pkgs/palp/spkg-configure.m4 new file mode 100644 index 00000000000..f4e293970ec --- /dev/null +++ b/build/pkgs/palp/spkg-configure.m4 @@ -0,0 +1,10 @@ +SAGE_SPKG_CONFIGURE([palp], [ + m4_foreach([palpprog], [[poly], [class], [nef], [cws]], [ + AC_PATH_PROG(PALP[]palpprog, [palpprog.x]) + AS_IF([test "x$PALP[]palpprog" = "x"], [sage_spkg_install_palp=yes]) + m4_foreach([suff], [4, 5, 6, 11], [ + AC_PATH_PROG(PALP[]palpprog[]suff, [palpprog[-]suff[d.x]]) + AS_IF([test "x$PALP[]palpprog[]suff" = "x"], [sage_spkg_install_palp=yes]) + ]) + ]) +]) diff --git a/build/pkgs/palp/spkg-install b/build/pkgs/palp/spkg-install deleted file mode 100644 index c85e0d81f11..00000000000 --- a/build/pkgs/palp/spkg-install +++ /dev/null @@ -1,35 +0,0 @@ -# Move over the GNU makefile to the file 'Makefile'. -mv -f src/GNUmakefile src/Makefile - -cd src -mv Global.h Global.h-template - -CFLAGS="-O3 -W -Wall $CFLAGS -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE" -# On Cygwin the stack size is ridiculously low by default, so that the nef -# executable which allocates everything on the stack for speed reason blows it -if [ "$UNAME" = "CYGWIN" ] ; then - CFLAGS="$CFLAGS -Wl,--stack,8000000" -fi - -BIN="$SAGE_LOCAL/bin" - -for dim in 4 5 6 11; do - echo Building PALP optimized for $dim dimensions - - sed "s/^#define[^a-zA-Z]*POLY_Dmax.*/#define POLY_Dmax $dim/" Global.h-template > Global.h - - sdh_make CC="$CC" CFLAGS="$CFLAGS" - for file in poly class cws nef mori; do - sdh_install -T "${file}.x" "${BIN}/${file}-${dim}d.x" - done - - # the next step is important to avert races on older file systems - # for example, ext3 has 1-second timestamp granularity! - sdh_make cleanall -done - -# symlinks for the default dimension -cd "${SAGE_DESTDIR}${BIN}" -for file in poly class cws nef mori; do - ln -sf ${file}-6d.x ${file}.x -done diff --git a/build/pkgs/palp/spkg-install.in b/build/pkgs/palp/spkg-install.in new file mode 100644 index 00000000000..f594cc307a9 --- /dev/null +++ b/build/pkgs/palp/spkg-install.in @@ -0,0 +1,40 @@ +# Move over the GNU makefile to the file 'Makefile'. +mv -f src/GNUmakefile src/Makefile + +cd src +mv Global.h Global.h-template + +CFLAGS="-O3 -W -Wall $CFLAGS -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE" +# On Cygwin the stack size is ridiculously low by default, so that the nef +# executable which allocates everything on the stack for speed reason blows it +if [ "$UNAME" = "CYGWIN" ] ; then + CFLAGS="$CFLAGS -Wl,--stack,8000000" +fi + +# Undefine NDEBUG. Palp source has assert statements and preprocessing them +# away changes the syntactical meaning of the program. +CPPFLAGS=$(echo "${CPPFLAGS}" | sed "s/-DNDEBUG//g") +CFLAGS=$(echo "${CFLAGS}" | sed "s/-DNDEBUG//g") + +BIN="$SAGE_LOCAL/bin" + +for dim in 4 5 6 11; do + echo Building PALP optimized for $dim dimensions + + sed "s/^#define[^a-zA-Z]*POLY_Dmax.*/#define POLY_Dmax $dim/" Global.h-template > Global.h + + sdh_make CC="$CC" CFLAGS="$CFLAGS" + for file in poly class cws nef mori; do + sdh_install -T "${file}.x" "${BIN}/${file}-${dim}d.x" + done + + # the next step is important to avert races on older file systems + # for example, ext3 has 1-second timestamp granularity! + sdh_make cleanall +done + +# symlinks for the default dimension +cd "${SAGE_DESTDIR}${BIN}" +for file in poly class cws nef mori; do + ln -sf ${file}-6d.x ${file}.x +done diff --git a/build/pkgs/pandoc/SPKG.rst b/build/pkgs/pandoc/SPKG.rst new file mode 100644 index 00000000000..7ef13ccebf2 --- /dev/null +++ b/build/pkgs/pandoc/SPKG.rst @@ -0,0 +1,10 @@ +Pandoc +====== + +Description +----------- + +This script package represents the document converter pandoc. + +We do not have an SPKG for it. The purpose of this script package is to +associate system package lists with it. diff --git a/build/pkgs/pandoc/distros/alpine.txt b/build/pkgs/pandoc/distros/alpine.txt new file mode 100644 index 00000000000..4a59b54c8c8 --- /dev/null +++ b/build/pkgs/pandoc/distros/alpine.txt @@ -0,0 +1 @@ +pandoc diff --git a/build/pkgs/pandoc/distros/arch.txt b/build/pkgs/pandoc/distros/arch.txt new file mode 100644 index 00000000000..4a59b54c8c8 --- /dev/null +++ b/build/pkgs/pandoc/distros/arch.txt @@ -0,0 +1 @@ +pandoc diff --git a/build/pkgs/pandoc/distros/conda.txt b/build/pkgs/pandoc/distros/conda.txt new file mode 100644 index 00000000000..4a59b54c8c8 --- /dev/null +++ b/build/pkgs/pandoc/distros/conda.txt @@ -0,0 +1 @@ +pandoc diff --git a/build/pkgs/pandoc/distros/debian.txt b/build/pkgs/pandoc/distros/debian.txt new file mode 100644 index 00000000000..4a59b54c8c8 --- /dev/null +++ b/build/pkgs/pandoc/distros/debian.txt @@ -0,0 +1 @@ +pandoc diff --git a/build/pkgs/pandoc/distros/fedora.txt b/build/pkgs/pandoc/distros/fedora.txt new file mode 100644 index 00000000000..4a59b54c8c8 --- /dev/null +++ b/build/pkgs/pandoc/distros/fedora.txt @@ -0,0 +1 @@ +pandoc diff --git a/build/pkgs/pandoc/distros/gentoo.txt b/build/pkgs/pandoc/distros/gentoo.txt new file mode 100644 index 00000000000..ddcca4715f4 --- /dev/null +++ b/build/pkgs/pandoc/distros/gentoo.txt @@ -0,0 +1 @@ +app-text/pandoc diff --git a/build/pkgs/pandoc/distros/homebrew.txt b/build/pkgs/pandoc/distros/homebrew.txt new file mode 100644 index 00000000000..4a59b54c8c8 --- /dev/null +++ b/build/pkgs/pandoc/distros/homebrew.txt @@ -0,0 +1 @@ +pandoc diff --git a/build/pkgs/pandoc/distros/opensuse.txt b/build/pkgs/pandoc/distros/opensuse.txt new file mode 100644 index 00000000000..4a59b54c8c8 --- /dev/null +++ b/build/pkgs/pandoc/distros/opensuse.txt @@ -0,0 +1 @@ +pandoc diff --git a/build/pkgs/pandoc/spkg-configure.m4 b/build/pkgs/pandoc/spkg-configure.m4 new file mode 100644 index 00000000000..2363c1a6069 --- /dev/null +++ b/build/pkgs/pandoc/spkg-configure.m4 @@ -0,0 +1,4 @@ +SAGE_SPKG_CONFIGURE([pandoc], [ + AC_PATH_PROG([PANDOC], [pandoc]) + AS_IF([test -z "$ac_cv_path_PANDOC"], [sage_spkg_install_pandoc=yes]) +]) diff --git a/build/pkgs/pandoc/spkg-install b/build/pkgs/pandoc/spkg-install new file mode 100755 index 00000000000..45fa6a3e0a9 --- /dev/null +++ b/build/pkgs/pandoc/spkg-install @@ -0,0 +1,5 @@ +#! /usr/bin/env bash +echo Error: pandoc, a prerequisite of rst2ipynb, is not installed. +echo Please install it manually, for example using the system packages +echo recommended by ./configure. +exit 1 diff --git a/build/pkgs/sagenb/type b/build/pkgs/pandoc/type similarity index 100% rename from build/pkgs/sagenb/type rename to build/pkgs/pandoc/type diff --git a/build/pkgs/pandoc_attributes/SPKG.rst b/build/pkgs/pandoc_attributes/SPKG.rst new file mode 100644 index 00000000000..a7922004186 --- /dev/null +++ b/build/pkgs/pandoc_attributes/SPKG.rst @@ -0,0 +1,35 @@ +pandoc_attributes +================= + +Description +----------- + +This is a simple parser / emitter for pandoc block attributes, intended +for use with pandocfilters. + +License +------- + +BSD 2-Clause License + + +Upstream Contact +---------------- + +- Author: Aaron O'Leary +- Home page: https://github.com/aaren/pandoc-attributes + +Dependencies +------------ + +- Python +- setuptools +- pandocfilters + + +Special Update/Build Instructions +--------------------------------- + +There are no release numbers, hence find the latest commit, download +https://github.com/aaren/pandoc-attributes/archive/${COMMIT}.zip and +rename it pandoc_attributes-${COMMIT:0:8}.zip diff --git a/build/pkgs/pandoc_attributes/SPKG.txt b/build/pkgs/pandoc_attributes/SPKG.txt deleted file mode 100644 index a06869d52f6..00000000000 --- a/build/pkgs/pandoc_attributes/SPKG.txt +++ /dev/null @@ -1,28 +0,0 @@ -= pandoc_attributes = - -== Description == - -This is a simple parser / emitter for pandoc block attributes, intended -for use with pandocfilters. - -== License == - -BSD 2-Clause License - -== Upstream Contact == - -Author: Aaron O'Leary -Home page: https://github.com/aaren/pandoc-attributes - -== Dependencies == - -* Python -* setuptools -* pandocfilters - -== Special Update/Build Instructions == - -There are no release numbers, hence find the latest commit, download -https://github.com/aaren/pandoc-attributes/archive/${COMMIT}.zip and -rename it pandoc_attributes-${COMMIT:0:8}.zip - diff --git a/build/pkgs/pandoc_attributes/dependencies b/build/pkgs/pandoc_attributes/dependencies index 39d602ea819..30b8148c07f 100644 --- a/build/pkgs/pandoc_attributes/dependencies +++ b/build/pkgs/pandoc_attributes/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) setuptools | pip pandocfilters +$(PYTHON) $(PYTHON_TOOLCHAIN) | pip pandocfilters ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pandoc_attributes/spkg-install b/build/pkgs/pandoc_attributes/spkg-install deleted file mode 100644 index 8dee1408329..00000000000 --- a/build/pkgs/pandoc_attributes/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing pandoc_attributes ... exiting" - exit 1 -fi - diff --git a/build/pkgs/pandoc_attributes/spkg-install.in b/build/pkgs/pandoc_attributes/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/pandoc_attributes/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/pandocfilters/SPKG.rst b/build/pkgs/pandocfilters/SPKG.rst new file mode 100644 index 00000000000..cc87256299d --- /dev/null +++ b/build/pkgs/pandocfilters/SPKG.rst @@ -0,0 +1,30 @@ +pandocfilters +============= + +Description +----------- + +A python module for writing pandoc filters. + +License +------- + +BSD 3-Clause License + + +Upstream Contact +---------------- + +Author: John MacFarlane Home page: https://github.com/jgm/pandocfilters + +Dependencies +------------ + +- Python + + +Special Update/Build Instructions +--------------------------------- + +Download the last release from +https://pypi.python.org/pypi/pandocfilters diff --git a/build/pkgs/pandocfilters/SPKG.txt b/build/pkgs/pandocfilters/SPKG.txt deleted file mode 100644 index d35dfd575e1..00000000000 --- a/build/pkgs/pandocfilters/SPKG.txt +++ /dev/null @@ -1,23 +0,0 @@ -= pandocfilters = - -== Description == - -A python module for writing pandoc filters. - -== License == - -BSD 3-Clause License - -== Upstream Contact == - -Author: John MacFarlane -Home page: https://github.com/jgm/pandocfilters - -== Dependencies == - -* Python - -== Special Update/Build Instructions == - -Download the last release from -https://pypi.python.org/pypi/pandocfilters diff --git a/build/pkgs/pandocfilters/dependencies b/build/pkgs/pandocfilters/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/pandocfilters/dependencies +++ b/build/pkgs/pandocfilters/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/jupyter_client/spkg-install b/build/pkgs/pandocfilters/spkg-install.in similarity index 100% rename from build/pkgs/jupyter_client/spkg-install rename to build/pkgs/pandocfilters/spkg-install.in diff --git a/build/pkgs/pari/SPKG.rst b/build/pkgs/pari/SPKG.rst new file mode 100644 index 00000000000..ef8a1549220 --- /dev/null +++ b/build/pkgs/pari/SPKG.rst @@ -0,0 +1,45 @@ +pari +==== + +Description +----------- + +PARI/GP is a widely used computer algebra system designed for fast +computations in number theory (factorizations, algebraic number theory, +elliptic curves...), but also contains a large number of other useful +functions to compute with mathematical entities such as matrices, +polynomials, power series, algebraic numbers etc., and a lot of +transcendental functions. PARI is also available as a C library to allow +for faster computations. + +Originally developed by Henri Cohen and his co-workers (Université +Bordeaux I, France), PARI is now under the GPL and maintained by Karim +Belabas with the help of many volunteer contributors. + +License +------- + +GPL version 2+ + + +Upstream Contact +---------------- + +- http://pari.math.u-bordeaux.fr/ + +Dependencies +------------ + +- Perl +- MPIR or GMP +- Readline +- GNU patch (shipped with Sage) + + +Special Update/Build Instructions +--------------------------------- + +See patches/README.txt for a list of patches. + +The current upstream tarball was created from the PARI git repository by +running "make snapshot". diff --git a/build/pkgs/pari/SPKG.txt b/build/pkgs/pari/SPKG.txt deleted file mode 100644 index a83829520db..00000000000 --- a/build/pkgs/pari/SPKG.txt +++ /dev/null @@ -1,35 +0,0 @@ -= pari = - -== Description == - -PARI/GP is a widely used computer algebra system designed for fast -computations in number theory (factorizations, algebraic number -theory, elliptic curves...), but also contains a large number of other -useful functions to compute with mathematical entities such as -matrices, polynomials, power series, algebraic numbers etc., and a lot -of transcendental functions. PARI is also available as a C library to -allow for faster computations. - -Originally developed by Henri Cohen and his co-workers (Université -Bordeaux I, France), PARI is now under the GPL and maintained by Karim -Belabas with the help of many volunteer contributors. - -== License == - -GPL version 2+ - -== Upstream Contact == - * http://pari.math.u-bordeaux.fr/ - -== Dependencies == - * Perl - * MPIR or GMP - * Readline - * GNU patch (shipped with Sage) - -== Special Update/Build Instructions == - -See patches/README.txt for a list of patches. - -The current upstream tarball was created from the PARI git repository -by running "make snapshot". diff --git a/build/pkgs/pari/checksums.ini b/build/pkgs/pari/checksums.ini index c1e512cbee7..f10b432dbb7 100644 --- a/build/pkgs/pari/checksums.ini +++ b/build/pkgs/pari/checksums.ini @@ -1,4 +1,5 @@ tarball=pari-VERSION.tar.gz -sha1=49a638ebcce77b9e9e2d8305156f37e7322bc09f -md5=6afe748a472c33ae8787a5034d7742a9 -cksum=4122745815 +sha1=2b9ff51feb388664b834dc346a44867546c78618 +md5=fb2968d7805424518fe44a59a2024afd +cksum=1247903778 +upstream_url=https://pari.math.u-bordeaux.fr/pub/pari/unix/pari-VERSION.tar.gz diff --git a/build/pkgs/pari/distros/arch.txt b/build/pkgs/pari/distros/arch.txt index eafcf1ff62c..b45646efa22 100644 --- a/build/pkgs/pari/distros/arch.txt +++ b/build/pkgs/pari/distros/arch.txt @@ -1,2 +1,8 @@ +# The packages only make sense as a set. +# packages from community/ +pari +# extra packages needed so that pari is picked from the system pari-galdata -pari-seadata-small +pari-seadata # pari's spkg-configure is not satisfied with pari-seadata-small +pari-elldata +pari-galpol diff --git a/build/pkgs/pari/distros/debian.txt b/build/pkgs/pari/distros/debian.txt index 39cd5d363b4..84e2f81b180 100644 --- a/build/pkgs/pari/distros/debian.txt +++ b/build/pkgs/pari/distros/debian.txt @@ -1,3 +1,5 @@ pari-gp2c libpari-dev +# #29319: cypari2 needs gphelp at installation time +pari-doc # We add these data packages because they are checked by spkg-configure.m4 pari-elldata pari-galdata pari-galpol pari-seadata diff --git a/build/pkgs/pari/distros/fedora.txt b/build/pkgs/pari/distros/fedora.txt index 50f779a5c8b..95ba141d56c 100644 --- a/build/pkgs/pari/distros/fedora.txt +++ b/build/pkgs/pari/distros/fedora.txt @@ -1,6 +1,8 @@ pari-devel -# spkg-configure checks for gp -pari-gp +# spkg-configure checks for gp, gphelp. Access to the documentation is crucial +# for the cypari2 build. #29342: By default configuration in /etc/dnf/dnf.conf, +# installation of documentation may be suppressed; we override this. +pari-gp --setopt=tsflags= # spkg-configure checks for data pari-galdata pari-galpol diff --git a/build/pkgs/pari/distros/gentoo.txt b/build/pkgs/pari/distros/gentoo.txt new file mode 100644 index 00000000000..449975ac8e1 --- /dev/null +++ b/build/pkgs/pari/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/pari sci-mathematics/pari-data diff --git a/build/pkgs/pari/distros/homebrew.txt b/build/pkgs/pari/distros/homebrew.txt new file mode 100644 index 00000000000..1987d196c12 --- /dev/null +++ b/build/pkgs/pari/distros/homebrew.txt @@ -0,0 +1,2 @@ +## cannot currently be used by Sage because the pari packages are not in Homebrew +#pari diff --git a/build/pkgs/pari/package-version.txt b/build/pkgs/pari/package-version.txt index 9e5bb77a3ba..a07f650f820 100644 --- a/build/pkgs/pari/package-version.txt +++ b/build/pkgs/pari/package-version.txt @@ -1 +1 @@ -2.11.2 +2.11.4.p0 diff --git a/build/pkgs/pari/spkg-check b/build/pkgs/pari/spkg-check deleted file mode 100644 index 9d2c72847cc..00000000000 --- a/build/pkgs/pari/spkg-check +++ /dev/null @@ -1,34 +0,0 @@ -########################################### -## PARI -########################################### - -if [ -z "$SAGE_LOCAL" ]; then - echo "SAGE_LOCAL undefined ... exiting" - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -if [ "x$SAGE_DEBUG" = xyes ] ; then - CFLAGS="$CFLAGS -O0 -g" # Disable optimisation, add debug symbols. Good - # for debugging or working around compiler bugs. -else - CFLAGS="-O3 -g $CFLAGS" # Default optimisation, with debug symbols. - # Prepend to not override user's setting. -fi - -export CFLAGS - -cd src - -$MAKE test-all - -if [ $? -ne 0 ]; then - echo "Error: PARI failed the self-tests when running '$MAKE test-all'" - exit 1 -fi - -if [ -d ../parigit ]; then - echo "WARNING: You should delete the parigit directory before submitting this spkg." -fi - -echo "The PARI self-tests all passed" diff --git a/build/pkgs/pari/spkg-check.in b/build/pkgs/pari/spkg-check.in new file mode 100644 index 00000000000..ae3aecb4a0a --- /dev/null +++ b/build/pkgs/pari/spkg-check.in @@ -0,0 +1,26 @@ +########################################### +## PARI +########################################### + +if [ "x$SAGE_DEBUG" = xyes ] ; then + CFLAGS="$CFLAGS -O0 -g" # Disable optimisation, add debug symbols. Good + # for debugging or working around compiler bugs. +else + CFLAGS="-O3 -g $CFLAGS" # Default optimisation, with debug symbols. + # Prepend to not override user's setting. +fi + +export CFLAGS + +cd src + +$MAKE test-all + +if [ $? -ne 0 ]; then + echo "Error: PARI failed the self-tests when running '$MAKE test-all'" + exit 1 +fi + +if [ -d ../parigit ]; then + echo "WARNING: You should delete the parigit directory before submitting this spkg." +fi diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index f638e4bcd2d..401663b00a0 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -1,20 +1,31 @@ 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]) - AC_MSG_CHECKING([installing PARI/GP packages? ]) + 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]) sage_spkg_install_pari=yes else + AC_PATH_PROG([GPHELP], [gphelp]) + dnl needed for cypari2 installation; see #29319 + if test x$GPHELP = x; then + AC_MSG_NOTICE([gphelp is not found; cannot use system pari/GP without gphelp]) + AC_MSG_NOTICE([Install a system package that provides it, possibly pari-doc.]) + AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) + sage_spkg_install_pari=yes + else + AC_MSG_CHECKING([whether gphelp has access to the documentation]) + dnl this is needed for cypari2, see #29342 + if $GPHELP -raw Catalan > /dev/null 2>&1; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AC_MSG_NOTICE([Install a system package that provides the documentation.]) + AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) + sage_spkg_install_pari=yes + fi + fi AC_MSG_CHECKING([is pari_elldata installed? ]) gp_ell_check=`echo "r=ellinit(\"11a1\"); r[[11]]" | $GP -qf 2>> config.log` if test x$gp_ell_check = x20008; then @@ -55,10 +66,31 @@ SAGE_SPKG_CONFIGURE([pari], [ AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) sage_spkg_install_pari=yes fi + AC_MSG_CHECKING([whether hyperellcharpoly bug is fixed]) + 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 + AC_MSG_RESULT([no; cannot use system pari/GP with known bug]) + AC_MSG_NOTICE([Upgrade your system package and reconfigure.]) + AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) + sage_spkg_install_pari=yes + fi + 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` + expected="[[0, 2, Mod(0, 2)]]~" + if test x"$bug_check" = x"$expected"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no; cannot use system pari/GP with known bug]) + AC_MSG_NOTICE([Upgrade your system package and reconfigure.]) + AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) + sage_spkg_install_pari=yes + fi fi dnl end GP test - if test x$sage_spkg_install_pari = x; then dnl main PARI test - AC_MSG_RESULT([no]) + if test x$sage_spkg_install_pari = xno; then dnl main PARI test AC_CHECK_HEADER([pari/pari.h], [], [sage_spkg_install_pari=yes]) dnl matpermanent appears in pari 2.11 AC_SEARCH_LIBS([matpermanent], [pari], [ @@ -100,14 +132,12 @@ 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']) - AC_MSG_RESULT([using Sage's pari SPKG]) else AC_SUBST(SAGE_PARI_PREFIX, ['']) - AC_MSG_RESULT([using pari/gp from the system]) fi ]) diff --git a/build/pkgs/pari/spkg-install b/build/pkgs/pari/spkg-install deleted file mode 100644 index da2b35e52b0..00000000000 --- a/build/pkgs/pari/spkg-install +++ /dev/null @@ -1,146 +0,0 @@ -########################################### -## PARI -########################################### - -cd src - -# Prepare all variables for building PARI/GP, except for CFLAGS - -# This is needed or there are weird locale problems involving rpath -# with building Sage: -LANG=C -export LANG - -# Pari ignores LDFLAGS when linking libpari-gmp.so, so we trick it: -DLLDFLAGS="$LDFLAGS" -export DLLDFLAGS - -# PARI does not build correctly with LD set (which is set by -# sage-env). PARI actually needs LD set to a compiler to do the -# linking, not to the linker directly. -unset LD - -# Let the user pass extra parameters to PARI's "Configure", e.g. -# to specify desired graphics support (which is disabled by default): -if [ -z "$PARI_CONFIGURE" ]; then - echo "============================================================" - echo "Configuring PARI/GP without graphics support (for plotting)." - echo "If you need it, pass the appropriate option(s) to PARI by" - echo "setting and exporting \$PARI_CONFIGURE prior to building" - echo "Sage (or at least before you build/install the PARI spkg)," - echo "e.g. by typing at the shell prompt:" - echo " export PARI_CONFIGURE=\"--graphic=auto\"" - echo "or" - echo " export PARI_CONFIGURE=\"--with-fltk\"" - echo "Note that PARI doesn't treat it as an error if the requested" - echo "graphics library or the corresponding header files are not" - echo "found; it will then simply disable graphics support." - echo "Please consult the PARI documentation for further details." - echo "============================================================" - - PARI_CONFIGURE="--graphic=none" -else - echo "============================================================" - echo "Configuring PARI/GP with additional user-specified options:" - echo " PARI_CONFIGURE=\"$PARI_CONFIGURE\"" - - # Do NOT add "--graphic=none" if the user provided one of these: - # "--with-fltk[=...]", "--with-qt[=...]" - # Comment by leif: This misbehavoir seems to have been fixed in - # PARI 2.4.3; the following doesn't hurt though: - if ! (echo "$PARI_CONFIGURE" | egrep -- "--with-fltk|--with-qt") \ - >/dev/null; - then - echo "To avoid unexpected behavior, we prepend \"--graphic=none\":" - PARI_CONFIGURE="--graphic=none $PARI_CONFIGURE" - echo " \"$PARI_CONFIGURE\"" - echo "(Disabling graphics can be overridden by user settings.)" - fi - echo "============================================================" -fi - -# Allow the user to enable PARI self-tuning. -# This is time-consuming, but some may want to do it. -# Comment by leif: Also, PARI's Configure calls "make" (instead of $MAKE) -# if we do tuning, so: -# TODO: Replace that in our patched version of Configure! -if [ "$SAGE_TUNE_pari" = yes -o "$SAGE_TUNE_PARI" = yes ]; then - echo - echo 'PARI/GP will be tuned for your system since you set SAGE_TUNE_pari="yes".' - echo 'This can take a long time.' - echo 'WARNING: Tuning PARI/GP is unreliable. You may find your build of PARI' - echo 'fails, or PARI/GP does not work properly once built. We recommend to' - echo 'build this package with SAGE_CHECK="yes".' - echo - PARI_CONFIGURE="$PARI_CONFIGURE --tune" -else - echo - echo 'To minimize Sage build time and to ensure the best reliability, PARI/GP' - echo 'will not be tuned for your system. Experience shows tuning can be' - echo 'unreliable. If you do want to tune PARI/GP, set the environment' - echo 'variable SAGE_TUNE_pari="yes" by typing the following before building' - echo 'Sage (or at least before building/installing PARI/GP):' - echo ' SAGE_TUNE_pari=yes' - echo ' export SAGE_TUNE_pari' - echo 'If you do this, we strongly recommend to also enable checking. For this,' - echo 'type the following:' - echo ' SAGE_CHECK=yes' - echo ' export SAGE_CHECK' - echo -fi - -unset GP_INSTALL_PREFIX # we do not want this to be set by the user - -# In addition, a lot of variables used (internally) by PARI might un- -# intentionally get their values from the "global" environment, so it's -# safer to clear them here (not further messing up PARI's scripts): -unset static tune timing_fun error -unset enable_tls -unset with_fltk with_qt -unset with_ncurses_lib -unset with_readline_include with_readline_lib without_readline -unset with_gmp_include with_gmp_lib without_gmp -unset dfltbindir dfltdatadir dfltemacsdir dfltincludedir -unset dfltlibdir dfltmandir dfltsysdatadir dfltobjdir - - -# Set CFLAGS -if [ "$SAGE_DEBUG" = yes ]; then - # Disable optimisation, add debug symbols. - CFLAGS="-O0 -g $CFLAGS" - - # Compile kernel files with -O1 instead of -funroll-loops; -O0 gives - # a segmentation fault on some OS X systems when doing - # factor(10356613*10694706299664611221) - # See #13921, also reported upstream: - # - http://pari.math.u-bordeaux.fr/archives/pari-dev-1301/msg00000.html - PARI_MAKEFLAGS="KERNELCFLAGS=-O1 $PARI_MAKEFLAGS" -else - # Use PARI's default CFLAGS (with -g added). - # PARI's Configure adds -O3 to the CFLAGS, so we don't need to add - # it explicitly. - CFLAGS="-g $CFLAGS" -fi - -export CFLAGS - - -# Build PARI/GP -# Configure PARI/GP, forcing bash instead of /bin/sh. It is not -# strictly necessary to use bash, but it hopefully makes the script -# less system-dependent. Since Sage assumes the existence of bash -# anyway, it doesn't hurt either. -- jdemeyer -# -# Note that "--graphic=none" is (usually) added to PARI_CONFIGURE: -#--with-gmp-include="/usr/include/x86_64-linux-gnu" \ -bash ./Configure --prefix="$SAGE_LOCAL" \ - --with-readline="$SAGE_LOCAL" $SAGE_CONFIGURE_GMP \ - --with-runtime-perl="/usr/bin/env perl" \ - --kernel=gmp $PARI_CONFIGURE || \ - sdh_die "Error configuring PARI with readline and GMP kernel failed." - -sdh_make $PARI_MAKEFLAGS gp - - -# install non-parallel (-j1) because of race conditions -sdh_make_install -j1 install-lib-sta diff --git a/build/pkgs/pari/spkg-install.in b/build/pkgs/pari/spkg-install.in new file mode 100644 index 00000000000..d8896b62a18 --- /dev/null +++ b/build/pkgs/pari/spkg-install.in @@ -0,0 +1,154 @@ +########################################### +## PARI +########################################### + +cd src + +# Prepare all variables for building PARI/GP, except for CFLAGS + +# This is needed or there are weird locale problems involving rpath +# with building Sage: +LANG=C +export LANG + +# Pari ignores LDFLAGS when linking libpari-gmp.so, so we trick it: +DLLDFLAGS="$LDFLAGS" +export DLLDFLAGS + +# PARI does not build correctly with LD set (which is set by +# sage-env). PARI actually needs LD set to a compiler to do the +# linking, not to the linker directly. +unset LD + +# Let the user pass extra parameters to PARI's "Configure", e.g. +# to specify desired graphics support (which is disabled by default): +if [ -z "$PARI_CONFIGURE" ]; then + echo "============================================================" + echo "Configuring PARI/GP without graphics support (for plotting)." + echo "If you need it, pass the appropriate option(s) to PARI by" + echo "setting and exporting \$PARI_CONFIGURE prior to building" + echo "Sage (or at least before you build/install the PARI spkg)," + echo "e.g. by typing at the shell prompt:" + echo " export PARI_CONFIGURE=\"--graphic=auto\"" + echo "or" + echo " export PARI_CONFIGURE=\"--with-fltk\"" + echo "Note that PARI doesn't treat it as an error if the requested" + echo "graphics library or the corresponding header files are not" + echo "found; it will then simply disable graphics support." + echo "Please consult the PARI documentation for further details." + echo "============================================================" + + PARI_CONFIGURE="--graphic=none" +else + echo "============================================================" + echo "Configuring PARI/GP with additional user-specified options:" + echo " PARI_CONFIGURE=\"$PARI_CONFIGURE\"" + + # Do NOT add "--graphic=none" if the user provided one of these: + # "--with-fltk[=...]", "--with-qt[=...]" + # Comment by leif: This misbehavoir seems to have been fixed in + # PARI 2.4.3; the following doesn't hurt though: + if ! (echo "$PARI_CONFIGURE" | egrep -- "--with-fltk|--with-qt") \ + >/dev/null; + then + echo "To avoid unexpected behavior, we prepend \"--graphic=none\":" + PARI_CONFIGURE="--graphic=none $PARI_CONFIGURE" + echo " \"$PARI_CONFIGURE\"" + echo "(Disabling graphics can be overridden by user settings.)" + fi + echo "============================================================" +fi + +# Allow the user to enable PARI self-tuning. +# This is time-consuming, but some may want to do it. +# Comment by leif: Also, PARI's Configure calls "make" (instead of $MAKE) +# if we do tuning, so: +# TODO: Replace that in our patched version of Configure! +if [ "$SAGE_TUNE_pari" = yes -o "$SAGE_TUNE_PARI" = yes ]; then + echo + echo 'PARI/GP will be tuned for your system since you set SAGE_TUNE_pari="yes".' + echo 'This can take a long time.' + echo 'WARNING: Tuning PARI/GP is unreliable. You may find your build of PARI' + echo 'fails, or PARI/GP does not work properly once built. We recommend to' + echo 'build this package with SAGE_CHECK="yes".' + echo + PARI_CONFIGURE="$PARI_CONFIGURE --tune" +else + echo + echo 'To minimize Sage build time and to ensure the best reliability, PARI/GP' + echo 'will not be tuned for your system. Experience shows tuning can be' + echo 'unreliable. If you do want to tune PARI/GP, set the environment' + echo 'variable SAGE_TUNE_pari="yes" by typing the following before building' + echo 'Sage (or at least before building/installing PARI/GP):' + echo ' SAGE_TUNE_pari=yes' + echo ' export SAGE_TUNE_pari' + echo 'If you do this, we strongly recommend to also enable checking. For this,' + echo 'type the following:' + echo ' SAGE_CHECK=yes' + echo ' export SAGE_CHECK' + echo +fi + +unset GP_INSTALL_PREFIX # we do not want this to be set by the user + +# In addition, a lot of variables used (internally) by PARI might un- +# intentionally get their values from the "global" environment, so it's +# safer to clear them here (not further messing up PARI's scripts): +unset static tune timing_fun error +unset enable_tls +unset with_fltk with_qt +unset with_ncurses_lib +unset with_readline_include with_readline_lib without_readline +unset with_gmp_include with_gmp_lib without_gmp +unset dfltbindir dfltdatadir dfltemacsdir dfltincludedir +unset dfltlibdir dfltmandir dfltsysdatadir dfltobjdir + +# Avoid segmentation fault on macOS Catalina with XCode 11.4 +# #29451 +MACOSX_VERSION=`uname -r | awk -F. '{print $1}'` +if [ $MACOSX_VERSION -ge 14 ]; then + # various packages have still have issues with + # two digit OS X versions + export MACOSX_DEPLOYMENT_TARGET=10.9 +fi + +# Set CFLAGS +if [ "$SAGE_DEBUG" = yes ]; then + # Disable optimisation, add debug symbols. + CFLAGS="-O0 -g $CFLAGS" + + # Compile kernel files with -O1 instead of -funroll-loops; -O0 gives + # a segmentation fault on some OS X systems when doing + # factor(10356613*10694706299664611221) + # See #13921, also reported upstream: + # - http://pari.math.u-bordeaux.fr/archives/pari-dev-1301/msg00000.html + PARI_MAKEFLAGS="KERNELCFLAGS=-O1 $PARI_MAKEFLAGS" +else + # Use PARI's default CFLAGS (with -g added). + # PARI's Configure adds -O3 to the CFLAGS, so we don't need to add + # it explicitly. + CFLAGS="-g $CFLAGS" +fi + +export CFLAGS + + +# Build PARI/GP +# Configure PARI/GP, forcing bash instead of /bin/sh. It is not +# strictly necessary to use bash, but it hopefully makes the script +# less system-dependent. Since Sage assumes the existence of bash +# anyway, it doesn't hurt either. -- jdemeyer +# +# Note that "--graphic=none" is (usually) added to PARI_CONFIGURE: +#--with-gmp-include="/usr/include/x86_64-linux-gnu" \ +bash ./Configure --prefix="$SAGE_LOCAL" \ + --with-readline="$SAGE_LOCAL" $SAGE_CONFIGURE_GMP \ + --with-runtime-perl="/usr/bin/env perl" \ + --kernel=gmp $PARI_CONFIGURE || \ + sdh_die "Error configuring PARI with readline and GMP kernel failed." + +sdh_make $PARI_MAKEFLAGS gp + + +# install non-parallel (-j1) because of race conditions +sdh_make_install -j1 install-lib-sta diff --git a/build/pkgs/pari_elldata/SPKG.rst b/build/pkgs/pari_elldata/SPKG.rst new file mode 100644 index 00000000000..6d487573f9b --- /dev/null +++ b/build/pkgs/pari_elldata/SPKG.rst @@ -0,0 +1,25 @@ +pari_elldata +============ + +Description +----------- + +PARI/GP version of J. E. Cremona Elliptic Curve Data, needed by +ellsearch and ellidentify. + +License +------- + +GNU General Public License (GPL version 2 or any later version). + + +Upstream Contact +---------------- + +http://pari.math.u-bordeaux.fr/ + +Dependencies +------------ + +- Installation: None +- Runtime: PARI/GP diff --git a/build/pkgs/pari_elldata/SPKG.txt b/build/pkgs/pari_elldata/SPKG.txt deleted file mode 100644 index a51f439e39e..00000000000 --- a/build/pkgs/pari_elldata/SPKG.txt +++ /dev/null @@ -1,18 +0,0 @@ -= pari_elldata = - -== Description == - -PARI/GP version of J. E. Cremona Elliptic Curve Data, needed by ellsearch and ellidentify. - -== License == - -GNU General Public License (GPL version 2 or any later version). - -== Upstream Contact == - -http://pari.math.u-bordeaux.fr/ - -== Dependencies == - -* Installation: None -* Runtime: PARI/GP diff --git a/build/pkgs/pari_elldata/distros/arch.txt b/build/pkgs/pari_elldata/distros/arch.txt new file mode 100644 index 00000000000..540f0b1ab86 --- /dev/null +++ b/build/pkgs/pari_elldata/distros/arch.txt @@ -0,0 +1 @@ +pari-elldata diff --git a/build/pkgs/pari_elldata/spkg-install b/build/pkgs/pari_elldata/spkg-install.in similarity index 100% rename from build/pkgs/pari_elldata/spkg-install rename to build/pkgs/pari_elldata/spkg-install.in diff --git a/build/pkgs/pari_galdata/SPKG.rst b/build/pkgs/pari_galdata/SPKG.rst new file mode 100644 index 00000000000..00b409f83a8 --- /dev/null +++ b/build/pkgs/pari_galdata/SPKG.rst @@ -0,0 +1,24 @@ +pari_galdata +============ + +Description +----------- + +PARI package "galdata": Needed by polgalois to compute Galois group in +degrees 8 through 11. + +License +------- + +GPL version 2+ + + +Upstream Contact +---------------- + +http://pari.math.u-bordeaux.fr/ + +Dependencies +------------ + +None (package contains data files only) diff --git a/build/pkgs/pari_galdata/SPKG.txt b/build/pkgs/pari_galdata/SPKG.txt deleted file mode 100644 index 049f93df73f..00000000000 --- a/build/pkgs/pari_galdata/SPKG.txt +++ /dev/null @@ -1,18 +0,0 @@ -= pari_galdata = - -== Description == - -PARI package "galdata": Needed by polgalois to compute Galois group in -degrees 8 through 11. - -== License == - -GPL version 2+ - -== Upstream Contact == - -http://pari.math.u-bordeaux.fr/ - -== Dependencies == - -None (package contains data files only) diff --git a/build/pkgs/pari_galdata/spkg-install b/build/pkgs/pari_galdata/spkg-install.in similarity index 100% rename from build/pkgs/pari_galdata/spkg-install rename to build/pkgs/pari_galdata/spkg-install.in diff --git a/build/pkgs/pari_galpol/SPKG.rst b/build/pkgs/pari_galpol/SPKG.rst new file mode 100644 index 00000000000..4dff32f9551 --- /dev/null +++ b/build/pkgs/pari_galpol/SPKG.rst @@ -0,0 +1,26 @@ +pari_galpol +=========== + +Description +----------- + +PARI package of the GALPOL database of polynomials defining Galois +extensions of the rationals, accessed by galoisgetpol, galoisgetgroup, +galoisgetname. + +License +------- + +GNU General Public License (GPL version 2 or any later version). + + +Upstream Contact +---------------- + +http://pari.math.u-bordeaux.fr/ + +Dependencies +------------ + +- Installation: None +- Runtime: PARI/GP diff --git a/build/pkgs/pari_galpol/SPKG.txt b/build/pkgs/pari_galpol/SPKG.txt deleted file mode 100644 index a05341cd1bd..00000000000 --- a/build/pkgs/pari_galpol/SPKG.txt +++ /dev/null @@ -1,20 +0,0 @@ -= pari_galpol = - -== Description == - -PARI package of the GALPOL database of polynomials defining Galois -extensions of the rationals, accessed by galoisgetpol, galoisgetgroup, -galoisgetname. - -== License == - -GNU General Public License (GPL version 2 or any later version). - -== Upstream Contact == - -http://pari.math.u-bordeaux.fr/ - -== Dependencies == - -* Installation: None -* Runtime: PARI/GP diff --git a/build/pkgs/pari_galpol/distros/arch.txt b/build/pkgs/pari_galpol/distros/arch.txt new file mode 100644 index 00000000000..e3b41380f41 --- /dev/null +++ b/build/pkgs/pari_galpol/distros/arch.txt @@ -0,0 +1 @@ +pari-galpol diff --git a/build/pkgs/pari_galpol/spkg-install b/build/pkgs/pari_galpol/spkg-install.in similarity index 100% rename from build/pkgs/pari_galpol/spkg-install rename to build/pkgs/pari_galpol/spkg-install.in diff --git a/build/pkgs/pari_jupyter/SPKG.rst b/build/pkgs/pari_jupyter/SPKG.rst new file mode 100644 index 00000000000..c365806b7c9 --- /dev/null +++ b/build/pkgs/pari_jupyter/SPKG.rst @@ -0,0 +1,28 @@ +pari_jupyter +============ + +Description +----------- + +A Jupyter kernel for PARI/GP + +License +------- + +GPL version 3 or later + + +Upstream Contact +---------------- + +- https://github.com/jdemeyer/pari_jupyter +- Jeroen Demeyer + +Dependencies +------------ + +- Python (tested with version 2.7.14 and 3.6.1) +- Jupyter 4 +- PARI version 2.8.0 or later +- Readline (any version which works with PARI) +- Optional: Cython version 0.25 or later diff --git a/build/pkgs/pari_jupyter/SPKG.txt b/build/pkgs/pari_jupyter/SPKG.txt deleted file mode 100644 index 3a6ad00273c..00000000000 --- a/build/pkgs/pari_jupyter/SPKG.txt +++ /dev/null @@ -1,22 +0,0 @@ -= pari_jupyter = - -== Description == - -A Jupyter kernel for PARI/GP - -== License == - -GPL version 3 or later - -== Upstream Contact == - -* https://github.com/jdemeyer/pari_jupyter -* Jeroen Demeyer - -== Dependencies == - -* Python (tested with version 2.7.14 and 3.6.1) -* Jupyter 4 -* PARI version 2.8.0 or later -* Readline (any version which works with PARI) -* Optional: Cython version 0.25 or later diff --git a/build/pkgs/pari_jupyter/dependencies b/build/pkgs/pari_jupyter/dependencies index 9e6b05089de..44eaa153cc2 100644 --- a/build/pkgs/pari_jupyter/dependencies +++ b/build/pkgs/pari_jupyter/dependencies @@ -1 +1 @@ -$(PYTHON) pari | pip cython notebook jupyter_core +$(PYTHON) pari | $(PYTHON_TOOLCHAIN) cython notebook jupyter_core diff --git a/build/pkgs/jupyter_core/spkg-install b/build/pkgs/pari_jupyter/spkg-install.in similarity index 100% rename from build/pkgs/jupyter_core/spkg-install rename to build/pkgs/pari_jupyter/spkg-install.in diff --git a/build/pkgs/pari_nftables/SPKG.rst b/build/pkgs/pari_nftables/SPKG.rst new file mode 100644 index 00000000000..2f8c273b660 --- /dev/null +++ b/build/pkgs/pari_nftables/SPKG.rst @@ -0,0 +1,25 @@ +pari_elldata +============ + +Description +----------- + +Repackaging of the historical megrez number field tables (errors fixed, +1/10th the size, easier to use). + +License +------- + +GNU General Public License (GPL version 2 or any later version). + + +Upstream Contact +---------------- + +http://pari.math.u-bordeaux.fr/ + +Dependencies +------------ + +- Installation: None +- Runtime: PARI/GP diff --git a/build/pkgs/pari_nftables/SPKG.txt b/build/pkgs/pari_nftables/SPKG.txt deleted file mode 100644 index 310faf0a446..00000000000 --- a/build/pkgs/pari_nftables/SPKG.txt +++ /dev/null @@ -1,19 +0,0 @@ -= pari_elldata = - -== Description == - -Repackaging of the historical megrez number field tables -(errors fixed, 1/10th the size, easier to use). - -== License == - -GNU General Public License (GPL version 2 or any later version). - -== Upstream Contact == - -http://pari.math.u-bordeaux.fr/ - -== Dependencies == - -* Installation: None -* Runtime: PARI/GP diff --git a/build/pkgs/pari_nftables/spkg-install b/build/pkgs/pari_nftables/spkg-install.in similarity index 100% rename from build/pkgs/pari_nftables/spkg-install rename to build/pkgs/pari_nftables/spkg-install.in diff --git a/build/pkgs/pari_seadata/SPKG.rst b/build/pkgs/pari_seadata/SPKG.rst new file mode 100644 index 00000000000..83561bd4e91 --- /dev/null +++ b/build/pkgs/pari_seadata/SPKG.rst @@ -0,0 +1,28 @@ +pari_seadata +============ + +Description +----------- + +Needed by ellap for large primes. These polynomials were extracted from +the ECHIDNA databases and computed by David R. Kohel. This covers finite +fields of cardinality q up to 750 bits. PARI/GP 2.9 contains fallback +code to go on when all modular polynomials in the database have been +exhausted and can handle larger fields (with an important slowdown). + +License +------- + +GNU General Public License (GPL version 2 or any later version). + + +Upstream Contact +---------------- + +http://pari.math.u-bordeaux.fr/ + +Dependencies +------------ + +- Installation: None +- Runtime: PARI/GP diff --git a/build/pkgs/pari_seadata/SPKG.txt b/build/pkgs/pari_seadata/SPKG.txt deleted file mode 100644 index ececd8350a0..00000000000 --- a/build/pkgs/pari_seadata/SPKG.txt +++ /dev/null @@ -1,23 +0,0 @@ -= pari_seadata = - -== Description == - -Needed by ellap for large primes. -These polynomials were extracted from the ECHIDNA databases and computed -by David R. Kohel. This covers finite fields of cardinality q up to 750 -bits. PARI/GP 2.9 contains fallback code to go on when all modular -polynomials in the database have been exhausted and can handle larger -fields (with an important slowdown). - -== License == - -GNU General Public License (GPL version 2 or any later version). - -== Upstream Contact == - -http://pari.math.u-bordeaux.fr/ - -== Dependencies == - -* Installation: None -* Runtime: PARI/GP diff --git a/build/pkgs/pari_seadata/distros/arch.txt b/build/pkgs/pari_seadata/distros/arch.txt new file mode 100644 index 00000000000..3abdcf7bfcd --- /dev/null +++ b/build/pkgs/pari_seadata/distros/arch.txt @@ -0,0 +1 @@ +pari-seadata diff --git a/build/pkgs/pari_seadata/spkg-install b/build/pkgs/pari_seadata/spkg-install.in similarity index 100% rename from build/pkgs/pari_seadata/spkg-install rename to build/pkgs/pari_seadata/spkg-install.in diff --git a/build/pkgs/pari_seadata_small/SPKG.rst b/build/pkgs/pari_seadata_small/SPKG.rst new file mode 100644 index 00000000000..149c0c5c416 --- /dev/null +++ b/build/pkgs/pari_seadata_small/SPKG.rst @@ -0,0 +1,26 @@ +pari_seadata_small +================== + +Description +----------- + +PARI package "seadata_small": Needed by ellap for large primes. This +"small" one is a much smaller version that should be suitable for primes +up to 350 bits. These polynomials were extracted from the ECHIDNA +databases and computed by David R. Kohel. + +License +------- + +GPL version 2+ + + +Upstream Contact +---------------- + +http://pari.math.u-bordeaux.fr/ + +Dependencies +------------ + +None (package contains data files only) diff --git a/build/pkgs/pari_seadata_small/SPKG.txt b/build/pkgs/pari_seadata_small/SPKG.txt deleted file mode 100644 index 081dd5738b5..00000000000 --- a/build/pkgs/pari_seadata_small/SPKG.txt +++ /dev/null @@ -1,20 +0,0 @@ -= pari_seadata_small = - -== Description == - -PARI package "seadata_small": Needed by ellap for large primes. This -"small" one is a much smaller version that should be suitable for -primes up to 350 bits. These polynomials were extracted from the ECHIDNA -databases and computed by David R. Kohel. - -== License == - -GPL version 2+ - -== Upstream Contact == - -http://pari.math.u-bordeaux.fr/ - -== Dependencies == - -None (package contains data files only) diff --git a/build/pkgs/pari_seadata_small/distros/arch.txt b/build/pkgs/pari_seadata_small/distros/arch.txt index 68d650832dc..38482f7f6b7 100644 --- a/build/pkgs/pari_seadata_small/distros/arch.txt +++ b/build/pkgs/pari_seadata_small/distros/arch.txt @@ -1 +1,4 @@ -pari-seadata-small +## this is the actual package, but pari's spkg-configure is not satisfied with pari-seadata-small, +## so we have to install the big package pari-seadata anyway. +# pari-seadata-small +pari-seadata diff --git a/build/pkgs/pari_seadata_small/spkg-install b/build/pkgs/pari_seadata_small/spkg-install.in similarity index 100% rename from build/pkgs/pari_seadata_small/spkg-install rename to build/pkgs/pari_seadata_small/spkg-install.in diff --git a/build/pkgs/parso/SPKG.txt b/build/pkgs/parso/SPKG.txt new file mode 100644 index 00000000000..db60aa229d9 --- /dev/null +++ b/build/pkgs/parso/SPKG.txt @@ -0,0 +1,8 @@ += parso = + +== Description == + +Parso is a Python parser that supports error recovery and round-trip +parsing for different Python versions (in multiple Python +versions). Parso is also able to list multiple syntax errors in your +python file. diff --git a/build/pkgs/parso/checksums.ini b/build/pkgs/parso/checksums.ini new file mode 100644 index 00000000000..acf5642e88c --- /dev/null +++ b/build/pkgs/parso/checksums.ini @@ -0,0 +1,5 @@ +tarball=parso-VERSION.tar.gz +sha1=8839588a883a5345fa00d1599809b58be432dd23 +md5=d3a892fb4d9a0ffed838b0a3416145bf +cksum=4271711100 +upstream_url=https://pypi.io/packages/source/p/parso/parso-VERSION.tar.gz diff --git a/build/pkgs/parso/dependencies b/build/pkgs/parso/dependencies new file mode 100644 index 00000000000..15df0c4d6d8 --- /dev/null +++ b/build/pkgs/parso/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/parso/package-version.txt b/build/pkgs/parso/package-version.txt new file mode 100644 index 00000000000..faef31a4357 --- /dev/null +++ b/build/pkgs/parso/package-version.txt @@ -0,0 +1 @@ +0.7.0 diff --git a/build/pkgs/parso/spkg-install.in b/build/pkgs/parso/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/parso/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/backports_ssl_match_hostname/type b/build/pkgs/parso/type similarity index 100% rename from build/pkgs/backports_ssl_match_hostname/type rename to build/pkgs/parso/type diff --git a/build/pkgs/patch/SPKG.rst b/build/pkgs/patch/SPKG.rst new file mode 100644 index 00000000000..283448e24bf --- /dev/null +++ b/build/pkgs/patch/SPKG.rst @@ -0,0 +1,43 @@ +patch +===== + +Description +----------- + +'patch' takes a patch file containing a difference listing produced by +the 'diff' program and applies those differences to one or more original +files, producing patched versions. + +The version of 'patch' included is the GNU one. Some of the 'diff' files +produced by GNU 'diff' are not acceptble to some versions of the 'patch' +command, such as the 'patch' command that comes with Solaris. + +License +------- + +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, or (at your option) any +later version. + + +Upstream Contact +---------------- + +Main web site: http://savannah.gnu.org/projects/patch/ Bug database at +http://savannah.gnu.org/bugs/?group=patch Submit bugs at +http://savannah.gnu.org/bugs/?func=additem&group=patch Mailing lists +bug-patch@gnu.org + +Dependencies +------------ + +None + + +Special Update/Build Instructions +--------------------------------- + +In the event patches ever need to be made to this package, the method of +applying the patches should not rely on the 'patch' existing on the +system. diff --git a/build/pkgs/patch/SPKG.txt b/build/pkgs/patch/SPKG.txt deleted file mode 100644 index 1d20977f4ee..00000000000 --- a/build/pkgs/patch/SPKG.txt +++ /dev/null @@ -1,34 +0,0 @@ -= patch = - -== Description == - -'patch' takes a patch file containing a difference listing produced -by the 'diff' program and applies those differences to one -or more original files, producing patched versions. - -The version of 'patch' included is the GNU one. Some of the 'diff' files -produced by GNU 'diff' are not acceptble to some versions of the 'patch' -command, such as the 'patch' command that comes with Solaris. - -== License == - -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, or (at your option) -any later version. - -== Upstream Contact == - -Main web site: http://savannah.gnu.org/projects/patch/ -Bug database at http://savannah.gnu.org/bugs/?group=patch -Submit bugs at http://savannah.gnu.org/bugs/?func=additem&group=patch -Mailing lists bug-patch@gnu.org - -== Dependencies == - -None - -== Special Update/Build Instructions == - -In the event patches ever need to be made to this package, the method of -applying the patches should not rely on the 'patch' existing on the system. diff --git a/build/pkgs/patch/distros/cygwin.txt b/build/pkgs/patch/distros/cygwin.txt new file mode 100644 index 00000000000..9eb7b90ed50 --- /dev/null +++ b/build/pkgs/patch/distros/cygwin.txt @@ -0,0 +1 @@ +patch diff --git a/build/pkgs/patch/distros/homebrew.txt b/build/pkgs/patch/distros/homebrew.txt new file mode 100644 index 00000000000..a2d08ac3307 --- /dev/null +++ b/build/pkgs/patch/distros/homebrew.txt @@ -0,0 +1 @@ +gpatch diff --git a/build/pkgs/patch/distros/slackware.txt b/build/pkgs/patch/distros/slackware.txt new file mode 100644 index 00000000000..9eb7b90ed50 --- /dev/null +++ b/build/pkgs/patch/distros/slackware.txt @@ -0,0 +1 @@ +patch diff --git a/build/pkgs/patch/spkg-install b/build/pkgs/patch/spkg-install.in similarity index 100% rename from build/pkgs/patch/spkg-install rename to build/pkgs/patch/spkg-install.in diff --git a/build/pkgs/pathlib2/SPKG.txt b/build/pkgs/pathlib2/SPKG.txt deleted file mode 100644 index 3330269b541..00000000000 --- a/build/pkgs/pathlib2/SPKG.txt +++ /dev/null @@ -1,11 +0,0 @@ -= pathlib = - -== Description == - -Object-oriented filesystem paths - -The old pathlib module on bitbucket is in bugfix-only mode. The goal -of pathlib2 is to provide a backport of standard pathlib module which -tracks the standard library module, so all the newest features of the -standard pathlib can be used also on older Python versions. - diff --git a/build/pkgs/pathlib2/checksums.ini b/build/pkgs/pathlib2/checksums.ini deleted file mode 100644 index 01d841eedb8..00000000000 --- a/build/pkgs/pathlib2/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=pathlib2-VERSION.tar.gz -sha1=3a902176bb4b5b7f1f112e501409b84c21c7f50c -md5=f9ede5c162ea6fc6a3b1d26db53bb6a2 -cksum=1296606336 diff --git a/build/pkgs/pathlib2/dependencies b/build/pkgs/pathlib2/dependencies deleted file mode 100644 index 10de8f8621e..00000000000 --- a/build/pkgs/pathlib2/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | setuptools pip six scandir - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/pathlib2/package-version.txt b/build/pkgs/pathlib2/package-version.txt deleted file mode 100644 index 0bee604df76..00000000000 --- a/build/pkgs/pathlib2/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -2.3.3 diff --git a/build/pkgs/pathpy/SPKG.rst b/build/pkgs/pathpy/SPKG.rst new file mode 100644 index 00000000000..95dda5598b0 --- /dev/null +++ b/build/pkgs/pathpy/SPKG.rst @@ -0,0 +1,7 @@ +path.py +======= + +Description +----------- + +A module wrapper for os.path diff --git a/build/pkgs/pathpy/SPKG.txt b/build/pkgs/pathpy/SPKG.txt deleted file mode 100644 index 1aec14146ab..00000000000 --- a/build/pkgs/pathpy/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= path.py = - -== Description == - -A module wrapper for os.path diff --git a/build/pkgs/pathpy/dependencies b/build/pkgs/pathpy/dependencies index b148b64d76c..15df0c4d6d8 100644 --- a/build/pkgs/pathpy/dependencies +++ b/build/pkgs/pathpy/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools setuptools_scm pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/kiwisolver/spkg-install b/build/pkgs/pathpy/spkg-install.in similarity index 100% rename from build/pkgs/kiwisolver/spkg-install rename to build/pkgs/pathpy/spkg-install.in diff --git a/build/pkgs/pcre/SPKG.rst b/build/pkgs/pcre/SPKG.rst new file mode 100644 index 00000000000..0f2b50045b5 --- /dev/null +++ b/build/pkgs/pcre/SPKG.rst @@ -0,0 +1,29 @@ +pcre +==== + +Description +----------- + +Perl-compatible regular expressions library. + +License +------- + +BSD License ; see LICENCE (sic) at the root of the original tarball. + + +Upstream Contact +---------------- + +Mailing list at https://lists.exim.org/mailman/listinfo/pcre-dev + +Dependencies +------------ + +None listed. + + +Special Update/Build Instructions +--------------------------------- + +None applicable (see README at tarball's root). diff --git a/build/pkgs/pcre/SPKG.txt b/build/pkgs/pcre/SPKG.txt deleted file mode 100644 index db08f9fa9c5..00000000000 --- a/build/pkgs/pcre/SPKG.txt +++ /dev/null @@ -1,21 +0,0 @@ -= pcre = - -== Description == - -Perl-compatible regular expressions library. - -== License == - -BSD License ; see LICENCE (sic) at the root of the original tarball. - -== Upstream Contact == - -Mailing list at https://lists.exim.org/mailman/listinfo/pcre-dev - -== Dependencies == - -None listed. - -== Special Update/Build Instructions == - -None applicable (see README at tarball's root). diff --git a/build/pkgs/pcre/distros/cygwin.txt b/build/pkgs/pcre/distros/cygwin.txt new file mode 100644 index 00000000000..11042f02d24 --- /dev/null +++ b/build/pkgs/pcre/distros/cygwin.txt @@ -0,0 +1 @@ +libpcre-devel diff --git a/build/pkgs/pcre/distros/fedora.txt b/build/pkgs/pcre/distros/fedora.txt new file mode 100644 index 00000000000..cd3cd89bdd9 --- /dev/null +++ b/build/pkgs/pcre/distros/fedora.txt @@ -0,0 +1 @@ +pcre pcre-devel diff --git a/build/pkgs/pcre/distros/homebrew.txt b/build/pkgs/pcre/distros/homebrew.txt new file mode 100644 index 00000000000..abd501ce241 --- /dev/null +++ b/build/pkgs/pcre/distros/homebrew.txt @@ -0,0 +1 @@ +pcre diff --git a/build/pkgs/pcre/distros/slackware.txt b/build/pkgs/pcre/distros/slackware.txt new file mode 100644 index 00000000000..abd501ce241 --- /dev/null +++ b/build/pkgs/pcre/distros/slackware.txt @@ -0,0 +1 @@ +pcre diff --git a/build/pkgs/pcre/spkg-install b/build/pkgs/pcre/spkg-install.in similarity index 100% rename from build/pkgs/pcre/spkg-install rename to build/pkgs/pcre/spkg-install.in diff --git a/build/pkgs/perl_cpan_polymake_prereq/SPKG.rst b/build/pkgs/perl_cpan_polymake_prereq/SPKG.rst new file mode 100644 index 00000000000..31cc724b335 --- /dev/null +++ b/build/pkgs/perl_cpan_polymake_prereq/SPKG.rst @@ -0,0 +1,13 @@ +perl_cpan_polymake_prereq +========================= + +Description +----------- + +This script package represents all Perl packages that are prerequisites +for polymake. + +License +------- + +Various free software licenses diff --git a/build/pkgs/perl_cpan_polymake_prereq/distros/cpan.txt b/build/pkgs/perl_cpan_polymake_prereq/distros/cpan.txt new file mode 100644 index 00000000000..13e1dc9fc9e --- /dev/null +++ b/build/pkgs/perl_cpan_polymake_prereq/distros/cpan.txt @@ -0,0 +1 @@ +XML::Writer XML::LibXML XML::LibXSLT File::Slurp JSON SVG MongoDB diff --git a/build/pkgs/perl_cpan_polymake_prereq/distros/debian.txt b/build/pkgs/perl_cpan_polymake_prereq/distros/debian.txt new file mode 100644 index 00000000000..c82e032627a --- /dev/null +++ b/build/pkgs/perl_cpan_polymake_prereq/distros/debian.txt @@ -0,0 +1 @@ +libxml-libxslt-perl libxml-writer-perl libxml2-dev libperl-dev libfile-slurp-perl libjson-perl libsvg-perl libterm-readkey-perl libterm-readline-gnu-perl libmongodb-perl diff --git a/build/pkgs/perl_cpan_polymake_prereq/distros/fedora.txt b/build/pkgs/perl_cpan_polymake_prereq/distros/fedora.txt new file mode 100644 index 00000000000..1ea99958152 --- /dev/null +++ b/build/pkgs/perl_cpan_polymake_prereq/distros/fedora.txt @@ -0,0 +1,2 @@ +perl-ExtUtils-Embed perl-File-Slurp perl-JSON perl-MongoDB perl-Term-ReadLine-Gnu +perl-XML-Writer perl-XML-LibXML perl-XML-LibXSLT diff --git a/build/pkgs/perl_cpan_polymake_prereq/distros/gentoo.txt b/build/pkgs/perl_cpan_polymake_prereq/distros/gentoo.txt new file mode 100644 index 00000000000..ecfce6042c2 --- /dev/null +++ b/build/pkgs/perl_cpan_polymake_prereq/distros/gentoo.txt @@ -0,0 +1 @@ +XML-Writer XML-LibXML XML-LibXSLT File-Slurp dev-perl/Term-ReadLine-Gnu JSON SVG dev-perl/MongoDB diff --git a/build/pkgs/perl_cpan_polymake_prereq/spkg-configure.m4 b/build/pkgs/perl_cpan_polymake_prereq/spkg-configure.m4 new file mode 100644 index 00000000000..8f6c34500c8 --- /dev/null +++ b/build/pkgs/perl_cpan_polymake_prereq/spkg-configure.m4 @@ -0,0 +1,9 @@ +SAGE_SPKG_CONFIGURE( + [perl_cpan_polymake_prereq], [ + m4_pushdef([MODULES], m4_include(build/pkgs/perl_cpan_polymake_prereq/distros/cpan.txt)) + AX_PROG_PERL_MODULES(MODULES, + [], + [sage_spkg_install_perl_cpan_polymake_prereq=yes + ] + ) +]) diff --git a/build/pkgs/perl_cpan_polymake_prereq/spkg-install b/build/pkgs/perl_cpan_polymake_prereq/spkg-install new file mode 100755 index 00000000000..80d1688cd8f --- /dev/null +++ b/build/pkgs/perl_cpan_polymake_prereq/spkg-install @@ -0,0 +1,6 @@ +#! /usr/bin/env bash +echo Error: The Perl prerequisites of the package polymake are not installed. +echo Please install CPAN packages $(cat build/pkgs/perl_cpan_polymake_prereq/distros/cpan.txt) +echo manually, either using the system packages recommended by ./configure +echo or directly from CPAN. +exit 1 diff --git a/build/pkgs/perl_cpan_polymake_prereq/type b/build/pkgs/perl_cpan_polymake_prereq/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/perl_cpan_polymake_prereq/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/perl_term_readline_gnu/SPKG.rst b/build/pkgs/perl_term_readline_gnu/SPKG.rst new file mode 100644 index 00000000000..d9e8a1cf06c --- /dev/null +++ b/build/pkgs/perl_term_readline_gnu/SPKG.rst @@ -0,0 +1,25 @@ +perl_term_readline_gnu +====================== + +Description +----------- + +Perl extension for the GNU Readline/History Library + +Available on CPAN + +License +------- + +The Perl 5 License (Artistic 1 & GPL 1) + + +Upstream Contact +---------------- + +Hiroo HAYASHI + +Dependencies +------------ + +readline diff --git a/build/pkgs/perl_term_readline_gnu/SPKG.txt b/build/pkgs/perl_term_readline_gnu/SPKG.txt deleted file mode 100644 index 02fd66662ef..00000000000 --- a/build/pkgs/perl_term_readline_gnu/SPKG.txt +++ /dev/null @@ -1,19 +0,0 @@ -= perl_term_readline_gnu = - -== Description == - -Perl extension for the GNU Readline/History Library - -Available on CPAN - -== License == - -The Perl 5 License (Artistic 1 & GPL 1) - -== Upstream Contact == - -Hiroo HAYASHI - -== Dependencies == - -readline diff --git a/build/pkgs/perl_term_readline_gnu/distros/arch.txt b/build/pkgs/perl_term_readline_gnu/distros/arch.txt new file mode 100644 index 00000000000..2ce91c8024b --- /dev/null +++ b/build/pkgs/perl_term_readline_gnu/distros/arch.txt @@ -0,0 +1 @@ +perl-term-readline-gnu diff --git a/build/pkgs/perl_term_readline_gnu/distros/cpan.txt b/build/pkgs/perl_term_readline_gnu/distros/cpan.txt new file mode 100644 index 00000000000..dc17924e21e --- /dev/null +++ b/build/pkgs/perl_term_readline_gnu/distros/cpan.txt @@ -0,0 +1 @@ +Term::ReadLine::Gnu diff --git a/build/pkgs/perl_term_readline_gnu/distros/cygwin.txt b/build/pkgs/perl_term_readline_gnu/distros/cygwin.txt new file mode 100644 index 00000000000..3b6a90263dd --- /dev/null +++ b/build/pkgs/perl_term_readline_gnu/distros/cygwin.txt @@ -0,0 +1 @@ +perl-Term-ReadLine-Gnu diff --git a/build/pkgs/perl_term_readline_gnu/distros/gentoo.txt b/build/pkgs/perl_term_readline_gnu/distros/gentoo.txt new file mode 100644 index 00000000000..ac670015b65 --- /dev/null +++ b/build/pkgs/perl_term_readline_gnu/distros/gentoo.txt @@ -0,0 +1 @@ +dev-perl/Term-ReadLine-Gnu diff --git a/build/pkgs/perl_term_readline_gnu/distros/opensuse.txt b/build/pkgs/perl_term_readline_gnu/distros/opensuse.txt new file mode 100644 index 00000000000..3b6a90263dd --- /dev/null +++ b/build/pkgs/perl_term_readline_gnu/distros/opensuse.txt @@ -0,0 +1 @@ +perl-Term-ReadLine-Gnu diff --git a/build/pkgs/perl_term_readline_gnu/patches/0001-Always-use-ncurses.patch b/build/pkgs/perl_term_readline_gnu/patches/0001-Always-use-ncurses.patch index 83d194fc58d..1ef0370492a 100644 --- a/build/pkgs/perl_term_readline_gnu/patches/0001-Always-use-ncurses.patch +++ b/build/pkgs/perl_term_readline_gnu/patches/0001-Always-use-ncurses.patch @@ -1,7 +1,7 @@ From e9fbab6c37a1955bdf44d8e7ab3fe125351e4aad Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 6 Apr 2018 15:17:41 -0500 -Subject: [PATCH] Always use ncurses +Subject: [PATCH 1/3] Always use ncurses --- Makefile.PL | 18 ++---------------- @@ -37,5 +37,5 @@ index ffd72c1..208aae0 100644 # Latest Perl in FreeBSD does not need this hack. (Dec.2002) $libs .= ' -lcrypt' if ($Config{osname} =~ /freebsd/i); -- -2.11.0 +2.24.1.1484.g7fcb965970 diff --git a/build/pkgs/perl_term_readline_gnu/patches/0002-Makefile.PL-Include-CFLAGS-LDFLAGS.patch b/build/pkgs/perl_term_readline_gnu/patches/0002-Makefile.PL-Include-CFLAGS-LDFLAGS.patch new file mode 100644 index 00000000000..f0b3d5fe5c3 --- /dev/null +++ b/build/pkgs/perl_term_readline_gnu/patches/0002-Makefile.PL-Include-CFLAGS-LDFLAGS.patch @@ -0,0 +1,51 @@ +From 060a5f4b359f08de92171b838ba9ac7ec9cf5697 Mon Sep 17 00:00:00 2001 +From: Isuru Fernando +Date: Thu, 30 Apr 2020 23:21:46 -0700 +Subject: [PATCH 2/3] Makefile.PL: Include CFLAGS, LDFLAGS + +--- + Makefile.PL | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/Makefile.PL b/Makefile.PL +index 208aae0..737fa88 100644 +--- a/Makefile.PL ++++ b/Makefile.PL +@@ -124,14 +124,14 @@ WriteMakefile + VERSION_FROM => 'Gnu.pm', + MIN_PERL_VERSION => '5.8.1', + LIBS => [ "$RLLIB $libs" ], +- LDDLFLAGS => "$RLLIB $Config{lddlflags}", ++ LDDLFLAGS => "$RLLIB $Config{lddlflags} $ENV{LDFLAGS}", + dynamic_lib => { OTHERLDFLAGS => $lddflags }, + DEFINE => $defs, + ($Config{osname} eq 'os2' ? + ( + IMPORTS => { xfree => 'emxlibcm.401' }, # Yuck! + ) : () ), +- INC => $RLINC, ++ INC => "$RLINC $ENV{CFLAGS}", + dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz' }, + clean => { FILES => "rlver.c rlver$Config{_exe} rlmalloc.c rlmalloc$Config{_exe}" }, + ); +@@ -200,7 +200,7 @@ EOF + close(F); + + # compile it +- my $comp_cmd = "$Config{cc} $RLINC $Config{ccflags} $defs $frlver -o rlver $RLLIB $lddflags $Config{ldflags} $libs"; ++ my $comp_cmd = "$Config{cc} $RLINC $Config{ccflags} $ENV{CFLAGS} $defs $frlver -o rlver $RLLIB $lddflags $Config{ldflags} $ENV{LDFLAGS} $libs"; + print $comp_cmd, "\n"; + system($comp_cmd); + if ($?) { +@@ -302,7 +302,7 @@ EOF + for my $symbol_set (@symbol_sets) { + my $xdef = join " ", map "-D$_=$symbol_set->{$_}", sort keys %$symbol_set; + # compile it +- my $comp_cmd = "$Config{cc} $RLINC $Config{ccflags} $defs $xdef $frlmalloc -o rlmalloc $RLLIB $lddflags $Config{ldflags} $libs"; ++ my $comp_cmd = "$Config{cc} $RLINC $Config{ccflags} $ENV{CFLAGS} $defs $xdef $frlmalloc -o rlmalloc $RLLIB $lddflags $Config{ldflags} $ENV{LDFLAGS} $libs"; + print $comp_cmd, "\n"; + unless (system($comp_cmd) || `./rlmalloc` !~ /^ok$/ || $?) { + $extra_defs = $xdef; +-- +2.24.1.1484.g7fcb965970 + diff --git a/build/pkgs/perl_term_readline_gnu/patches/0003-Need-LDFLAGS-in-LIBS-too.patch b/build/pkgs/perl_term_readline_gnu/patches/0003-Need-LDFLAGS-in-LIBS-too.patch new file mode 100644 index 00000000000..84991378f06 --- /dev/null +++ b/build/pkgs/perl_term_readline_gnu/patches/0003-Need-LDFLAGS-in-LIBS-too.patch @@ -0,0 +1,25 @@ +From e449c91dcb7881c252bb256d2b426d9de41ef16b Mon Sep 17 00:00:00 2001 +From: Isuru Fernando +Date: Fri, 1 May 2020 01:31:04 -0500 +Subject: [PATCH 3/3] Need LDFLAGS in LIBS too + +--- + Makefile.PL | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Makefile.PL b/Makefile.PL +index 737fa88..83d73c0 100644 +--- a/Makefile.PL ++++ b/Makefile.PL +@@ -123,7 +123,7 @@ WriteMakefile + }, + VERSION_FROM => 'Gnu.pm', + MIN_PERL_VERSION => '5.8.1', +- LIBS => [ "$RLLIB $libs" ], ++ LIBS => [ "$RLLIB $ENV{LDFLAGS} $libs" ], + LDDLFLAGS => "$RLLIB $Config{lddlflags} $ENV{LDFLAGS}", + dynamic_lib => { OTHERLDFLAGS => $lddflags }, + DEFINE => $defs, +-- +2.24.1.1484.g7fcb965970 + diff --git a/build/pkgs/perl_term_readline_gnu/spkg-configure.m4 b/build/pkgs/perl_term_readline_gnu/spkg-configure.m4 index 4c75a32e13f..471dcb20991 100644 --- a/build/pkgs/perl_term_readline_gnu/spkg-configure.m4 +++ b/build/pkgs/perl_term_readline_gnu/spkg-configure.m4 @@ -2,7 +2,8 @@ SAGE_SPKG_CONFIGURE( [perl_term_readline_gnu], [dnl direct testing for Term::ReadLine::Gnu does not work AX_PROG_PERL_MODULES( Term::ReadLine, [dnl check that it's a GNU one AC_MSG_CHECKING( Term::ReadLine module...) - $PERL -e "use Term::ReadLine; Term::ReadLine->get_all_function_names" > /dev/null 2>&1 + # #29563 TERM needs to be set to a value, or Term::ReadLine::Gnu may refuse to load + TERM=vt100 $PERL -e "use Term::ReadLine; Term::ReadLine->get_all_function_names" > /dev/null 2>&1 if test $? -ne 0; then AC_MSG_RESULT(non-GNU) sage_spkg_install_perl_term_readline_gnu=yes diff --git a/build/pkgs/perl_term_readline_gnu/spkg-install b/build/pkgs/perl_term_readline_gnu/spkg-install deleted file mode 100644 index e84a410ad6d..00000000000 --- a/build/pkgs/perl_term_readline_gnu/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -# In the configure phase, the package fails to use rpath for a test -# program that it compiles, causing a build failure on Linux as -# reported in #22505 (libreadline.so not found). We work around it by -# using LD_LIBRARY_PATH. -export LD_LIBRARY_PATH="$SAGE_LOCAL/lib":"$LD_LIBRARY_PATH" -perl Makefile.PL --prefix="$SAGE_LOCAL" INSTALL_BASE="$SAGE_LOCAL" -$MAKE install diff --git a/build/pkgs/perl_term_readline_gnu/spkg-install.in b/build/pkgs/perl_term_readline_gnu/spkg-install.in new file mode 100644 index 00000000000..9bf33372d2a --- /dev/null +++ b/build/pkgs/perl_term_readline_gnu/spkg-install.in @@ -0,0 +1,25 @@ +cd src + +case $(uname) in + Darwin) + # #21175/#29408: Set the ARCHFLAGS environment variable. This + # is to make sure that the output of perl -MExtUtils::Embed + # -e ldopts and perl -MExtUtils::Embed -e ccflags (picked up + # from system perl) do not contain -arch flags incompatible + # with our gcc. + export ARCHFLAGS="" + # Perl does some weird things with -Wl,-rpath commands. + # Add a workaround to those + if [[ "$($CC --version)" == *clang* ]]; then + export LDFLAGS=$(echo $LDFLAGS | sed "s/-Wl,-rpath,/-Xlinker -rpath -Xlinker /g") + fi + ;; +esac + +# In the configure phase, the package fails to use rpath for a test +# program that it compiles, causing a build failure on Linux as +# reported in #22505 (libreadline.so not found). We work around it by +# using LD_LIBRARY_PATH. +export LD_LIBRARY_PATH="$SAGE_LOCAL/lib":"$LD_LIBRARY_PATH" +perl Makefile.PL --prefix="$SAGE_LOCAL" INSTALL_BASE="$SAGE_LOCAL" +$MAKE install diff --git a/build/pkgs/pexpect/SPKG.rst b/build/pkgs/pexpect/SPKG.rst new file mode 100644 index 00000000000..85be49569b1 --- /dev/null +++ b/build/pkgs/pexpect/SPKG.rst @@ -0,0 +1,27 @@ +pexpect +======= + +Description +----------- + +Pexpect is a pure Python module for spawning child applications; +controlling them; and responding to expected patterns in their output. + +License +------- + +ISC license: http://opensource.org/licenses/isc-license.txt This license +is approved by the OSI and FSF as GPL-compatible. + + +Upstream Contact +---------------- + +- http://pexpect.readthedocs.org/en/stable/ +- https://github.com/pexpect/pexpect + +Dependencies +------------ + +- GNU patch +- Python diff --git a/build/pkgs/pexpect/SPKG.txt b/build/pkgs/pexpect/SPKG.txt deleted file mode 100644 index b2485eccedf..00000000000 --- a/build/pkgs/pexpect/SPKG.txt +++ /dev/null @@ -1,21 +0,0 @@ -= pexpect = - -== Description == - -Pexpect is a pure Python module for spawning child applications; -controlling them; and responding to expected patterns in their output. - -== License == - -ISC license: http://opensource.org/licenses/isc-license.txt -This license is approved by the OSI and FSF as GPL-compatible. - -== Upstream Contact == - -http://pexpect.readthedocs.org/en/stable/ -https://github.com/pexpect/pexpect - -== Dependencies == - - * GNU patch - * Python diff --git a/build/pkgs/pexpect/dependencies b/build/pkgs/pexpect/dependencies index 75051ac8933..c320fcc874e 100644 --- a/build/pkgs/pexpect/dependencies +++ b/build/pkgs/pexpect/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) ptyprocess | pip +$(PYTHON) ptyprocess | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pexpect/spkg-install b/build/pkgs/pexpect/spkg-install.in similarity index 100% rename from build/pkgs/pexpect/spkg-install rename to build/pkgs/pexpect/spkg-install.in diff --git a/build/pkgs/pickleshare/SPKG.rst b/build/pkgs/pickleshare/SPKG.rst new file mode 100644 index 00000000000..812cf0b930f --- /dev/null +++ b/build/pkgs/pickleshare/SPKG.rst @@ -0,0 +1,16 @@ +pickleshare +=========== + +Description +----------- + +PickleShare - a small 'shelve' like datastore with concurrency support + +Like shelve, a PickleShareDB object acts like a normal dictionary. +Unlike shelve, many processes can access the database simultaneously. +Changing a value in database is immediately visible to other processes +accessing the same database. + +Concurrency is possible because the values are stored in separate files. +Hence the "database" is a directory where all files are governed by +PickleShare. diff --git a/build/pkgs/pickleshare/SPKG.txt b/build/pkgs/pickleshare/SPKG.txt deleted file mode 100644 index 3e67a43c105..00000000000 --- a/build/pkgs/pickleshare/SPKG.txt +++ /dev/null @@ -1,14 +0,0 @@ -= pickleshare = - -== Description == - -PickleShare - a small 'shelve' like datastore with concurrency support - -Like shelve, a PickleShareDB object acts like a normal dictionary. Unlike -shelve, many processes can access the database simultaneously. Changing a -value in database is immediately visible to other processes accessing the -same database. - -Concurrency is possible because the values are stored in separate files. -Hence the "database" is a directory where all files are governed by -PickleShare. diff --git a/build/pkgs/pickleshare/dependencies b/build/pkgs/pickleshare/dependencies index b13bdf2e19c..7b6f95c9d9b 100644 --- a/build/pkgs/pickleshare/dependencies +++ b/build/pkgs/pickleshare/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) pathpy pathlib2 | setuptools pip +$(PYTHON) pathpy | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/nbconvert/spkg-install b/build/pkgs/pickleshare/spkg-install.in similarity index 100% rename from build/pkgs/nbconvert/spkg-install rename to build/pkgs/pickleshare/spkg-install.in diff --git a/build/pkgs/pillow/SPKG.rst b/build/pkgs/pillow/SPKG.rst new file mode 100644 index 00000000000..a3c2fbac3ca --- /dev/null +++ b/build/pkgs/pillow/SPKG.rst @@ -0,0 +1,27 @@ +Pillow +====== + +Description +----------- + +Pillow is the "friendly" PIL fork by Alex Clark and Contributors. + +The Python Imaging Library (PIL) adds powerful image processing and +graphics capabilities to Python. The library supports many file formats. + +License +------- + +Standard PIL License + + +Upstream Contact +---------------- + +- Author: Alex Clark +- Homepage: http://python-imaging.github.io/ + +Dependencies +------------ + +- Python diff --git a/build/pkgs/pillow/SPKG.txt b/build/pkgs/pillow/SPKG.txt deleted file mode 100644 index 340b0cbc351..00000000000 --- a/build/pkgs/pillow/SPKG.txt +++ /dev/null @@ -1,22 +0,0 @@ -= Pillow = - -== Description == - -Pillow is the "friendly" PIL fork by Alex Clark and Contributors. - -The Python Imaging Library (PIL) adds powerful image processing and -graphics capabilities to Python. The library supports many file -formats. - -== License == - -Standard PIL License - -== Upstream Contact == - -Author: Alex Clark -Homepage: http://python-imaging.github.io/ - -== Dependencies == - - * Python diff --git a/build/pkgs/pillow/checksums.ini b/build/pkgs/pillow/checksums.ini index 83633e72667..cddbf885333 100644 --- a/build/pkgs/pillow/checksums.ini +++ b/build/pkgs/pillow/checksums.ini @@ -1,4 +1,5 @@ tarball=Pillow-VERSION.tar.gz -sha1=b1d2d766b82efe28958025e4b3ee109e591cb483 -md5=0da5a4c9c548aa5cfe999302aea8c8f1 -cksum=379384082 +sha1=aa3ea4f9df720f210f9f7c4dde75b247bf76ef61 +md5=3da1c992ce9fcc67597d83036f591712 +cksum=88669447 +upstream_url=https://pypi.io/packages/source/p/pillow/Pillow-VERSION.tar.gz diff --git a/build/pkgs/pillow/dependencies b/build/pkgs/pillow/dependencies index dbe5d796c45..48498b499f5 100644 --- a/build/pkgs/pillow/dependencies +++ b/build/pkgs/pillow/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) setuptools +$(PYTHON) zlib freetype | $(PYTHON_TOOLCHAIN) pkgconf ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pillow/package-version.txt b/build/pkgs/pillow/package-version.txt index 19369614319..0ee843cc604 100644 --- a/build/pkgs/pillow/package-version.txt +++ b/build/pkgs/pillow/package-version.txt @@ -1 +1 @@ -5.3.0.p0 +7.2.0 diff --git a/build/pkgs/pillow/spkg-install b/build/pkgs/pillow/spkg-install.in similarity index 100% rename from build/pkgs/pillow/spkg-install rename to build/pkgs/pillow/spkg-install.in diff --git a/build/pkgs/pip/SPKG.rst b/build/pkgs/pip/SPKG.rst new file mode 100644 index 00000000000..92ded395cc5 --- /dev/null +++ b/build/pkgs/pip/SPKG.rst @@ -0,0 +1,31 @@ +pip +=== + +Description +----------- + +This package installs pip, the tool for installing and managing Python +packages, such as those found in the Python Package Index. It’s a +replacement for easy_install. + +License +------- + +MIT + + +Upstream Contact +---------------- + +- Project Page: https://github.com/pypa/pip +- Install howto: https://pip.pypa.io/en/latest/installing.html +- Changelog: https://pip.pypa.io/en/latest/news.html +- Bug Tracking: https://github.com/pypa/pip/issues +- Mailing list: http://groups.google.com/group/python-virtualenv +- Docs: https://pip.pypa.io/ + +Dependencies +------------ + +- python +- setuptools diff --git a/build/pkgs/pip/SPKG.txt b/build/pkgs/pip/SPKG.txt deleted file mode 100644 index ebdd18450ed..00000000000 --- a/build/pkgs/pip/SPKG.txt +++ /dev/null @@ -1,26 +0,0 @@ -= pip = - -== Description == - -This package installs pip, the tool for installing and managing Python packages, -such as those found in the Python Package Index. It’s a replacement for -easy_install. - -== License == - -MIT - -== Upstream Contact == - -Project Page: https://github.com/pypa/pip -Install howto: https://pip.pypa.io/en/latest/installing.html -Changelog: https://pip.pypa.io/en/latest/news.html -Bug Tracking: https://github.com/pypa/pip/issues -Mailing list: http://groups.google.com/group/python-virtualenv -Docs: https://pip.pypa.io/ - -== Dependencies == - -* python -* setuptools - diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index a11ba827600..a3a9f987369 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,4 +1,5 @@ tarball=pip-VERSION.tar.gz -sha1=1226368a8d39bd8b945517b6f7cb9802b279564e -md5=75cad449ad62c88b22de317a26781714 -cksum=886798891 +sha1=68e2ac7462489518db27eba9ade8be39e40798c9 +md5=62fa8775c44b070c5e1a3f44b0b6ccc5 +cksum=2731990114 +upstream_url=https://pypi.io/packages/source/p/pip/pip-VERSION.tar.gz diff --git a/build/pkgs/pip/dependencies b/build/pkgs/pip/dependencies index 6fb99bfe3f2..6f2aa240c02 100644 --- a/build/pkgs/pip/dependencies +++ b/build/pkgs/pip/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) python3 setuptools +$(PYTHON) setuptools wheel ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index 33718932a44..2a0e5e0a153 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -18.1 +20.1.1 diff --git a/build/pkgs/pip/spkg-install b/build/pkgs/pip/spkg-install deleted file mode 100644 index f7243906fb7..00000000000 --- a/build/pkgs/pip/spkg-install +++ /dev/null @@ -1,22 +0,0 @@ -cd src - - -# pip can install itself! But first we need to ensure that the pip -# source directory in on the PYTHONPATH -export PYTHONPATH="$(pwd)/src" - -# need to use --upgrade or --ignore-installed; Otherwise pip, which is -# importing itself, will think itself is already installed -# -if [ "$SAGE_PYTHON3" = "yes" ]; then - versions="3" -else - # Install pip3 first so pip2 overwrites local/bin/pip - versions="3 2" -fi - -for vers in $versions; do - python${vers} -m pip install --verbose --no-index --ignore-installed \ - --no-build-isolation --isolated --root="$SAGE_DESTDIR" . || \ - sdh_die "Error building / installing pip${vers}" -done diff --git a/build/pkgs/pip/spkg-install.in b/build/pkgs/pip/spkg-install.in new file mode 100644 index 00000000000..df71edf7d2e --- /dev/null +++ b/build/pkgs/pip/spkg-install.in @@ -0,0 +1,17 @@ +cd src + + +# pip can install itself! But first we need to ensure that the pip +# source directory in on the PYTHONPATH +export PYTHONPATH="$(pwd)/src" + +# need to use --upgrade or --ignore-installed; Otherwise pip, which is +# importing itself, will think itself is already installed +# +versions=3 + +for vers in $versions; do + python${vers} -m pip install --verbose --no-index --ignore-installed \ + --no-build-isolation --isolated --root="$SAGE_DESTDIR" . || \ + sdh_die "Error building / installing pip${vers}" +done diff --git a/build/pkgs/pkgconf/SPKG.rst b/build/pkgs/pkgconf/SPKG.rst new file mode 100644 index 00000000000..3916889b08a --- /dev/null +++ b/build/pkgs/pkgconf/SPKG.rst @@ -0,0 +1,33 @@ +pkgconf +======= + +Description +----------- + +Pkgconf is an implementation of the pkg-config spec with minimal +dependencies. + +License +------- + +ISC License (equivalent to Simplified BSD) + + +Upstream Contact +---------------- + +https://github.com/pkgconf/pkgconf + +Dependencies +------------ + +- C compiler + toolchain + + +Special Update/Build Instructions +--------------------------------- + +- install.patch: Use install script from AC_PROG_INSTALL + +Pkgconf is used in bzip2, so we must not use the bzip2-compressed +tarball. diff --git a/build/pkgs/pkgconf/SPKG.txt b/build/pkgs/pkgconf/SPKG.txt deleted file mode 100644 index 1ba83f5b08a..00000000000 --- a/build/pkgs/pkgconf/SPKG.txt +++ /dev/null @@ -1,25 +0,0 @@ -= pkgconf = - -== Description == - -Pkgconf is an implementation of the pkg-config spec with minimal -dependencies. - -== License == - -ISC License (equivalent to Simplified BSD) - -== Upstream Contact == - -https://github.com/pkgconf/pkgconf - -== Dependencies == - -* C compiler + toolchain - -== Special Update/Build Instructions == - -* install.patch: Use install script from AC_PROG_INSTALL - -Pkgconf is used in bzip2, so we must not use the bzip2-compressed -tarball. diff --git a/build/pkgs/pkgconf/distros/homebrew.txt b/build/pkgs/pkgconf/distros/homebrew.txt new file mode 100644 index 00000000000..6d2c214eb6d --- /dev/null +++ b/build/pkgs/pkgconf/distros/homebrew.txt @@ -0,0 +1 @@ +pkg-config diff --git a/build/pkgs/pkgconf/spkg-check b/build/pkgs/pkgconf/spkg-check.in similarity index 100% rename from build/pkgs/pkgconf/spkg-check rename to build/pkgs/pkgconf/spkg-check.in diff --git a/build/pkgs/pkgconf/spkg-install b/build/pkgs/pkgconf/spkg-install.in similarity index 100% rename from build/pkgs/pkgconf/spkg-install rename to build/pkgs/pkgconf/spkg-install.in diff --git a/build/pkgs/pkgconf/spkg-postinst b/build/pkgs/pkgconf/spkg-postinst.in similarity index 100% rename from build/pkgs/pkgconf/spkg-postinst rename to build/pkgs/pkgconf/spkg-postinst.in diff --git a/build/pkgs/pkgconf/spkg-postrm b/build/pkgs/pkgconf/spkg-postrm.in similarity index 100% rename from build/pkgs/pkgconf/spkg-postrm rename to build/pkgs/pkgconf/spkg-postrm.in diff --git a/build/pkgs/pkgconfig/SPKG.rst b/build/pkgs/pkgconfig/SPKG.rst new file mode 100644 index 00000000000..f027440c23d --- /dev/null +++ b/build/pkgs/pkgconfig/SPKG.rst @@ -0,0 +1,32 @@ +pkgconfig +========= + +Description +----------- + +Pkgconfig is a Python module to interface with the pkg-config command +line tool. + +License +------- + +MIT License + + +Upstream Contact +---------------- + +https://github.com/matze/pkgconfig + +Dependencies +------------ + +- Python 2.6+ + + +Special Update/Build Instructions +--------------------------------- + +Standard setup.py + +- remove_nose.patch: Remove the nose dependency (not actually used) diff --git a/build/pkgs/pkgconfig/SPKG.txt b/build/pkgs/pkgconfig/SPKG.txt deleted file mode 100644 index 13b53ecbb6e..00000000000 --- a/build/pkgs/pkgconfig/SPKG.txt +++ /dev/null @@ -1,24 +0,0 @@ -= pkgconfig = - -== Description == - -Pkgconfig is a Python module to interface with the pkg-config command -line tool. - -== License == - -MIT License - -== Upstream Contact == - -https://github.com/matze/pkgconfig - -== Dependencies == - -* Python 2.6+ - -== Special Update/Build Instructions == - -Standard setup.py - -* remove_nose.patch: Remove the nose dependency (not actually used) diff --git a/build/pkgs/pkgconfig/dependencies b/build/pkgs/pkgconfig/dependencies index 2573414ce8a..628b27750cd 100644 --- a/build/pkgs/pkgconfig/dependencies +++ b/build/pkgs/pkgconfig/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) pkgconf ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pkgconfig/spkg-install b/build/pkgs/pkgconfig/spkg-install deleted file mode 100644 index d14edc90bcd..00000000000 --- a/build/pkgs/pkgconfig/spkg-install +++ /dev/null @@ -1,8 +0,0 @@ -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo >&2 "Error installing pkgconfig." - exit 1 -fi diff --git a/build/pkgs/pkgconfig/spkg-install.in b/build/pkgs/pkgconfig/spkg-install.in new file mode 100644 index 00000000000..761190e309c --- /dev/null +++ b/build/pkgs/pkgconfig/spkg-install.in @@ -0,0 +1,13 @@ +cd src + +# Make sure that modern pip uses the generated setup.py +# that is distributed with the PyPI tarball, +# so we do not have to have poetry. Trac #29803. +rm -f pyproject.toml + +sdh_pip_install . + +if [ $? -ne 0 ]; then + echo >&2 "Error installing pkgconfig." + exit 1 +fi diff --git a/build/pkgs/planarity/SPKG.rst b/build/pkgs/planarity/SPKG.rst new file mode 100644 index 00000000000..be0b86d7f96 --- /dev/null +++ b/build/pkgs/planarity/SPKG.rst @@ -0,0 +1,46 @@ +planarity +========= + +Description +----------- + +This code project provides a library for implementing graph algorithms +as well as implementations of several planarity-related graph +algorithms. The origin of this project is the reference implementation +for the Edge Addition Planarity Algorithm [1], which is now the fastest +and simplest linear-time method for planar graph embedding and planarity +obstruction isolation (i.e. Kuratowski subgraph isolation). + +[1] http://dx.doi.org/10.7155/jgaa.00091 + +License +------- + +New BSD License + + +Upstream Contact +---------------- + +- https://github.com/graph-algorithms/edge-addition-planarity-suite/ + +- John Boyer + +Dependencies +------------ + +None + + +Special Update/Build Instructions +--------------------------------- + +The tarballs can be found at, +https://github.com/graph-algorithms/edge-addition-planarity-suite/releases +sage tarball is repackaged after running autogen.sh + +One change was made to the upstream code: + +- extern.patch - declare variables declared in headers as extern. + + https://github.com/graph-algorithms/edge-addition-planarity-suite/pull/3 diff --git a/build/pkgs/planarity/SPKG.txt b/build/pkgs/planarity/SPKG.txt deleted file mode 100644 index 6f5a75eafd5..00000000000 --- a/build/pkgs/planarity/SPKG.txt +++ /dev/null @@ -1,36 +0,0 @@ -= planarity = - -== Description == - -This code project provides a library for implementing graph algorithms as well -as implementations of several planarity-related graph algorithms. The origin of -this project is the reference implementation for the Edge Addition Planarity -Algorithm [1], which is now the fastest and simplest linear-time method for -planar graph embedding and planarity obstruction isolation (i.e. Kuratowski -subgraph isolation). - -[1] http://dx.doi.org/10.7155/jgaa.00091 - -== License == - -New BSD License - -== Upstream Contact == - -* https://github.com/graph-algorithms/edge-addition-planarity-suite/ - -* John Boyer - -== Dependencies == - -None - -== Special Update/Build Instructions == - -The tarballs can be found at, -https://github.com/graph-algorithms/edge-addition-planarity-suite/releases -sage tarball is repackaged after running autogen.sh - -One change was made to the upstream code: -- extern.patch - declare variables declared in headers as extern. - https://github.com/graph-algorithms/edge-addition-planarity-suite/pull/3 diff --git a/build/pkgs/planarity/distros/debian.txt b/build/pkgs/planarity/distros/debian.txt new file mode 100644 index 00000000000..33ebf0bb6d8 --- /dev/null +++ b/build/pkgs/planarity/distros/debian.txt @@ -0,0 +1 @@ +libplanarity-dev planarity diff --git a/build/pkgs/planarity/distros/fedora.txt b/build/pkgs/planarity/distros/fedora.txt new file mode 100644 index 00000000000..b107ebbf96f --- /dev/null +++ b/build/pkgs/planarity/distros/fedora.txt @@ -0,0 +1 @@ +planarity planarity-devel diff --git a/build/pkgs/planarity/distros/gentoo.txt b/build/pkgs/planarity/distros/gentoo.txt new file mode 100644 index 00000000000..51b174a6c70 --- /dev/null +++ b/build/pkgs/planarity/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/planarity diff --git a/build/pkgs/planarity/spkg-install b/build/pkgs/planarity/spkg-install.in similarity index 100% rename from build/pkgs/planarity/spkg-install rename to build/pkgs/planarity/spkg-install.in diff --git a/build/pkgs/plantri/SPKG.rst b/build/pkgs/plantri/SPKG.rst new file mode 100644 index 00000000000..3ae1cc2e1e0 --- /dev/null +++ b/build/pkgs/plantri/SPKG.rst @@ -0,0 +1,42 @@ +Plantri +======= + +Description +----------- + +Plantri is a program that generates certain types of graphs that are +imbedded on the sphere. + +Exactly one member of each isomorphism class is output, using an amount +of memory almost independent of the number of graphs produced. This, +together with the exceptionally fast operation and careful validation, +makes the program suitable for processing very large numbers of graphs. + +Isomorphisms are defined with respect to the embeddings, so in some +cases outputs may be isomorphic as abstract graphs. + +License +------- + +Plantri is distributed without a license. + + +Upstream Contact +---------------- + +Gunnar Brinkmann + +- University of Ghent +- Gunnar.Brinkmann@ugent.be + +Brendan McKay + +- Australian National University +- bdm@cs.anu.edu.au + +See http://cs.anu.edu.au/~bdm/plantri + +Dependencies +------------ + +- None diff --git a/build/pkgs/plantri/SPKG.txt b/build/pkgs/plantri/SPKG.txt deleted file mode 100644 index 24defea2632..00000000000 --- a/build/pkgs/plantri/SPKG.txt +++ /dev/null @@ -1,38 +0,0 @@ -= Plantri = - -== Description == - -Plantri is a program that generates certain types of graphs that are -imbedded on the sphere. - -Exactly one member of each isomorphism class is output, using an -amount of memory almost independent of the number of graphs produced. -This, together with the exceptionally fast operation and careful -validation, makes the program suitable for processing very large -numbers of graphs. - -Isomorphisms are defined with respect to the embeddings, so in some -cases outputs may be isomorphic as abstract graphs. - -== License == -Plantri is distributed without a license. - -== Upstream Contact == -Gunnar Brinkmann - University of Ghent - Gunnar.Brinkmann@ugent.be - -Brendan McKay - Australian National University - bdm@cs.anu.edu.au - -See http://cs.anu.edu.au/~bdm/plantri - -== Dependencies == - * None - -== Changelog == - -=== plantri-4.5, 11th September 2014 === - * #16970: First release put into Sage. - diff --git a/build/pkgs/plantri/spkg-install b/build/pkgs/plantri/spkg-install.in similarity index 100% rename from build/pkgs/plantri/spkg-install rename to build/pkgs/plantri/spkg-install.in diff --git a/build/pkgs/polylib/SPKG.rst b/build/pkgs/polylib/SPKG.rst new file mode 100644 index 00000000000..54f0c438d6a --- /dev/null +++ b/build/pkgs/polylib/SPKG.rst @@ -0,0 +1,24 @@ +polylib +======= + +Description +----------- + +The Polyhedral Library (PolyLib for short) operates on objects made up +of unions of polyhedra of any dimension. polylib is a C library. + +License +------- + +GPL v3 + + +Upstream Contact +---------------- + +- https://groups.google.com/forum/#!forum/isl-development + +Dependencies +------------ + +- GMP diff --git a/build/pkgs/polylib/SPKG.txt b/build/pkgs/polylib/SPKG.txt deleted file mode 100644 index 5e31ce96964..00000000000 --- a/build/pkgs/polylib/SPKG.txt +++ /dev/null @@ -1,19 +0,0 @@ -= polylib = - -== Description == - -The Polyhedral Library (PolyLib for short) operates on objects made up of unions of polyhedra of any dimension. -polylib is a C library. - - -== License == - -GPL v3 - -== Upstream Contact == - -* https://groups.google.com/forum/#!forum/isl-development - -== Dependencies == - - * GMP diff --git a/build/pkgs/polylib/spkg-check b/build/pkgs/polylib/spkg-check deleted file mode 100644 index 8cd43978e7f..00000000000 --- a/build/pkgs/polylib/spkg-check +++ /dev/null @@ -1,2 +0,0 @@ -cd src -sdh_make check diff --git a/build/pkgs/polylib/spkg-check.in b/build/pkgs/polylib/spkg-check.in new file mode 100644 index 00000000000..45b317a382c --- /dev/null +++ b/build/pkgs/polylib/spkg-check.in @@ -0,0 +1,2 @@ +cd src +sdh_make_check diff --git a/build/pkgs/polylib/spkg-install b/build/pkgs/polylib/spkg-install.in similarity index 100% rename from build/pkgs/polylib/spkg-install rename to build/pkgs/polylib/spkg-install.in diff --git a/build/pkgs/polymake/SPKG.rst b/build/pkgs/polymake/SPKG.rst new file mode 100644 index 00000000000..8bb67899526 --- /dev/null +++ b/build/pkgs/polymake/SPKG.rst @@ -0,0 +1,76 @@ +Polymake +======== + +Description +----------- + +polymake is open source software for research in polyhedral geometry. It +deals with polytopes, polyhedra and fans as well as simplicial +complexes, matroids, graphs, tropical hypersurfaces, and other objects. +Supported platforms include various flavors of Linux, Free BSD and Mac +OS. + +License +------- + +- GPL v3 + + +Upstream Contact +---------------- + +- https://polymake.org/ + +Dependencies +------------ + +Polymake needs a working installation of Perl, including its shared +library and some modules (XML::Writer XML::LibXML XML::LibXSLT +Term::ReadLine::Gnu JSON SVG). The Polymake interface in Sage +additionally needs File::Slurp. For full functionality including +polymake's polyDB, also the Perl module MongoDB is required. + +These are not provided by a Sage package. The script package +perl_cpan_polymake_prereq will signal an error at build time if these +prerequisites are not met. + +The configure script will inform you about the equivalent system +packages that you should install. Otherwise, you can use CPAN (see +below). + +Sage might install the Term::ReadLine::Gnu module, however, when you +install polymake, if it is not provided by the system, or if Sage +installs its own readline library. + + +A distribution-independent way to install Perl modules (into a user's +home directory or /usr/local) is using CPAN. This is also the way to +install the modules on macOS. For this, if you don't have root access, +you will need the local::lib Perl module installed:: + + cpan -i XML::Writer XML::LibXML XML::LibXSLT File::Slurp Term::ReadLine::Gnu JSON SVG MongoDB + +Several Sage packages should be installed before installing the polymake +package to give a more featureful Polymake installation: + + sage -i 4ti2 latte_int topcom qhull + +Software that would need to be installed manually (no Sage package +available) for a more featureful Polymake installation: azove, porta, +vinci, SplitsTree4. + +Information on missing Polymake prerequisites after installing polymake:: + + $ sage -sh + (sage-sh) $ polymake + polytope> show_unconfigured; + + +Debugging polymake install problems +----------------------------------- + +:: + + # apt-get install libdevel-trace-perl + $ cd src + $ perl -d:Trace support/configure.pl diff --git a/build/pkgs/polymake/SPKG.txt b/build/pkgs/polymake/SPKG.txt deleted file mode 100644 index 2a20c29f8a6..00000000000 --- a/build/pkgs/polymake/SPKG.txt +++ /dev/null @@ -1,80 +0,0 @@ -= Polymake = - -== Description == - -polymake is open source software for research in polyhedral -geometry. It deals with polytopes, polyhedra and fans as -well as simplicial complexes, matroids, graphs, tropical -hypersurfaces, and other objects. Supported platforms -include various flavors of Linux, Free BSD and Mac OS. - -== License == - - * GPL v3 - -== Upstream Contact == - - * https://polymake.org/ - -== Dependencies == - -Polymake needs a working installation of Perl, including its shared -library and some modules (XML::Writer XML::LibXML XML::LibXSLT). -The Polymake interface in Sage additionally needs File::Slurp. -For full functionality including polymake's polyDB, -also install the Perl module MongoDB. - -These are not provided by a Sage package. - -Sage might install the Term::ReadLine::Gnu module, however, when you install polymake. -Alternatively, as of #27795, Sage will try to use system's -Term::ReadLine::Gnu, if available. - - -On Ubuntu/Debian, use: - sudo apt-get install libxml-libxslt-perl libxml-writer-perl libxml2-dev libperl-dev libfile-slurp-perl libjson-perl libsvg-perl libterm-readkey-perl libterm-readline-gnu-perl libmongodb-perl - -On Fedora 23, use: - sudo yum install perl-ExtUtils-Embed perl-File-Slurp perl-JSON perl-MongoDB perl-Term-ReadLine-Gnu - -On Gentoo, use - emerge --ask XML-Writer XML-LibXML XML-LibXSLT File-Slurp dev-perl/Term-ReadLine-Gnu JSON SVG dev-perl/MongoDB - -On Mac OS X, to build Term::Readline::Gnu, on macOS 10.14 (Mojave), one -needs to do the following: - - sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target / - -or a "fatal error: 'EXTERN.h' file not found" will be signalled. - - -A distribution-independent way to install Perl modules (into a user's -home directory) is using CPAN. This is also the way to install the -modules on macOS. For this, if you don't have root access, you will -need the local::lib Perl module installed. - - cpan -i XML::Writer XML::LibXML XML::LibXSLT File::Slurp Term::ReadLine::Gnu JSON SVG MongoDB - - -Several Sage packages should be installed before installing the -polymake package to give a more featureful Polymake installation: - sage -i 4ti2 latte_int topcom qhull - -Software that would need to be installed manually (no Sage package - available) for a more featureful Polymake installation: - azove, - porta, - vinci, - SplitsTree4 - -Information on missing Polymake prerequisites after installing -polymake: - $ sage -sh - (sage-sh) $ polymake - polytope> show_unconfigured; - -== Debugging polymake install problems == - -# apt-get install libdevel-trace-perl -$ cd src -$ perl -d:Trace support/configure.pl diff --git a/build/pkgs/polymake/dependencies b/build/pkgs/polymake/dependencies index 1b99fbf67de..94488d7fe95 100644 --- a/build/pkgs/polymake/dependencies +++ b/build/pkgs/polymake/dependencies @@ -1,4 +1,4 @@ -$(MP_LIBRARY) bliss cddlib lrslib normaliz perl_term_readline_gnu | ninja_build +$(MP_LIBRARY) bliss cddlib lrslib normaliz perl_term_readline_gnu ppl perl_cpan_polymake_prereq | ninja_build ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/polymake/spkg-check b/build/pkgs/polymake/spkg-check deleted file mode 100644 index 6e00b18fa67..00000000000 --- a/build/pkgs/polymake/spkg-check +++ /dev/null @@ -1,2 +0,0 @@ -cd src/ -sdh_make test diff --git a/build/pkgs/polymake/spkg-check.in b/build/pkgs/polymake/spkg-check.in new file mode 100644 index 00000000000..062b633fd53 --- /dev/null +++ b/build/pkgs/polymake/spkg-check.in @@ -0,0 +1,2 @@ +cd src/ +${MAKE:-make} test || sdh_die "Failures checking polymake" diff --git a/build/pkgs/polymake/spkg-install b/build/pkgs/polymake/spkg-install deleted file mode 100644 index c1af82b3cb5..00000000000 --- a/build/pkgs/polymake/spkg-install +++ /dev/null @@ -1,22 +0,0 @@ -cd src - -more_configure_options= - -if [ `uname` = Darwin ]; then - more_configure_options="$more_configure_options --without-fink" -fi - -# We disable SoPlex to avoid linking errors (#24905). -# Since polymake v3.4, it does not find our lrs installation if we do not provide --with-lrs explicitly. -./configure --without-java \ - --without-javaview \ - --without-soplex \ - --with-lrs="$SAGE_LOCAL" \ - --prefix="$SAGE_LOCAL" \ - --exec-prefix="$SAGE_LOCAL" \ - --includedir="$SAGE_LOCAL"/include \ - --bindir="$SAGE_LOCAL"/bin \ - --libdir="$SAGE_LOCAL"/lib \ - $more_configure_options || sdh_die "Error configuring Polymake" -sdh_make -sdh_make_install diff --git a/build/pkgs/polymake/spkg-install.in b/build/pkgs/polymake/spkg-install.in new file mode 100644 index 00000000000..5a74ff5c609 --- /dev/null +++ b/build/pkgs/polymake/spkg-install.in @@ -0,0 +1,30 @@ +cd src + +more_configure_options= + +case $(uname) in + Darwin) + more_configure_options="$more_configure_options --without-fink" + # #21175/#29408: Set the ARCHFLAGS environment variable. This + # is to make sure that the output of perl -MExtUtils::Embed + # -e ldopts and perl -MExtUtils::Embed -e ccflags (picked up + # from system perl) do not contain -arch flags incompatible + # with our gcc. + export ARCHFLAGS="" + ;; +esac + +# We disable SoPlex to avoid linking errors (#24905). +# Since polymake v3.4, it does not find our lrs installation if we do not provide --with-lrs explicitly. +./configure --without-java \ + --without-javaview \ + --without-soplex \ + --with-lrs="$SAGE_LOCAL" \ + --prefix="$SAGE_LOCAL" \ + --exec-prefix="$SAGE_LOCAL" \ + --includedir="$SAGE_LOCAL"/include \ + --bindir="$SAGE_LOCAL"/bin \ + --libdir="$SAGE_LOCAL"/lib \ + $more_configure_options || sdh_die "Error configuring Polymake" +sdh_make +sdh_make_install diff --git a/build/pkgs/polymake/spkg-legacy-uninstall b/build/pkgs/polymake/spkg-legacy-uninstall.in similarity index 100% rename from build/pkgs/polymake/spkg-legacy-uninstall rename to build/pkgs/polymake/spkg-legacy-uninstall.in diff --git a/build/pkgs/polymake/spkg-postinst b/build/pkgs/polymake/spkg-postinst.in similarity index 100% rename from build/pkgs/polymake/spkg-postinst rename to build/pkgs/polymake/spkg-postinst.in diff --git a/build/pkgs/polytopes_db/SPKG.rst b/build/pkgs/polytopes_db/SPKG.rst new file mode 100644 index 00000000000..a46d4d2fe18 --- /dev/null +++ b/build/pkgs/polytopes_db/SPKG.rst @@ -0,0 +1,27 @@ + +Reflexive Polytopes Databases +============================= + +Description +----------- + +This package includes lists of 2- and 3-dimensional reflexive polytopes. + +The list of polygons is quite easy to get and it has been known for a +while. The list of 3-polytopes was originally obtained by Maximilian +Kreuzer and Harald Skarke using their software PALP, which is included +into the standard distribution of Sage. To work with lattice and +reflexive polytopes from Sage you can use sage.geometry.lattice_polytope +module, which relies on PALP for some of its functionality. To get +access to the databases of this package, use ReflexivePolytope and +ReflexivePolytopes commands. + +License +------- + +GPL + +Dependencies +------------ + +None diff --git a/build/pkgs/polytopes_db/SPKG.txt b/build/pkgs/polytopes_db/SPKG.txt deleted file mode 100644 index 3f2aae989ff..00000000000 --- a/build/pkgs/polytopes_db/SPKG.txt +++ /dev/null @@ -1,21 +0,0 @@ -= Reflexive Polytopes Databases = - -== Description == - -This package includes lists of 2- and 3-dimensional reflexive polytopes. - -The list of polygons is quite easy to get and it has been known for a while. -The list of 3-polytopes was originally obtained by Maximilian Kreuzer and -Harald Skarke using their software PALP, which is included into the standard -distribution of Sage. To work with lattice and reflexive polytopes from Sage -you can use sage.geometry.lattice_polytope module, which relies on PALP for -some of its functionality. To get access to the databases of this package, use -ReflexivePolytope and ReflexivePolytopes commands. - -== License == - -GPL - -== Dependencies == - -None diff --git a/build/pkgs/polytopes_db/spkg-install b/build/pkgs/polytopes_db/spkg-install.in similarity index 100% rename from build/pkgs/polytopes_db/spkg-install rename to build/pkgs/polytopes_db/spkg-install.in diff --git a/build/pkgs/polytopes_db_4d/SPKG.txt b/build/pkgs/polytopes_db_4d/SPKG.txt new file mode 100644 index 00000000000..4f00c8f6d20 --- /dev/null +++ b/build/pkgs/polytopes_db_4d/SPKG.txt @@ -0,0 +1,30 @@ += 4D Reflexive Polytopes Database = + +== Description == + +This package contains the database of 4-d reflexive polytopes with +Hodge numbers as index. + +Based on the original list by Maximilian Kreuzer and Harald Skarke +using their software PALP. + +== License == + +GPL v2+ + +== SPKG Maintainers == + +Volker Braun + +== Dependencies == + +None + +== Changelog == + +=== polytopes_db_3d-1.0 (Volker Braun, 2013 April 17) === + + * #14467: Initial version + + + diff --git a/build/pkgs/polytopes_db_4d/checksums.ini b/build/pkgs/polytopes_db_4d/checksums.ini new file mode 100644 index 00000000000..edfee6b7841 --- /dev/null +++ b/build/pkgs/polytopes_db_4d/checksums.ini @@ -0,0 +1,5 @@ +tarball=polytopes_db_4d-VERSION.spkg +sha1=c9779821e365df2d7f9bc684f9e2ec0e95fb8650 +md5=fe775a26fd7b2afc187e9bfabfb1b86a +cksum=3415837678 +upstream_url=http://ftp.sparcs.org/sage/spkg/huge/polytopes_db_4d-1.0.spkg diff --git a/build/pkgs/polytopes_db_4d/package-version.txt b/build/pkgs/polytopes_db_4d/package-version.txt new file mode 100644 index 00000000000..d3827e75a5c --- /dev/null +++ b/build/pkgs/polytopes_db_4d/package-version.txt @@ -0,0 +1 @@ +1.0 diff --git a/build/pkgs/polytopes_db_4d/spkg-install.in b/build/pkgs/polytopes_db_4d/spkg-install.in new file mode 100755 index 00000000000..e4c57278ca3 --- /dev/null +++ b/build/pkgs/polytopes_db_4d/spkg-install.in @@ -0,0 +1,4 @@ +POLYTOPES_DIR="$SAGE_SHARE"/reflexive_polytopes +mkdir -p "$POLYTOPES_DIR" +cd src/src +cp -r -p Hodge4d "$POLYTOPES_DIR"/ diff --git a/build/pkgs/polytopes_db_4d/spkg-legacy-uninstall.in b/build/pkgs/polytopes_db_4d/spkg-legacy-uninstall.in new file mode 100755 index 00000000000..00afd5f6e92 --- /dev/null +++ b/build/pkgs/polytopes_db_4d/spkg-legacy-uninstall.in @@ -0,0 +1,2 @@ +# Cleanup of previous installation +rm -rf "${SAGE_SHARE}/reflexive_polytopes/Hodge4d" diff --git a/build/pkgs/polytopes_db_4d/type b/build/pkgs/polytopes_db_4d/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/polytopes_db_4d/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/ppl/SPKG.rst b/build/pkgs/ppl/SPKG.rst new file mode 100644 index 00000000000..e62faa3b989 --- /dev/null +++ b/build/pkgs/ppl/SPKG.rst @@ -0,0 +1,62 @@ + +Parma Polyhedra Library +======================= + +Description +----------- + +The Parma Polyhedra Library (PPL) provides numerical abstractions +especially targeted at applications in the field of analysis and +verification of complex systems. These abstractions include convex +polyhedra, defined as the intersection of a finite number of (open or +closed) halfspaces, each described by a linear inequality (strict or +non-strict) with rational coefficients; some special classes of +polyhedra shapes that offer interesting complexity/precision tradeoffs; +and grids which represent regularly spaced points that satisfy a set of +linear congruence relations. The library also supports finite powersets +and products of (any kind of) polyhedra and grids, a mixed integer +linear programming problem solver using an exact-arithmetic version of +the simplex algorithm, a parametric integer programming solver, and +primitives for the termination analysis via the automatic synthesis of +linear ranking functions. + +It is written in C++, but comes with interfaces to C, Java, OCaml, and +Prolog. PPL is one of the fastest implementations of polyhedral +computations. + +Benchmarks are included in this paper: https://arxiv.org/abs/cs/0612085 + +License +------- + +GPL v3+ + + +Upstream Contact +---------------- + +- http://www.cs.unipr.it/ppl/ +- BUGSENG srl (http://bugseng.com) + +Core Development Team + +- Roberto Bagnara (University of Parma) +- Patricia M. Hill (University of Parma) +- Enea Zaffanella (University of Parma) + +Dependencies +------------ + +- gmp (or mpir) + + +Special Update/Build Instructions +--------------------------------- + +Patches +~~~~~~~ + +- ptrdiff_t-ppl-1.1.patch: Fixes to compile with gcc 4.9; C++ name + lookup issue. + +- weak.patch: disable use of weak symbols on Cygwin64. diff --git a/build/pkgs/ppl/SPKG.txt b/build/pkgs/ppl/SPKG.txt deleted file mode 100644 index cc857a87158..00000000000 --- a/build/pkgs/ppl/SPKG.txt +++ /dev/null @@ -1,45 +0,0 @@ -= Parma Polyhedra Library = - -== Description == -The Parma Polyhedra Library (PPL) provides numerical abstractions -especially targeted at applications in the field of analysis and -verification of complex systems. These abstractions include convex -polyhedra, defined as the intersection of a finite number of (open or -closed) halfspaces, each described by a linear inequality (strict or -non-strict) with rational coefficients; some special classes of -polyhedra shapes that offer interesting complexity/precision -tradeoffs; and grids which represent regularly spaced points that -satisfy a set of linear congruence relations. The library also -supports finite powersets and products of (any kind of) polyhedra and -grids, a mixed integer linear programming problem solver using an -exact-arithmetic version of the simplex algorithm, a parametric -integer programming solver, and primitives for the termination -analysis via the automatic synthesis of linear ranking functions. - -It is written in C++, but comes with interfaces to C, Java, OCaml, and -Prolog. PPL is one of the fastest implementations of polyhedral -computations. - -Benchmarks are included in this paper: https://arxiv.org/abs/cs/0612085 - -== License == -GPL v3+ - -== Upstream Contact == -http://www.cs.unipr.it/ppl/ -BUGSENG srl (http://bugseng.com) - -Core Development Team -Roberto Bagnara (University of Parma) -Patricia M. Hill (University of Parma) -Enea Zaffanella (University of Parma) - -== Dependencies == -* gmp (or mpir) - -== Special Update/Build Instructions == - -=== Patches === -* ptrdiff_t-ppl-1.1.patch: Fixes to compile with gcc 4.9; C++ name - lookup issue. -* weak.patch: disable use of weak symbols on Cygwin64. diff --git a/build/pkgs/ppl/distros/arch.txt b/build/pkgs/ppl/distros/arch.txt new file mode 100644 index 00000000000..0efaae6634f --- /dev/null +++ b/build/pkgs/ppl/distros/arch.txt @@ -0,0 +1 @@ +ppl diff --git a/build/pkgs/ppl/distros/conda.txt b/build/pkgs/ppl/distros/conda.txt new file mode 100644 index 00000000000..0efaae6634f --- /dev/null +++ b/build/pkgs/ppl/distros/conda.txt @@ -0,0 +1 @@ +ppl diff --git a/build/pkgs/ppl/distros/debian.txt b/build/pkgs/ppl/distros/debian.txt new file mode 100644 index 00000000000..be1642b61b1 --- /dev/null +++ b/build/pkgs/ppl/distros/debian.txt @@ -0,0 +1,2 @@ +libppl-dev +ppl-dev diff --git a/build/pkgs/ppl/distros/fedora.txt b/build/pkgs/ppl/distros/fedora.txt new file mode 100644 index 00000000000..bed9ea7b421 --- /dev/null +++ b/build/pkgs/ppl/distros/fedora.txt @@ -0,0 +1,2 @@ +ppl +ppl-devel diff --git a/build/pkgs/ppl/distros/gentoo.txt b/build/pkgs/ppl/distros/gentoo.txt new file mode 100644 index 00000000000..11fd588324a --- /dev/null +++ b/build/pkgs/ppl/distros/gentoo.txt @@ -0,0 +1 @@ +dev-libs/ppl diff --git a/build/pkgs/ppl/distros/homebrew.txt b/build/pkgs/ppl/distros/homebrew.txt new file mode 100644 index 00000000000..0efaae6634f --- /dev/null +++ b/build/pkgs/ppl/distros/homebrew.txt @@ -0,0 +1 @@ +ppl diff --git a/build/pkgs/ppl/spkg-check b/build/pkgs/ppl/spkg-check.in similarity index 100% rename from build/pkgs/ppl/spkg-check rename to build/pkgs/ppl/spkg-check.in diff --git a/build/pkgs/ppl/spkg-configure.m4 b/build/pkgs/ppl/spkg-configure.m4 new file mode 100644 index 00000000000..257fb777acc --- /dev/null +++ b/build/pkgs/ppl/spkg-configure.m4 @@ -0,0 +1,17 @@ +SAGE_SPKG_CONFIGURE([ppl], [ + SAGE_SPKG_DEPCHECK([glpk gmp mpir], [ + # If our dependencies come from the system, then we can use the + # system ppl, too. This macro works sort-of like the + # PKG_CHECK_MODULES macro, defining e.g. PPL_CFLAGS when a + # suitable version of PPL is detected. The upstream version fails + # to differentiate between LDFLAGS and LIBS (which is in turn the + # fault of the ppl-config program), leading to argument-order + # problems on the command line. Our version of the macro + # defines PPL_LIBS separately so that we can distinguish the two. + AM_PATH_PPL([1.2], [ + LIBS="$LIBS $PPL_LIBS" + sage_spkg_install_ppl=no + ], + [sage_spkg_install_ppl=yes]) + ]) +]) diff --git a/build/pkgs/ppl/spkg-install b/build/pkgs/ppl/spkg-install.in similarity index 100% rename from build/pkgs/ppl/spkg-install rename to build/pkgs/ppl/spkg-install.in diff --git a/build/pkgs/pplpy/SPKG.rst b/build/pkgs/pplpy/SPKG.rst new file mode 100644 index 00000000000..2a7c7e5618e --- /dev/null +++ b/build/pkgs/pplpy/SPKG.rst @@ -0,0 +1,23 @@ +pplpy +===== + +Description +----------- + +PPL Python wrapper + +This Python package provides a wrapper to the C++ Parma Polyhedra +Library (PPL). + +The whole package started as a fork of a tiny part of the Sage software. + +License +------- + +GPL version 3 + + +Upstream Contact +---------------- + +- https://github.com/videlec/pplpy diff --git a/build/pkgs/pplpy/SPKG.txt b/build/pkgs/pplpy/SPKG.txt deleted file mode 100644 index 2e486a634fe..00000000000 --- a/build/pkgs/pplpy/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= pplpy = - -== Description == - -PPL Python wrapper - -This Python package provides a wrapper to the C++ Parma Polyhedra Library (PPL). - -The whole package started as a fork of a tiny part of the Sage software. - -== License == - -GPL version 3 - -== Upstream Contact == - -* https://github.com/videlec/pplpy diff --git a/build/pkgs/pplpy/dependencies b/build/pkgs/pplpy/dependencies index a36dbf85e2b..16e271bb5f9 100644 --- a/build/pkgs/pplpy/dependencies +++ b/build/pkgs/pplpy/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) $(MP_LIBRARY) gmpy2 cysignals mpfr mpc ppl | pip sphinx +$(PYTHON) $(MP_LIBRARY) gmpy2 cysignals mpfr mpc ppl | $(PYTHON_TOOLCHAIN) sphinx ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pplpy/spkg-check b/build/pkgs/pplpy/spkg-check.in similarity index 100% rename from build/pkgs/pplpy/spkg-check rename to build/pkgs/pplpy/spkg-check.in diff --git a/build/pkgs/pplpy/spkg-install b/build/pkgs/pplpy/spkg-install.in similarity index 100% rename from build/pkgs/pplpy/spkg-install rename to build/pkgs/pplpy/spkg-install.in diff --git a/build/pkgs/pplpy/spkg-postinst b/build/pkgs/pplpy/spkg-postinst.in similarity index 100% rename from build/pkgs/pplpy/spkg-postinst rename to build/pkgs/pplpy/spkg-postinst.in diff --git a/build/pkgs/pplpy/spkg-postrm b/build/pkgs/pplpy/spkg-postrm.in similarity index 100% rename from build/pkgs/pplpy/spkg-postrm rename to build/pkgs/pplpy/spkg-postrm.in diff --git a/build/pkgs/primecount/SPKG.rst b/build/pkgs/primecount/SPKG.rst new file mode 100644 index 00000000000..f01e3971e3c --- /dev/null +++ b/build/pkgs/primecount/SPKG.rst @@ -0,0 +1,21 @@ +primecount +========== + +Description +----------- + +primecount is a C++ implementation of several algorithms for counting +primes maintained by Kim Walisch. + +Website: https://github.com/kimwalisch/primecount/ + +License +------- + +primecount is licensed BSD 2 + + +Upstream Contact +---------------- + +- https://github.com/kimwalisch/primecount/ diff --git a/build/pkgs/primecount/SPKG.txt b/build/pkgs/primecount/SPKG.txt deleted file mode 100644 index 4f2011fc8a6..00000000000 --- a/build/pkgs/primecount/SPKG.txt +++ /dev/null @@ -1,16 +0,0 @@ -= primecount = - -== Description == - -primecount is a C++ implementation of several algorithms for -counting primes maintained by Kim Walisch. - -Website: https://github.com/kimwalisch/primecount/ - -== License == - -primecount is licensed BSD 2 - -== Upstream Contact == - - * https://github.com/kimwalisch/primecount/ diff --git a/build/pkgs/primecount/spkg-check b/build/pkgs/primecount/spkg-check deleted file mode 100644 index 59bd9be1618..00000000000 --- a/build/pkgs/primecount/spkg-check +++ /dev/null @@ -1,19 +0,0 @@ -############################################################################### -# -# primecount Sage check script -# -############################################################################### - -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src -$MAKE test - -if [ $? -ne 0 ]; then - echo >&2 "Error: primecount failed to pass its test suite." - exit 1 -fi diff --git a/build/pkgs/primecount/spkg-check.in b/build/pkgs/primecount/spkg-check.in new file mode 100644 index 00000000000..76a77d47bbf --- /dev/null +++ b/build/pkgs/primecount/spkg-check.in @@ -0,0 +1,8 @@ +############################################################################### +# +# primecount Sage check script +# +############################################################################### + +cd src +$MAKE test diff --git a/build/pkgs/primecount/spkg-install b/build/pkgs/primecount/spkg-install.in similarity index 100% rename from build/pkgs/primecount/spkg-install rename to build/pkgs/primecount/spkg-install.in diff --git a/build/pkgs/prometheus_client/SPKG.rst b/build/pkgs/prometheus_client/SPKG.rst new file mode 100644 index 00000000000..802395ae58b --- /dev/null +++ b/build/pkgs/prometheus_client/SPKG.rst @@ -0,0 +1,9 @@ +prometheus_client +================= + +Description +----------- + +The official Python 2 and 3 client for Prometheus (see +https://prometheus.io), an open-source systems monitoring and alerting +toolkit. diff --git a/build/pkgs/prometheus_client/SPKG.txt b/build/pkgs/prometheus_client/SPKG.txt deleted file mode 100644 index 607e4e290cf..00000000000 --- a/build/pkgs/prometheus_client/SPKG.txt +++ /dev/null @@ -1,6 +0,0 @@ -= prometheus_client = - -== Description == - -The official Python 2 and 3 client for Prometheus (see https://prometheus.io), -an open-source systems monitoring and alerting toolkit. diff --git a/build/pkgs/prometheus_client/dependencies b/build/pkgs/prometheus_client/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/prometheus_client/dependencies +++ b/build/pkgs/prometheus_client/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/nbformat/spkg-install b/build/pkgs/prometheus_client/spkg-install.in similarity index 100% rename from build/pkgs/nbformat/spkg-install rename to build/pkgs/prometheus_client/spkg-install.in diff --git a/build/pkgs/prompt_toolkit/SPKG.rst b/build/pkgs/prompt_toolkit/SPKG.rst new file mode 100644 index 00000000000..38e45cd036f --- /dev/null +++ b/build/pkgs/prompt_toolkit/SPKG.rst @@ -0,0 +1,9 @@ +prompt_toolkit +============== + +Description +----------- + +Library for building powerful interactive command lines in Python + +https://pypi.python.org/pypi/prompt_toolkit diff --git a/build/pkgs/prompt_toolkit/SPKG.txt b/build/pkgs/prompt_toolkit/SPKG.txt deleted file mode 100644 index 3e530f4952a..00000000000 --- a/build/pkgs/prompt_toolkit/SPKG.txt +++ /dev/null @@ -1,7 +0,0 @@ -= prompt_toolkit = - -== Description == - -Library for building powerful interactive command lines in Python - -https://pypi.python.org/pypi/prompt_toolkit diff --git a/build/pkgs/prompt_toolkit/checksums.ini b/build/pkgs/prompt_toolkit/checksums.ini index d5061696b05..13877b2a434 100644 --- a/build/pkgs/prompt_toolkit/checksums.ini +++ b/build/pkgs/prompt_toolkit/checksums.ini @@ -1,4 +1,5 @@ tarball=prompt_toolkit-VERSION.tar.gz -sha1=2c24a25b09d4a0481bf8080cd9701d2032497915 -md5=8fe70295006dbc8afedd43e5eba99032 -cksum=709719713 +sha1=f53084baf7348bfb8d41515404957e5e44bf9857 +md5=96ba0be8d3145eb70e3da25654987670 +cksum=438046741 +upstream_url=https://pypi.io/packages/source/p/prompt_toolkit/prompt_toolkit-VERSION.tar.gz diff --git a/build/pkgs/prompt_toolkit/dependencies b/build/pkgs/prompt_toolkit/dependencies index 927f34fcf60..35259ef6b78 100644 --- a/build/pkgs/prompt_toolkit/dependencies +++ b/build/pkgs/prompt_toolkit/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) six wcwidth | pip +$(PYTHON) six wcwidth | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/prompt_toolkit/package-version.txt b/build/pkgs/prompt_toolkit/package-version.txt index a9707166ba7..eca690e737b 100644 --- a/build/pkgs/prompt_toolkit/package-version.txt +++ b/build/pkgs/prompt_toolkit/package-version.txt @@ -1 +1 @@ -1.0.15 +3.0.5 diff --git a/build/pkgs/packaging/spkg-install b/build/pkgs/prompt_toolkit/spkg-install.in similarity index 100% rename from build/pkgs/packaging/spkg-install rename to build/pkgs/prompt_toolkit/spkg-install.in diff --git a/build/pkgs/psutil/SPKG.rst b/build/pkgs/psutil/SPKG.rst new file mode 100644 index 00000000000..5895ee2cdf7 --- /dev/null +++ b/build/pkgs/psutil/SPKG.rst @@ -0,0 +1,20 @@ +psutil +====== + +Description +----------- + +psutil is a cross-platform library for retrieving information onrunning +processes and system utilization (CPU, memory, disks, network) in +Python. + +License +------- + +3-clause BSD license + + +Upstream Contact +---------------- + +https://github.com/giampaolo/psutil/ diff --git a/build/pkgs/psutil/SPKG.txt b/build/pkgs/psutil/SPKG.txt deleted file mode 100644 index 7bcad7020b6..00000000000 --- a/build/pkgs/psutil/SPKG.txt +++ /dev/null @@ -1,14 +0,0 @@ -= psutil = - -== Description == - -psutil is a cross-platform library for retrieving information onrunning -processes and system utilization (CPU, memory, disks, network) in Python. - -== License == - -3-clause BSD license - -== Upstream Contact == - -https://github.com/giampaolo/psutil/ diff --git a/build/pkgs/psutil/dependencies b/build/pkgs/psutil/dependencies index 84d37f31977..da2b0925acd 100644 --- a/build/pkgs/psutil/dependencies +++ b/build/pkgs/psutil/dependencies @@ -1 +1 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) diff --git a/build/pkgs/psutil/spkg-install b/build/pkgs/psutil/spkg-install.in similarity index 100% rename from build/pkgs/psutil/spkg-install rename to build/pkgs/psutil/spkg-install.in diff --git a/build/pkgs/ptyprocess/SPKG.rst b/build/pkgs/ptyprocess/SPKG.rst new file mode 100644 index 00000000000..260a913cd76 --- /dev/null +++ b/build/pkgs/ptyprocess/SPKG.rst @@ -0,0 +1,32 @@ +ptyprocess +========== + +Description +----------- + +Launch a subprocess in a pseudo terminal (pty), and interact with both +the process and its pty. + +Sometimes, piping stdin and stdout is not enough. There might be a +password prompt that doesn't read from stdin, output that changes when +it's going to a pipe rather than a terminal, or curses-style interfaces +that rely on a terminal. If you need to automate these things, running +the process in a pseudo terminal (pty) is the answer. + +License +------- + +Ptyprocess is under the ISC license, as code derived from Pexpect. + + http://opensource.org/licenses/ISC + + +Upstream Contact +---------------- + +https://github.com/pexpect/ptyprocess + +Dependencies +------------ + +- Python diff --git a/build/pkgs/ptyprocess/SPKG.txt b/build/pkgs/ptyprocess/SPKG.txt deleted file mode 100644 index 173ccb9dee5..00000000000 --- a/build/pkgs/ptyprocess/SPKG.txt +++ /dev/null @@ -1,25 +0,0 @@ -= ptyprocess = - -== Description == - -Launch a subprocess in a pseudo terminal (pty), and interact with both the -process and its pty. - -Sometimes, piping stdin and stdout is not enough. There might be a password -prompt that doesn't read from stdin, output that changes when it's going to a -pipe rather than a terminal, or curses-style interfaces that rely on a terminal. -If you need to automate these things, running the process in a pseudo terminal -(pty) is the answer. - -== License == - -Ptyprocess is under the ISC license, as code derived from Pexpect. - http://opensource.org/licenses/ISC - -== Upstream Contact == - -https://github.com/pexpect/ptyprocess - -== Dependencies == - - * Python diff --git a/build/pkgs/ptyprocess/dependencies b/build/pkgs/ptyprocess/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/ptyprocess/dependencies +++ b/build/pkgs/ptyprocess/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/ptyprocess/spkg-install b/build/pkgs/ptyprocess/spkg-install.in similarity index 100% rename from build/pkgs/ptyprocess/spkg-install rename to build/pkgs/ptyprocess/spkg-install.in diff --git a/build/pkgs/pybind11/checksums.ini b/build/pkgs/pybind11/checksums.ini new file mode 100644 index 00000000000..1cd7a9a5e56 --- /dev/null +++ b/build/pkgs/pybind11/checksums.ini @@ -0,0 +1,5 @@ +tarball=pybind11-VERSION.tar.gz +sha1=21cdff0a455438b9727acf2b6f125fa54434eea8 +md5=5355e1fd05c8eedef19cc9bfd3d82a77 +cksum=726764164 +upstream_url=https://pypi.io/packages/source/p/pybind11/pybind11-VERSION.tar.gz diff --git a/build/pkgs/pybind11/dependencies b/build/pkgs/pybind11/dependencies new file mode 100644 index 00000000000..da2b0925acd --- /dev/null +++ b/build/pkgs/pybind11/dependencies @@ -0,0 +1 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) diff --git a/build/pkgs/pybind11/package-version.txt b/build/pkgs/pybind11/package-version.txt new file mode 100644 index 00000000000..437459cd94c --- /dev/null +++ b/build/pkgs/pybind11/package-version.txt @@ -0,0 +1 @@ +2.5.0 diff --git a/build/pkgs/pandocfilters/spkg-install b/build/pkgs/pybind11/spkg-install.in similarity index 100% rename from build/pkgs/pandocfilters/spkg-install rename to build/pkgs/pybind11/spkg-install.in diff --git a/build/pkgs/configparser/type b/build/pkgs/pybind11/type similarity index 100% rename from build/pkgs/configparser/type rename to build/pkgs/pybind11/type diff --git a/build/pkgs/pybtex/requirements.txt b/build/pkgs/pybtex/requirements.txt new file mode 100644 index 00000000000..980ee2030fe --- /dev/null +++ b/build/pkgs/pybtex/requirements.txt @@ -0,0 +1 @@ +pybtex diff --git a/build/pkgs/pybtex/type b/build/pkgs/pybtex/type index a1b589e38a3..134d9bc32d5 100644 --- a/build/pkgs/pybtex/type +++ b/build/pkgs/pybtex/type @@ -1 +1 @@ -pip +optional diff --git a/build/pkgs/pycosat/SPKG.rst b/build/pkgs/pycosat/SPKG.rst new file mode 100644 index 00000000000..d88c7a52998 --- /dev/null +++ b/build/pkgs/pycosat/SPKG.rst @@ -0,0 +1,35 @@ +pycosat +======= + +Description +----------- + +PicoSAT is a popular SAT solver written by Armin Biere in pure C. This +package provides efficient Python bindings to picosat on the C level, +i.e. when importing pycosat, the picosat solver becomes part of the +Python process itself. For ease of deployment, the picosat source +(namely picosat.c and picosat.h) is included in this project. These +files have been extracted from the picosat source. + +License +------- + +MIT + + +Upstream Contact +---------------- + +- PicoSAT: http://fmv.jku.at/picosat/ +- pycosat: https://github.com/ContinuumIO/pycosat + +Dependencies +------------ + +None. + + +Special Update/Build Instructions +--------------------------------- + +None. diff --git a/build/pkgs/pycosat/SPKG.txt b/build/pkgs/pycosat/SPKG.txt deleted file mode 100644 index 0d99e44ba15..00000000000 --- a/build/pkgs/pycosat/SPKG.txt +++ /dev/null @@ -1,28 +0,0 @@ -= pycosat = - -== Description == - -PicoSAT is a popular SAT solver written by Armin Biere in pure C. This package -provides efficient Python bindings to picosat on the C level, i.e. when -importing pycosat, the picosat solver becomes part of the Python process itself. -For ease of deployment, the picosat source (namely picosat.c and picosat.h) is -included in this project. These files have been extracted from the picosat -source. - -== License == - -MIT - -== Upstream Contact == - -PicoSAT: http://fmv.jku.at/picosat/ -pycosat: https://github.com/ContinuumIO/pycosat - -== Dependencies == - -None. - -== Special Update/Build Instructions == - -None. - diff --git a/build/pkgs/pycosat/dependencies b/build/pkgs/pycosat/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/pycosat/dependencies +++ b/build/pkgs/pycosat/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pycosat/spkg-check b/build/pkgs/pycosat/spkg-check deleted file mode 100644 index a42c0346810..00000000000 --- a/build/pkgs/pycosat/spkg-check +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -echo "Testing pycosat..." - -cd src -python test_pycosat.py - -if [ $? -ne 0 ]; then - echo >&2 "Error running self tests." - exit 1 -fi diff --git a/build/pkgs/pycosat/spkg-check.in b/build/pkgs/pycosat/spkg-check.in new file mode 100644 index 00000000000..561dc96a7d3 --- /dev/null +++ b/build/pkgs/pycosat/spkg-check.in @@ -0,0 +1,2 @@ +cd src +python test_pycosat.py diff --git a/build/pkgs/pycosat/spkg-install b/build/pkgs/pycosat/spkg-install deleted file mode 100644 index 724f15c86a3..00000000000 --- a/build/pkgs/pycosat/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing pycosat ... exiting" - exit 1 -fi - diff --git a/build/pkgs/pycosat/spkg-install.in b/build/pkgs/pycosat/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/pycosat/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/pycparser/SPKG.txt b/build/pkgs/pycparser/SPKG.txt new file mode 100644 index 00000000000..56334914f0d --- /dev/null +++ b/build/pkgs/pycparser/SPKG.txt @@ -0,0 +1,15 @@ += pycparser = + +== Description == + +development website: https://github.com/eliben/pycparser +PyPI page: https://pypi.org/project/pycparser/ + +== License == + + * BSD + +== Upstream Contact == + + * https://github.com/eliben/pycparser + diff --git a/build/pkgs/pycparser/checksums.ini b/build/pkgs/pycparser/checksums.ini new file mode 100644 index 00000000000..885d94acf9d --- /dev/null +++ b/build/pkgs/pycparser/checksums.ini @@ -0,0 +1,5 @@ +tarball=pycparser-VERSION.tar.gz +sha1=0ae93d89b69fab48af3a407a2f8663bcea270c3d +md5=b8f88de737db8c346ee8d31c07c7a25a +cksum=3289554032 +upstream_url=https://pypi.io/packages/source/p/pycparser/pycparser-VERSION.tar.gz diff --git a/build/pkgs/pycparser/dependencies b/build/pkgs/pycparser/dependencies new file mode 100644 index 00000000000..15df0c4d6d8 --- /dev/null +++ b/build/pkgs/pycparser/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/pycparser/package-version.txt b/build/pkgs/pycparser/package-version.txt new file mode 100644 index 00000000000..a4b5a6f4a49 --- /dev/null +++ b/build/pkgs/pycparser/package-version.txt @@ -0,0 +1 @@ +2.20 diff --git a/build/pkgs/pari_jupyter/spkg-install b/build/pkgs/pycparser/spkg-install.in similarity index 100% rename from build/pkgs/pari_jupyter/spkg-install rename to build/pkgs/pycparser/spkg-install.in diff --git a/build/pkgs/enum34/type b/build/pkgs/pycparser/type similarity index 100% rename from build/pkgs/enum34/type rename to build/pkgs/pycparser/type diff --git a/build/pkgs/pycygwin/SPKG.rst b/build/pkgs/pycygwin/SPKG.rst new file mode 100644 index 00000000000..2fa961c54ed --- /dev/null +++ b/build/pkgs/pycygwin/SPKG.rst @@ -0,0 +1,14 @@ +pycygwin +======== + +Description +----------- + +Python bindings for Cygwin's C API. Provides some utilities to help with +the Cygwin port. Naturally, this package should only be installed on +Cygwin--for other platforms its installation is a no-op. + +Website +------- + +https://github.com/embray/PyCygwin diff --git a/build/pkgs/pycygwin/SPKG.txt b/build/pkgs/pycygwin/SPKG.txt deleted file mode 100644 index 7f918edfd29..00000000000 --- a/build/pkgs/pycygwin/SPKG.txt +++ /dev/null @@ -1,11 +0,0 @@ -= pycygwin = - -== Description == - -Python bindings for Cygwin's C API. Provides some utilities to help with -the Cygwin port. Naturally, this package should only be installed on -Cygwin--for other platforms its installation is a no-op. - -== Website == - -https://github.com/embray/PyCygwin diff --git a/build/pkgs/pycygwin/dependencies b/build/pkgs/pycygwin/dependencies index b078da4ff6a..d3dac75e5f2 100644 --- a/build/pkgs/pycygwin/dependencies +++ b/build/pkgs/pycygwin/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) cython | setuptools pip +$(PYTHON) cython | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pycygwin/spkg-install b/build/pkgs/pycygwin/spkg-install.in similarity index 100% rename from build/pkgs/pycygwin/spkg-install rename to build/pkgs/pycygwin/spkg-install.in diff --git a/build/pkgs/pyflakes/requirements.txt b/build/pkgs/pyflakes/requirements.txt new file mode 100644 index 00000000000..38675cb44a2 --- /dev/null +++ b/build/pkgs/pyflakes/requirements.txt @@ -0,0 +1 @@ +pyflakes diff --git a/build/pkgs/pyflakes/type b/build/pkgs/pyflakes/type index a1b589e38a3..134d9bc32d5 100644 --- a/build/pkgs/pyflakes/type +++ b/build/pkgs/pyflakes/type @@ -1 +1 @@ -pip +optional diff --git a/build/pkgs/pygments/SPKG.rst b/build/pkgs/pygments/SPKG.rst new file mode 100644 index 00000000000..9bf68e5326a --- /dev/null +++ b/build/pkgs/pygments/SPKG.rst @@ -0,0 +1,51 @@ +Pygments +======== + +Description +----------- + +Pygments is a syntax highlighting package written in Python. + +It is a generic syntax highlighter suitable for use in code hosting, +forums, wikis or other applications that need to prettify source code. +Highlights are: + +- a wide range of over 300 languages and other text formats is + supported + +- special attention is paid to details, increasing quality by a fair + amount + +- support for new languages and formats are added easily +- a number of output formats, presently HTML, LaTeX, RTF, SVG, all + image + formats that PIL supports and ANSI sequences + +- it is usable as a command-line tool and as a library + +License +------- + +Modified BSD + + +Upstream Contact +---------------- + +- Author: Georg Brandl +- Home Page: http://pygments.org + +Dependencies +------------ + +Python + + +Special Update/Build Instructions +--------------------------------- + +Patches included: + +- sage_prompt.patch: patch pygments/lexers/agile.py to treat the + "sage:" prompt like Python's ">>>" prompt. This allows a very + kludgy patch to be removed from the Sphinx package (see #10118). diff --git a/build/pkgs/pygments/SPKG.txt b/build/pkgs/pygments/SPKG.txt deleted file mode 100644 index ae7f23d4cb9..00000000000 --- a/build/pkgs/pygments/SPKG.txt +++ /dev/null @@ -1,39 +0,0 @@ -= Pygments = - -== Description == - -Pygments is a syntax highlighting package written in Python. - -It is a generic syntax highlighter suitable for use in code hosting, -forums, wikis or other applications that need to prettify source code. -Highlights are: - -* a wide range of over 300 languages and other text formats is - supported -* special attention is paid to details, increasing quality by a fair - amount -* support for new languages and formats are added easily -* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image - formats that PIL supports and ANSI sequences -* it is usable as a command-line tool and as a library - -== License == - -Modified BSD - -== Upstream Contact == - -Author: Georg Brandl -Home Page: http://pygments.org - -== Dependencies == - -Python - -== Special Update/Build Instructions == - -Patches included: - - * sage_prompt.patch: patch pygments/lexers/agile.py to treat the - "sage:" prompt like Python's ">>>" prompt. This allows a very - kludgy patch to be removed from the Sphinx package (see #10118). diff --git a/build/pkgs/pygments/dependencies b/build/pkgs/pygments/dependencies index 2573414ce8a..15df0c4d6d8 100644 --- a/build/pkgs/pygments/dependencies +++ b/build/pkgs/pygments/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pygments/spkg-install b/build/pkgs/pygments/spkg-install deleted file mode 100644 index c4e6da37c23..00000000000 --- a/build/pkgs/pygments/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -#Install new version -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing Pygments." - exit 1 -fi diff --git a/build/pkgs/pygments/spkg-install.in b/build/pkgs/pygments/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/pygments/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/pynac/SPKG.rst b/build/pkgs/pynac/SPKG.rst new file mode 100644 index 00000000000..27c3f062c84 --- /dev/null +++ b/build/pkgs/pynac/SPKG.rst @@ -0,0 +1,36 @@ +pynac +===== + +Description +----------- + +A modified version of GiNaC that replaces the dependency on CLN by +Python. + +License +------- + +GPL V2+ + + +Upstream Contact +---------------- + +- Burcin Erocal - burcin spam.erocal.org +- William Stein - wstein spam.gmail.com +- Mike Hansen - mhansen spam.gmail.com + +Dependencies +------------ + +Python + + +Special Update/Build Instructions +--------------------------------- + +If build fails trying to run autoheader, run + + autoreconf -i --force + +in the src directory. diff --git a/build/pkgs/pynac/SPKG.txt b/build/pkgs/pynac/SPKG.txt deleted file mode 100644 index fe87c09f20c..00000000000 --- a/build/pkgs/pynac/SPKG.txt +++ /dev/null @@ -1,28 +0,0 @@ -= pynac = - -== Description == - -A modified version of GiNaC that replaces the dependency on CLN by Python. - -== License == - -GPL V2+ - -== Upstream Contact == - - * Burcin Erocal - burcin spam.erocal.org - * William Stein - wstein spam.gmail.com - * Mike Hansen - mhansen spam.gmail.com - -== Dependencies == - -Python - -== Special Update/Build Instructions == - -If build fails trying to run autoheader, run - - autoreconf -i --force - -in the src directory. - diff --git a/build/pkgs/pynac/checksums.ini b/build/pkgs/pynac/checksums.ini index 927a719bb0a..1f24040868d 100644 --- a/build/pkgs/pynac/checksums.ini +++ b/build/pkgs/pynac/checksums.ini @@ -1,4 +1,5 @@ tarball=pynac-VERSION.tar.bz2 -sha1=895950af30f818d4bbd55d6695c33ec63515fd5f -md5=44f1f0a2c5feff7b3e30fa763ff7e2c0 -cksum=3238748787 +sha1=4913173e8bbb3d79bb4ee1faf631b154c9b8ef8c +md5=b38234ffedc018e7f31217dcc2879035 +cksum=881228674 +upstream_url=https://github.com/mkoeppe/pynac/releases/download/pynac-VERSION/pynac-VERSION.tar.bz2 diff --git a/build/pkgs/pynac/package-version.txt b/build/pkgs/pynac/package-version.txt index 2551882a7bb..2268e754e75 100644 --- a/build/pkgs/pynac/package-version.txt +++ b/build/pkgs/pynac/package-version.txt @@ -1 +1 @@ -0.7.26 +0.7.26.sage-2020-04-03.p0 diff --git a/build/pkgs/pynac/patches/py_ssize_t_clean.patch b/build/pkgs/pynac/patches/py_ssize_t_clean.patch new file mode 100644 index 00000000000..c781ddd3082 --- /dev/null +++ b/build/pkgs/pynac/patches/py_ssize_t_clean.patch @@ -0,0 +1,45 @@ +From 37f3233e7eead521c25f798cab1df5746b9e8708 Mon Sep 17 00:00:00 2001 +From: Antonio Rojas +Date: Sun, 26 Jul 2020 20:04:35 +0200 +Subject: [PATCH 1/2] define PY_SSIZE_T_CLEAN + +As required by python 3.8 +--- + ginac/function.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/ginac/function.cpp b/ginac/function.cpp +index c158723..689e2b8 100644 +--- a/ginac/function.cpp ++++ b/ginac/function.cpp +@@ -21,6 +21,7 @@ + */ + + #define register ++#define PY_SSIZE_T_CLEAN + #include + #include "py_funcs.h" + #include "function.h" + +From 0869189faf3899d6aeb07501e16719159f13cb2f Mon Sep 17 00:00:00 2001 +From: Antonio Rojas +Date: Sun, 26 Jul 2020 20:05:19 +0200 +Subject: [PATCH 2/2] define PY_SSIZE_T_CLEAN + +As required by Python 3.8 +--- + ginac/numeric.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/ginac/numeric.cpp b/ginac/numeric.cpp +index 276d86c..b463806 100644 +--- a/ginac/numeric.cpp ++++ b/ginac/numeric.cpp +@@ -50,6 +50,7 @@ + */ + + #define register ++#define PY_SSIZE_T_CLEAN + #include + #include + #include "flint/fmpz.h" diff --git a/build/pkgs/pynac/spkg-install b/build/pkgs/pynac/spkg-install deleted file mode 100644 index e0f2b234538..00000000000 --- a/build/pkgs/pynac/spkg-install +++ /dev/null @@ -1,45 +0,0 @@ -########################################### -## pynac -########################################### - -export CXXFLAGS="-O2 -g $CXXFLAGS" - -WORKDIR=${PWD}/src -PYNACDIR=${WORKDIR} - -build_pynac() -{ - cd ${PYNACDIR} - PKG_CONFIG_PATH=${SAGE_LOCAL}/lib/pkgconfig; export PKG_CONFIG_PATH - sdh_configure --disable-static --with-giac=no PYTHON=sage-python23 - sdh_make - cd ${WORKDIR} -} - -install_pynac() -{ - rm ${SAGE_LOCAL}/lib/*ginac* - rm ${SAGE_LOCAL}/lib/*pynac* - rm -rf ${SAGE_LOCAL}/include/ginac - rm -rf ${SAGE_LOCAL}/include/pynac - cd ${PYNACDIR} - sdh_make_install - cd ${WORKDIR} -} - -clean_pynac() -{ - true -} - - -cd src - -echo "Starting build..." -echo "Running build_pynac..." -build_pynac -echo "Done build_pynac." - -echo "Installing pynac..." -install_pynac -echo "Done installing pynac." diff --git a/build/pkgs/pynac/spkg-install.in b/build/pkgs/pynac/spkg-install.in new file mode 100644 index 00000000000..e50b0ebf0a7 --- /dev/null +++ b/build/pkgs/pynac/spkg-install.in @@ -0,0 +1,45 @@ +########################################### +## pynac +########################################### + +export CXXFLAGS="-O2 -g $(echo $CXXFLAGS | sed 's/-fvisibility-inlines-hidden//g')" + +WORKDIR=${PWD}/src +PYNACDIR=${WORKDIR} + +build_pynac() +{ + cd ${PYNACDIR} + PKG_CONFIG_PATH=${SAGE_LOCAL}/lib/pkgconfig; export PKG_CONFIG_PATH + sdh_configure --disable-static --with-giac=no PYTHON=sage-python23 + sdh_make + cd ${WORKDIR} +} + +install_pynac() +{ + rm ${SAGE_LOCAL}/lib/*ginac* + rm ${SAGE_LOCAL}/lib/*pynac* + rm -rf ${SAGE_LOCAL}/include/ginac + rm -rf ${SAGE_LOCAL}/include/pynac + cd ${PYNACDIR} + sdh_make_install + cd ${WORKDIR} +} + +clean_pynac() +{ + true +} + + +cd src + +echo "Starting build..." +echo "Running build_pynac..." +build_pynac +echo "Done build_pynac." + +echo "Installing pynac..." +install_pynac +echo "Done installing pynac." diff --git a/build/pkgs/pynormaliz/SPKG.rst b/build/pkgs/pynormaliz/SPKG.rst new file mode 100644 index 00000000000..da072c3f873 --- /dev/null +++ b/build/pkgs/pynormaliz/SPKG.rst @@ -0,0 +1,28 @@ +pynormaliz +========== + +Description +----------- + +The Python module PyNormaliz provides wrappers for normaliz. + +License +------- + +- GPL v2 or later + + +Upstream Contact +---------------- + + https://github.com/sebasguts/PyNormaliz + +Dependencies +------------ + +- pip +- normaliz + + +Special Update/Build Instructions +--------------------------------- diff --git a/build/pkgs/pynormaliz/SPKG.txt b/build/pkgs/pynormaliz/SPKG.txt deleted file mode 100644 index 3de174ff111..00000000000 --- a/build/pkgs/pynormaliz/SPKG.txt +++ /dev/null @@ -1,21 +0,0 @@ -= pynormaliz = - -== Description == - -The Python module PyNormaliz provides wrappers for normaliz. - -== License == - - * GPL v2 or later - -== Upstream Contact == - - https://github.com/sebasguts/PyNormaliz - -== Dependencies == - - * pip - * normaliz - -== Special Update/Build Instructions == - diff --git a/build/pkgs/pynormaliz/dependencies b/build/pkgs/pynormaliz/dependencies index b20d6758848..40184a0cf5d 100644 --- a/build/pkgs/pynormaliz/dependencies +++ b/build/pkgs/pynormaliz/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) normaliz | pip +$(PYTHON) normaliz | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pynormaliz/spkg-check b/build/pkgs/pynormaliz/spkg-check.in similarity index 100% rename from build/pkgs/pynormaliz/spkg-check rename to build/pkgs/pynormaliz/spkg-check.in diff --git a/build/pkgs/pynormaliz/spkg-install b/build/pkgs/pynormaliz/spkg-install deleted file mode 100644 index d51207b07ce..00000000000 --- a/build/pkgs/pynormaliz/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -#Install new version -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing PyNormaliz." - exit 1 -fi diff --git a/build/pkgs/pynormaliz/spkg-install.in b/build/pkgs/pynormaliz/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/pynormaliz/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/pyopenssl/requirements.txt b/build/pkgs/pyopenssl/requirements.txt new file mode 100755 index 00000000000..74e8b70d952 --- /dev/null +++ b/build/pkgs/pyopenssl/requirements.txt @@ -0,0 +1,2 @@ +service_identity +pyopenssl diff --git a/build/pkgs/pyopenssl/spkg-install b/build/pkgs/pyopenssl/spkg-install deleted file mode 100755 index 60561523ef4..00000000000 --- a/build/pkgs/pyopenssl/spkg-install +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -if [ -z "$SAGE_LOCAL" ]; then - echo "SAGE_LOCAL undefined ... exiting" - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -exec pip install --upgrade service_identity pyopenssl diff --git a/build/pkgs/pyopenssl/type b/build/pkgs/pyopenssl/type index 84f7e31d99b..134d9bc32d5 100644 --- a/build/pkgs/pyopenssl/type +++ b/build/pkgs/pyopenssl/type @@ -1 +1 @@ -script +optional diff --git a/build/pkgs/pyparsing/SPKG.rst b/build/pkgs/pyparsing/SPKG.rst new file mode 100644 index 00000000000..dcdcae68b90 --- /dev/null +++ b/build/pkgs/pyparsing/SPKG.rst @@ -0,0 +1,24 @@ +pyparsing +========= + +Description +----------- + +A Python Parsing Module + +License +------- + +MIT License + + +Upstream Contact +---------------- + +- Author: Paul McGuire +- Home page: http://pyparsing.wikispaces.com + +Dependencies +------------ + +Python diff --git a/build/pkgs/pyparsing/SPKG.txt b/build/pkgs/pyparsing/SPKG.txt deleted file mode 100644 index be9bcc4075a..00000000000 --- a/build/pkgs/pyparsing/SPKG.txt +++ /dev/null @@ -1,25 +0,0 @@ -= pyparsing = - -== Description == - -A Python Parsing Module - -== License == - -MIT License - -== Upstream Contact == - -Author: Paul McGuire -Home page: http://pyparsing.wikispaces.com - -== Dependencies == - -Python - -== Changelog == - -=== pyparsing-2.0.1 (John H. Palmieri, 20 December 2013) === - - * Trac #14993: initial release. - diff --git a/build/pkgs/pyparsing/dependencies b/build/pkgs/pyparsing/dependencies index 2573414ce8a..15df0c4d6d8 100644 --- a/build/pkgs/pyparsing/dependencies +++ b/build/pkgs/pyparsing/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pyparsing/spkg-install b/build/pkgs/pyparsing/spkg-install deleted file mode 100644 index ccef7261c90..00000000000 --- a/build/pkgs/pyparsing/spkg-install +++ /dev/null @@ -1,14 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing pyparsing ... exiting" - exit 1 -fi diff --git a/build/pkgs/pyparsing/spkg-install.in b/build/pkgs/pyparsing/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/pyparsing/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/pysingular/SPKG.rst b/build/pkgs/pysingular/SPKG.rst new file mode 100644 index 00000000000..7dba8c07cdd --- /dev/null +++ b/build/pkgs/pysingular/SPKG.rst @@ -0,0 +1,20 @@ +PySingular +========== + +Description +----------- + +A basic interface to call Singular from python + +This python module is meant to be used in Singulars Jupyter interface. + +License +------- + +GPL version 2 or later + + +Upstream Contact +---------------- + +- https://github.com/sebasguts/SingularPython diff --git a/build/pkgs/pysingular/SPKG.txt b/build/pkgs/pysingular/SPKG.txt deleted file mode 100644 index 1517ebb2677..00000000000 --- a/build/pkgs/pysingular/SPKG.txt +++ /dev/null @@ -1,15 +0,0 @@ -= PySingular = - -== Description == - -A basic interface to call Singular from python - -This python module is meant to be used in Singulars Jupyter interface. - -== License == - -GPL version 2 or later - -== Upstream Contact == - -* https://github.com/sebasguts/SingularPython diff --git a/build/pkgs/pysingular/dependencies b/build/pkgs/pysingular/dependencies index 142ad79d170..bd58b826630 100644 --- a/build/pkgs/pysingular/dependencies +++ b/build/pkgs/pysingular/dependencies @@ -1 +1 @@ -$(PYTHON) singular | pip +$(PYTHON) singular | $(PYTHON_TOOLCHAIN) diff --git a/build/pkgs/pathlib2/spkg-install b/build/pkgs/pysingular/spkg-install.in similarity index 100% rename from build/pkgs/pathlib2/spkg-install rename to build/pkgs/pysingular/spkg-install.in diff --git a/build/pkgs/pytest/requirements.txt b/build/pkgs/pytest/requirements.txt new file mode 100644 index 00000000000..e079f8a6038 --- /dev/null +++ b/build/pkgs/pytest/requirements.txt @@ -0,0 +1 @@ +pytest diff --git a/build/pkgs/pytest/type b/build/pkgs/pytest/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/pytest/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/python2/SPKG.txt b/build/pkgs/python2/SPKG.txt deleted file mode 100644 index b0a72bfa09c..00000000000 --- a/build/pkgs/python2/SPKG.txt +++ /dev/null @@ -1,71 +0,0 @@ -= python = - -== Description == - -Python is a dynamic object-oriented programming language that can be -used for many kinds of software development. It offers strong support -for integration with other languages and tools, comes with extensive -standard libraries, and can be learned in a few days. Many Python -programmers report substantial productivity gains and feel the -language encourages the development of higher quality, more -maintainable code. - -For more details see http://www.python.org - -== License == - -Python is licensed under the PSF LICENSE. The Python license imposes -very few restrictions on what you can do with Python. Most of the -source code is copyrighted by the Python Software Foundation (PSF). A -few files have a different copyright owner, but the same license -applies to all of them. Python's licensing is GPL compatible. - -For more details see http://www.python.org/psf/license/ - -== Upstream Contact == - -There are a large number of community resources. For more details see -http://www.python.org/community/ - -== Dependencies == - - * GNU patch - * readline - * libpng - * SQLite - -== Special Update/Build Instructions == - - * We keep a copy of the stdlib 'random' module in - src/sage/cpython/_py2_random.py. Normally it shouldn't be necessary - to update this, but when upgrading Python make sure to bring over - any security updates from the upstream 'random' module in the unlikely - chance there are any. This might mean updating any "random" test results - that depend on the implementation details of the random module. - * Spaces in SAGE_ROOT aren't yet fully supported by this package, - since we put $SAGE_LOCAL/... into CPPFLAGS and LDFLAGS, which - wouldn't work then. - -=== Patches === - - * socket.patch: Work around an SSL issue. - * permissions.patch: Changes the permission of installed libraries - to 0755 (like any other library) instead of 0555. - * sys_path_security-issue_16202.patch: ensure that the current working - directory or the script directory is prepended to sys.path only if - there is no security risk in doing so. - * ncurses-issue_9665.patch: Fixes Python issue #9665 (by patching configure - and configure.in after running autotools). - * ncurses-issue_14438.patch: Fixes Python issue #14438 (ncurses) - * disable_print_refs_debug.patch: Remove some unused debug output - that breaks doctests. - * no_strict_proto-issue_5755.patch: don't add -Wstrict-prototypes compiler - flag, which isn't valid for C++ (but Python uses the same compiler flags - for C and C++). See http://bugs.python.org/issue5755. - * hashlibfallbacks-issue_18000.patch: Fixed Python issue #18000. - * tinfo.patch: make sure tinfo is correctly linked in when needed on Cygwin. - * uuid-issue_11063.patch: patch from Python issue 11063; reduce uuid - module import side effects and fix thread related issues. - * getcallargs-issue_20108.patch: fix inspect.getcallargs() when the - function has a "func" keyword argument. Needed for @interact from - ipywidgets. diff --git a/build/pkgs/python2/checksums.ini b/build/pkgs/python2/checksums.ini deleted file mode 100644 index 186826b266a..00000000000 --- a/build/pkgs/python2/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=Python-VERSION.tar.xz -sha1=f99348a095ec4a6411c84c0d15343d11920c9724 -md5=a80ae3cc478460b922242f43a1b4094d -cksum=3147661506 diff --git a/build/pkgs/python2/dependencies b/build/pkgs/python2/dependencies deleted file mode 100644 index f6d4a1622ab..00000000000 --- a/build/pkgs/python2/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -zlib readline sqlite libpng bzip2 | xz - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/python2/package-version.txt b/build/pkgs/python2/package-version.txt deleted file mode 100644 index cf7deab283c..00000000000 --- a/build/pkgs/python2/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -2.7.15.p1 diff --git a/build/pkgs/python2/patches/2.6.5-FD_SETSIZE.patch b/build/pkgs/python2/patches/2.6.5-FD_SETSIZE.patch deleted file mode 100644 index 1059ca4ad45..00000000000 --- a/build/pkgs/python2/patches/2.6.5-FD_SETSIZE.patch +++ /dev/null @@ -1,45 +0,0 @@ -This patch has never been submitted upstream for some reason, but it simply -increases the default number of file descriptors the Python process can have on -Cygwin, which mitigates some issues; see -https://cygwin.com/ml/cygwin/2011-03/msg00651.html ---- a/Modules/selectmodule.c 2012-02-02 22:35:21.835125000 -0500 -+++ b/Modules/selectmodule.c 2012-02-02 22:41:41.210125000 -0500 -@@ -6,6 +6,21 @@ - >= 0. - */ - -+/* Windows #defines FD_SETSIZE to 64 if FD_SETSIZE isn't already defined. -+ 64 is too small (too many people have bumped into that limit). -+ Here we boost it. -+ -+ Cygwin also defines FD_SETSIZE to 64, so also increase the limit on -+ Cygwin. We must do this before sys/types.h is included, which otherwise -+ sets FD_SETSIZE to the default. -+ -+ Users who want even more than the boosted limit should #define -+ FD_SETSIZE higher before this; e.g., via compiler /D switch. -+*/ -+#if (defined(MS_WINDOWS) || defined(__CYGWIN__)) && !defined(FD_SETSIZE) -+#define FD_SETSIZE 512 -+#endif -+ - #include "Python.h" - #include - -@@ -16,16 +31,6 @@ - #undef HAVE_BROKEN_POLL - #endif - --/* Windows #defines FD_SETSIZE to 64 if FD_SETSIZE isn't already defined. -- 64 is too small (too many people have bumped into that limit). -- Here we boost it. -- Users who want even more than the boosted limit should #define -- FD_SETSIZE higher before this; e.g., via compiler /D switch. --*/ --#if defined(MS_WINDOWS) && !defined(FD_SETSIZE) --#define FD_SETSIZE 512 --#endif -- - #if defined(HAVE_POLL_H) - #include - #elif defined(HAVE_SYS_POLL_H) diff --git a/build/pkgs/python2/patches/cygwin64.patch b/build/pkgs/python2/patches/cygwin64.patch deleted file mode 100644 index 345eeb17322..00000000000 --- a/build/pkgs/python2/patches/cygwin64.patch +++ /dev/null @@ -1,32 +0,0 @@ ---- a/Modules/_ctypes/libffi/src/x86/ffi.c 2013-02-11 13:24:18.000000000 -0600 -+++ b/Modules/_ctypes/libffi/src/x86/ffi.c 2013-03-12 23:22:18.267762700 -0500 -@@ -28,7 +28,7 @@ - DEALINGS IN THE SOFTWARE. - ----------------------------------------------------------------------- */ - --#if !defined(__x86_64__) || defined(_WIN64) -+#if !defined(__x86_64__) || defined(_WIN64) || defined(__CYGWIN__) - - #ifdef _WIN64 - #include ---- a/Modules/_ctypes/libffi/src/x86/win64.S 2013-02-11 13:24:18.000000000 -0600 -+++ b/Modules/_ctypes/libffi/src/x86/win64.S 2013-03-13 04:50:09.526614600 -0500 -@@ -295,7 +295,7 @@ SYMBOL_NAME(ffi_closure_win64): - mov %rax, %rcx # context is first parameter - mov %rsp, %rdx # stack is second parameter - add $48, %rdx # point to start of arguments -- mov $SYMBOL_NAME(ffi_closure_win64_inner), %rax -+ lea SYMBOL_NAME(ffi_closure_win64_inner)(%rip), %rax - callq *%rax # call the real closure function - add $40, %rsp - movq %rax, %xmm0 # If the closure returned a float, ---- a/Modules/_ctypes/libffi/fficonfig.py.in 2013-05-12 05:32:49.000000000 +0200 -+++ b/Modules/_ctypes/libffi/fficonfig.py.in 2013-06-03 22:28:06.617994762 +0200 -@@ -23,6 +23,7 @@ - 'FRV': ['src/frv/eabi.S', 'src/frv/ffi.c'], - 'S390': ['src/s390/sysv.S', 'src/s390/ffi.c'], - 'X86_64': ['src/x86/ffi64.c', 'src/x86/unix64.S', 'src/x86/ffi.c', 'src/x86/sysv.S'], -+ 'X86_WIN64': ['src/x86/ffi.c', 'src/x86/win64.S'], - 'SH': ['src/sh/sysv.S', 'src/sh/ffi.c'], - 'SH64': ['src/sh64/sysv.S', 'src/sh64/ffi.c'], - 'PA': ['src/pa/linux.S', 'src/pa/ffi.c'], diff --git a/build/pkgs/python2/patches/descr_ref-issue_25750.patch b/build/pkgs/python2/patches/descr_ref-issue_25750.patch deleted file mode 100644 index b55cf58a2d2..00000000000 --- a/build/pkgs/python2/patches/descr_ref-issue_25750.patch +++ /dev/null @@ -1,58 +0,0 @@ -When calling tp_descr_get(self, obj, type), make sure that we own a reference to "self" - -diff -ru Python-2.7.9/Objects/typeobject.c Python-2.7.9-fixed//Objects/typeobject.c ---- Python-2.7.9/Objects/typeobject.c 2014-12-10 16:59:57.000000000 +0100 -+++ Python-2.7.9-fixed//Objects/typeobject.c 2015-11-27 20:39:58.276156800 +0100 -@@ -2542,6 +2542,7 @@ - PyTypeObject *metatype = Py_TYPE(type); - PyObject *meta_attribute, *attribute; - descrgetfunc meta_get; -+ PyObject* res; - - if (!PyString_Check(name)) { - PyErr_Format(PyExc_TypeError, -@@ -2563,6 +2564,7 @@ - meta_attribute = _PyType_Lookup(metatype, name); - - if (meta_attribute != NULL) { -+ Py_INCREF(meta_attribute); - meta_get = Py_TYPE(meta_attribute)->tp_descr_get; - - if (meta_get != NULL && PyDescr_IsData(meta_attribute)) { -@@ -2570,10 +2572,11 @@ - * writes. Assume the attribute is not overridden in - * type's tp_dict (and bases): call the descriptor now. - */ -- return meta_get(meta_attribute, (PyObject *)type, -+ res = meta_get(meta_attribute, (PyObject *)type, - (PyObject *)metatype); -+ Py_DECREF(meta_attribute); -+ return res; - } -- Py_INCREF(meta_attribute); - } - - /* No data descriptor found on metatype. Look in tp_dict of this -@@ -2581,6 +2584,7 @@ - attribute = _PyType_Lookup(type, name); - if (attribute != NULL) { - /* Implement descriptor functionality, if any */ -+ Py_INCREF(attribute); - descrgetfunc local_get = Py_TYPE(attribute)->tp_descr_get; - - Py_XDECREF(meta_attribute); -@@ -2588,11 +2592,12 @@ - if (local_get != NULL) { - /* NULL 2nd argument indicates the descriptor was - * found on the target object itself (or a base) */ -- return local_get(attribute, (PyObject *)NULL, -+ res = local_get(attribute, (PyObject *)NULL, - (PyObject *)type); -+ Py_DECREF(attribute); -+ return res; - } - -- Py_INCREF(attribute); - return attribute; - } - diff --git a/build/pkgs/python2/patches/getcallargs-issue_20108.patch b/build/pkgs/python2/patches/getcallargs-issue_20108.patch deleted file mode 100644 index 5756041c93f..00000000000 --- a/build/pkgs/python2/patches/getcallargs-issue_20108.patch +++ /dev/null @@ -1,26 +0,0 @@ -# HG changeset patch -# User Benjamin Peterson -# Date 1388687048 21600 -# Node ID b0d472e3ff42de9fd2b1a702f5874202c2d2b27f -# Parent 8083b887068667a7b62443d56de310e7fe10d3df -avoid parameter name clash (closes #20108) - -diff -ru a/Lib/inspect.py b/Lib/inspect.py ---- a/Lib/inspect.py 2015-05-23 18:09:04.000000000 +0200 -+++ b/Lib/inspect.py 2016-09-21 11:34:16.491938401 +0200 -@@ -892,12 +892,14 @@ - specs.append(formatvarkw(varkw) + formatvalue(locals[varkw])) - return '(' + string.join(specs, ', ') + ')' - --def getcallargs(func, *positional, **named): -+def getcallargs(*func_and_positional, **named): - """Get the mapping of arguments to values. - - A dict is returned, with keys the function argument names (including the - names of the * and ** arguments, if any), and values the respective bound - values from 'positional' and 'named'.""" -+ func = func_and_positional[0] -+ positional = func_and_positional[1:] - args, varargs, varkw, defaults = getargspec(func) - f_name = func.__name__ - arg2value = {} diff --git a/build/pkgs/python2/patches/hashlibfallbacks-issue_18000.patch b/build/pkgs/python2/patches/hashlibfallbacks-issue_18000.patch deleted file mode 100644 index f9a2de2fdc1..00000000000 --- a/build/pkgs/python2/patches/hashlibfallbacks-issue_18000.patch +++ /dev/null @@ -1,69 +0,0 @@ -diff -ur src/Lib/test/test_hashlib.py new/Lib/test/test_hashlib.py ---- a/Lib/test/test_hashlib.py 2013-05-12 04:32:46.000000000 +0100 -+++ b/Lib/test/test_hashlib.py 2013-05-18 12:24:04.055336404 +0100 -@@ -21,9 +21,6 @@ - from test import test_support - from test.test_support import _4G, precisionbigmemtest - --# Were we compiled --with-pydebug or with #define Py_DEBUG? --COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount') -- - - def hexstr(s): - import string -@@ -40,7 +37,7 @@ - 'sha224', 'SHA224', 'sha256', 'SHA256', - 'sha384', 'SHA384', 'sha512', 'SHA512' ) - -- _warn_on_extension_import = COMPILED_WITH_PYDEBUG -+ _warn_on_extension_import = True - - def _conditional_import_module(self, module_name): - """Import a module and return a reference to it or None on failure.""" -diff -ur src/setup.py new/setup.py ---- a/setup.py 2013-05-12 04:32:54.000000000 +0100 -+++ b/setup.py 2013-05-18 12:22:04.417057535 +0100 -@@ -29,9 +29,6 @@ - return sys.platform - host_platform = get_platform() - --# Were we compiled --with-pydebug or with #define Py_DEBUG? --COMPILED_WITH_PYDEBUG = ('--with-pydebug' in sysconfig.get_config_var("CONFIG_ARGS")) -- - # This global variable is used to hold the list of modules to be disabled. - disabled_module_list = [] - -@@ -854,21 +851,18 @@ - print ("warning: openssl 0x%08x is too old for _hashlib" % - openssl_ver) - missing.append('_hashlib') -- if COMPILED_WITH_PYDEBUG or not have_usable_openssl: -- # The _sha module implements the SHA1 hash algorithm. -- exts.append( Extension('_sha', ['shamodule.c']) ) -- # The _md5 module implements the RSA Data Security, Inc. MD5 -- # Message-Digest Algorithm, described in RFC 1321. The -- # necessary files md5.c and md5.h are included here. -- exts.append( Extension('_md5', -- sources = ['md5module.c', 'md5.c'], -- depends = ['md5.h']) ) -- -- min_sha2_openssl_ver = 0x00908000 -- if COMPILED_WITH_PYDEBUG or openssl_ver < min_sha2_openssl_ver: -- # OpenSSL doesn't do these until 0.9.8 so we'll bring our own hash -- exts.append( Extension('_sha256', ['sha256module.c']) ) -- exts.append( Extension('_sha512', ['sha512module.c']) ) -+ -+ # We always compile these even when OpenSSL is available (issue #14693). -+ # It's harmless and the object code is tiny (40-50 KB per module, -+ # only loaded when actually used). -+ exts.append( Extension('_sha256', ['sha256module.c'], -+ depends=['hashlib.h']) ) -+ exts.append( Extension('_sha512', ['sha512module.c'], -+ depends=['hashlib.h']) ) -+ exts.append( Extension('_md5', ['md5module.c', 'md5.c'], -+ depends=['hashlib.h']) ) -+ exts.append( Extension('_sha', ['shamodule.c'], -+ depends=['hashlib.h']) ) - - # Modules that provide persistent dictionary-like semantics. You will - # probably want to arrange for at least one of them to be available on diff --git a/build/pkgs/python2/patches/linux_linking_issue_25229.patch b/build/pkgs/python2/patches/linux_linking_issue_25229.patch deleted file mode 100644 index 4d9fb35cc03..00000000000 --- a/build/pkgs/python2/patches/linux_linking_issue_25229.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/Lib/distutils/unixccompiler.py b/Lib/distutils/unixccompiler.py -index 3af540e..625f155 100644 ---- a/Lib/distutils/unixccompiler.py -+++ b/Lib/distutils/unixccompiler.py -@@ -238,7 +238,7 @@ class UnixCCompiler(CCompiler): - return ["+s", "-L" + dir] - elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": - return ["-rpath", dir] -- elif self._is_gcc(compiler): -+ elif sys.platform[:5] == "linux" or self._is_gcc(compiler): - return "-Wl,-R" + dir - else: - return "-R" + dir diff --git a/build/pkgs/python2/patches/macos_no_include.patch b/build/pkgs/python2/patches/macos_no_include.patch deleted file mode 100644 index 0003924e13c..00000000000 --- a/build/pkgs/python2/patches/macos_no_include.patch +++ /dev/null @@ -1,72 +0,0 @@ -diff --git a/configure b/configure -index 4a047e6..e3c8bdf 100755 ---- a/configure -+++ b/configure -@@ -6102,6 +6102,12 @@ $as_echo "$ac_cv_no_strict_aliasing_ok" >&6; } - Darwin*) - # -Wno-long-double, -no-cpp-precomp, and -mno-fused-madd - # used to be here, but non-Apple gcc doesn't accept them. -+ -+cat >>confdefs.h <<_ACEOF -+#define Py_MACOS_SYSROOT `xcrun --show-sdk-path` -+_ACEOF -+ -+ - if test "${CC}" = gcc - then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking which compiler should be used" >&5 -diff --git a/configure.ac b/configure.ac -index 913d546..6cb1f28 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -1155,6 +1155,7 @@ yes) - Darwin*) - # -Wno-long-double, -no-cpp-precomp, and -mno-fused-madd - # used to be here, but non-Apple gcc doesn't accept them. -+ AC_DEFINE_UNQUOTED([Py_MACOS_SYSROOT], [`xcrun --show-sdk-path`], [Get the root dir of XCode]) - if test "${CC}" = gcc - then - AC_MSG_CHECKING(which compiler should be used) -diff --git a/pyconfig.h.in b/pyconfig.h.in -index 11c4a66..a4e12a2 100644 ---- a/pyconfig.h.in -+++ b/pyconfig.h.in -@@ -1016,6 +1016,9 @@ - /* Defined if Python is built as a shared library. */ - #undef Py_ENABLE_SHARED - -+/* Get the root dir of XCode */ -+#undef Py_MACOS_SYSROOT -+ - /* Define as the size of the unicode type. */ - #undef Py_UNICODE_SIZE - -diff --git a/setup.py b/setup.py -index 33cecc6..5d6a411 100644 ---- a/setup.py -+++ b/setup.py -@@ -50,7 +50,7 @@ def macosx_sdk_root(): - cflags = sysconfig.get_config_var('CFLAGS') - m = re.search(r'-isysroot\s+(\S+)', cflags) - if m is None: -- sysroot = '/' -+ sysroot = sysconfig.get_config_var('Py_MACOS_SYSROOT') - else: - sysroot = m.group(1) - return sysroot -@@ -92,7 +92,6 @@ def find_file(filename, std_dirs, paths): - # Check the additional directories - for dir in paths: - f = os.path.join(dir, filename) -- - if host_platform == 'darwin' and is_macosx_sdk_path(dir): - f = os.path.join(sysroot, dir[1:], filename) - -@@ -557,6 +556,7 @@ class PyBuildExt(build_ext): - # NOTE: using shlex.split would technically be more correct, but - # also gives a bootstrap problem. Let's hope nobody uses directories - # with whitespace in the name to store libraries. -+ inc_dirs += ['/usr/include'] - cflags, ldflags = sysconfig.get_config_vars( - 'CFLAGS', 'LDFLAGS') - for item in cflags.split(): diff --git a/build/pkgs/python2/patches/ncurses-issue_14438.patch b/build/pkgs/python2/patches/ncurses-issue_14438.patch deleted file mode 100644 index e2500d93401..00000000000 --- a/build/pkgs/python2/patches/ncurses-issue_14438.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff -ru a/Include/py_curses.h b/Include/py_curses.h ---- a/Include/py_curses.h 2018-04-15 00:06:30.000000000 +0200 -+++ b/Include/py_curses.h 2018-04-19 14:50:07.967630196 +0200 -@@ -12,6 +12,13 @@ - #endif - #endif /* __APPLE__ */ - -+#ifdef __CYGWIN__ -+/* the following define is necessary for Cygwin; without it, the -+ Cygwin-supplied ncurses.h sets NCURSES_OPAQUE to 1, and then Python -+ can't get at the WINDOW flags field. */ -+#define NCURSES_INTERNALS -+#endif /* __CYGWIN__ */ -+ - #ifdef __FreeBSD__ - /* - ** On FreeBSD, [n]curses.h and stdlib.h/wchar.h use different guards diff --git a/build/pkgs/python2/patches/ncurses-issue_9665.patch b/build/pkgs/python2/patches/ncurses-issue_9665.patch deleted file mode 100644 index e0d22792a3a..00000000000 --- a/build/pkgs/python2/patches/ncurses-issue_9665.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff -ur src.orig/configure.ac src/configure.ac ---- src.orig/configure.ac 2012-09-30 19:16:16.208133911 +0200 -+++ src/configure.ac 2012-09-30 19:15:12.800599068 +0200 -@@ -1245,6 +1245,9 @@ - OSF*) - BASECFLAGS="$BASECFLAGS -mieee" - ;; -+ CYGWIN*) -+ BASECFLAGS="-I/usr/include/ncurses $BASECFLAGS" -+ ;; - esac - ;; - -diff -ur src.orig/configure src/configure ---- src.orig/configure 2012-09-30 19:16:16.216133852 +0200 -+++ src/configure 2012-09-30 19:15:44.876363826 +0200 -@@ -6146,6 +6146,9 @@ - OSF*) - BASECFLAGS="$BASECFLAGS -mieee" - ;; -+ CYGWIN*) -+ BASECFLAGS="-I/usr/include/ncurses $BASECFLAGS" -+ ;; - esac - ;; - diff --git a/build/pkgs/python2/patches/no_strict_proto-issue_5755.patch b/build/pkgs/python2/patches/no_strict_proto-issue_5755.patch deleted file mode 100644 index 2f83403b36a..00000000000 --- a/build/pkgs/python2/patches/no_strict_proto-issue_5755.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff -ru src/configure.ac b/configure.ac ---- src/configure.ac 2013-04-06 16:02:41.000000000 +0200 -+++ b/configure.ac 2013-04-11 18:11:17.947929754 +0200 -@@ -1051,9 +1051,6 @@ - then - case $GCC in - yes) -- if test "$CC" != 'g++' ; then -- STRICT_PROTO="-Wstrict-prototypes" -- fi - # For gcc 4.x we need to use -fwrapv so lets check if its supported - if "$CC" -v --help 2>/dev/null |grep -- -fwrapv > /dev/null; then - WRAP="-fwrapv" -diff -ru src/configure b/configure ---- src/configure 2013-04-06 16:02:41.000000000 +0200 -+++ b/configure 2013-04-11 18:11:25.737930322 +0200 -@@ -5931,9 +5931,6 @@ - then - case $GCC in - yes) -- if test "$CC" != 'g++' ; then -- STRICT_PROTO="-Wstrict-prototypes" -- fi - # For gcc 4.x we need to use -fwrapv so lets check if its supported - if "$CC" -v --help 2>/dev/null |grep -- -fwrapv > /dev/null; then - WRAP="-fwrapv" diff --git a/build/pkgs/python2/patches/permissions.patch b/build/pkgs/python2/patches/permissions.patch deleted file mode 100644 index d982758d9e1..00000000000 --- a/build/pkgs/python2/patches/permissions.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- src.orig/Makefile.pre.in 2013-04-06 15:02:34.000000000 +0100 -+++ src/Makefile.pre.in 2013-04-07 12:59:46.675111329 +0100 -@@ -59,7 +59,7 @@ - # Shared libraries must be installed with executable mode on some systems; - # rather than figuring out exactly which, we always give them executable mode. - # Also, making them read-only seems to be a good idea... --INSTALL_SHARED= ${INSTALL} -m 555 -+INSTALL_SHARED= ${INSTALL} -m 755 - - MKDIR_P= @MKDIR_P@ - diff --git a/build/pkgs/python2/patches/re_match_index-issue_27177.patch b/build/pkgs/python2/patches/re_match_index-issue_27177.patch deleted file mode 100644 index 0a6997642c7..00000000000 --- a/build/pkgs/python2/patches/re_match_index-issue_27177.patch +++ /dev/null @@ -1,37 +0,0 @@ -Fix http://bugs.python.org/issue27177 -diff -ru Python-2.7.10/Lib/test/test_index.py Python-2.7.10-fix-re//Lib/test/test_index.py ---- Python-2.7.10/Lib/test/test_index.py 2015-05-23 18:09:11.000000000 +0200 -+++ Python-2.7.10-fix-re//Lib/test/test_index.py 2016-06-01 15:50:07.274162354 +0200 -@@ -246,6 +246,20 @@ - self.assertEqual(xrange(1, 20)[n], 6) - self.assertEqual(xrange(1, 20).__getitem__(n), 6) - -+class MatchGroupTestCase(unittest.TestCase): -+ -+ def test_re_group(self): -+ n = newstyle() -+ n.ind = 0 -+ o = oldstyle() -+ o.ind = 1 -+ -+ import re -+ p = re.compile('(a)(b)') -+ m = p.match('ab') -+ self.assertEqual(m.group(0), m.group(n)) -+ self.assertEqual(m.group(1), m.group(o)) -+ - class OverflowTestCase(unittest.TestCase): - - def setUp(self): -diff -ru Python-2.7.10/Modules/_sre.c Python-2.7.10-fix-re//Modules/_sre.c ---- Python-2.7.10/Modules/_sre.c 2015-05-23 18:09:19.000000000 +0200 -+++ Python-2.7.10-fix-re//Modules/_sre.c 2016-06-01 15:50:58.614165047 +0200 -@@ -3303,6 +3303,8 @@ - - if (PyInt_Check(index) || PyLong_Check(index)) - return PyInt_AsSsize_t(index); -+ if (PyIndex_Check(index)) -+ return PyNumber_AsSsize_t(index, PyExc_IndexError); - - i = -1; - diff --git a/build/pkgs/python2/patches/socket.patch b/build/pkgs/python2/patches/socket.patch deleted file mode 100644 index 27e4dcb5c33..00000000000 --- a/build/pkgs/python2/patches/socket.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff -ru a/Lib/socket.py b/Lib/socket.py ---- a/Lib/socket.py 2018-04-15 00:06:30.000000000 +0200 -+++ b/Lib/socket.py 2018-04-19 14:49:27.227076923 +0200 -@@ -51,6 +51,9 @@ - - try: - import _ssl -+ from _ssl import SSLError as sslerror -+ # we try this second line since sometimes the first -+ # passes even though the module isn't there - except ImportError: - # no SSL support - pass -@@ -64,7 +67,6 @@ - return _realssl.sslwrap_simple(sock, keyfile, certfile) - - # we need to import the same constants we used to... -- from _ssl import SSLError as sslerror - from _ssl import \ - RAND_add, \ - RAND_status, \ diff --git a/build/pkgs/python2/patches/sys_path_security-issue_16202.patch b/build/pkgs/python2/patches/sys_path_security-issue_16202.patch deleted file mode 100644 index c032e5507b7..00000000000 --- a/build/pkgs/python2/patches/sys_path_security-issue_16202.patch +++ /dev/null @@ -1,314 +0,0 @@ -This patch is needed to fix http://trac.sagemath.org/ticket/13579 -It has been reported upstream as a security issue but fear of breaking -third-party software prevents its inclusion ; see: -http://bugs.python.org/issue16202 -and -https://github.com/ipython/ipython/issues/7044 - -diff -ru src/Python/sysmodule.c b/Python/sysmodule.c ---- src/Python/sysmodule.c 2012-04-10 01:07:35.000000000 +0200 -+++ b/Python/sysmodule.c 2012-10-29 22:18:46.337514322 +0100 -@@ -46,6 +46,10 @@ - #include - #endif - -+#ifdef HAVE_SYS_TYPES_H -+#include -+#endif -+ - PyObject * - PySys_GetObject(char *name) - { -@@ -1615,93 +1619,86 @@ - return av; - } - --void --PySys_SetArgvEx(int argc, char **argv, int updatepath) -+/* Prepend the parent directory of filename "arg" to the Python list -+ * "path". Return 0 normally, return -1 in case of error. */ -+static int -+PySys_UpdatePath(PyObject *path, char *arg) - { - #if defined(HAVE_REALPATH) - char fullpath[MAXPATHLEN]; - #elif defined(MS_WINDOWS) && !defined(MS_WINCE) - char fullpath[MAX_PATH]; - #endif -- PyObject *av = makeargvobject(argc, argv); -- PyObject *path = PySys_GetObject("path"); -- if (av == NULL) -- Py_FatalError("no mem for sys.argv"); -- if (PySys_SetObject("argv", av) != 0) -- Py_FatalError("can't assign sys.argv"); -- if (updatepath && path != NULL) { -- char *argv0 = argv[0]; -+ -+ /* Store original "arg" */ -+ char *given_arg = arg; -+ -+ Py_ssize_t n = 0; /* Length of arg */ -+ if (arg[0] != '\0') -+ { - char *p = NULL; -- Py_ssize_t n = 0; -- PyObject *a; - #ifdef HAVE_READLINK - char link[MAXPATHLEN+1]; -- char argv0copy[2*MAXPATHLEN+1]; -- int nr = 0; -- if (argc > 0 && argv0 != NULL && strcmp(argv0, "-c") != 0) -- nr = readlink(argv0, link, MAXPATHLEN); -+ char argcopy[2*MAXPATHLEN+1]; -+ int nr = readlink(arg, link, MAXPATHLEN); - if (nr > 0) { - /* It's a symlink */ - link[nr] = '\0'; - if (link[0] == SEP) -- argv0 = link; /* Link to absolute path */ -+ arg = link; /* Link to absolute path */ - else if (strchr(link, SEP) == NULL) - ; /* Link without path */ - else { -- /* Must join(dirname(argv0), link) */ -- char *q = strrchr(argv0, SEP); -+ /* Must join(dirname(arg), link) */ -+ char *q = strrchr(arg, SEP); - if (q == NULL) -- argv0 = link; /* argv0 without path */ -+ arg = link; /* arg without path */ - else { - /* Must make a copy */ -- strcpy(argv0copy, argv0); -- q = strrchr(argv0copy, SEP); -+ strcpy(argcopy, arg); -+ q = strrchr(argcopy, SEP); - strcpy(q+1, link); -- argv0 = argv0copy; -+ arg = argcopy; - } - } - } - #endif /* HAVE_READLINK */ - #if SEP == '\\' /* Special case for MS filename syntax */ -- if (argc > 0 && argv0 != NULL && strcmp(argv0, "-c") != 0) { -- char *q; -+ char *q; - #if defined(MS_WINDOWS) && !defined(MS_WINCE) -- /* This code here replaces the first element in argv with the full -- path that it represents. Under CE, there are no relative paths so -- the argument must be the full path anyway. */ -- char *ptemp; -- if (GetFullPathName(argv0, -- sizeof(fullpath), -- fullpath, -- &ptemp)) { -- argv0 = fullpath; -- } -+ /* This code here replaces the first element in argv with the full -+ path that it represents. Under CE, there are no relative paths so -+ the argument must be the full path anyway. */ -+ char *ptemp; -+ if (GetFullPathName(arg, -+ sizeof(fullpath), -+ fullpath, -+ &ptemp)) { -+ arg = fullpath; -+ } - #endif -- p = strrchr(argv0, SEP); -- /* Test for alternate separator */ -- q = strrchr(p ? p : argv0, '/'); -- if (q != NULL) -- p = q; -- if (p != NULL) { -- n = p + 1 - argv0; -- if (n > 1 && p[-1] != ':') -- n--; /* Drop trailing separator */ -- } -+ p = strrchr(arg, SEP); -+ /* Test for alternate separator */ -+ q = strrchr(p ? p : arg, '/'); -+ if (q != NULL) -+ p = q; -+ if (p != NULL) { -+ n = p + 1 - arg; -+ if (n > 1 && p[-1] != ':') -+ n--; /* Drop trailing separator */ - } - #else /* All other filename syntaxes */ -- if (argc > 0 && argv0 != NULL && strcmp(argv0, "-c") != 0) { - #if defined(HAVE_REALPATH) -- if (realpath(argv0, fullpath)) { -- argv0 = fullpath; -- } --#endif -- p = strrchr(argv0, SEP); -+ if (realpath(arg, fullpath)) { -+ arg = fullpath; - } -+#endif -+ p = strrchr(arg, SEP); - if (p != NULL) { - #ifndef RISCOS -- n = p + 1 - argv0; -+ n = p + 1 - arg; - #else /* don't include trailing separator */ -- n = p - argv0; -+ n = p - arg; - #endif /* RISCOS */ - #if SEP == '/' /* Special case for Unix filename syntax */ - if (n > 1) -@@ -1709,12 +1706,146 @@ - #endif /* Unix */ - } - #endif /* All others */ -- a = PyString_FromStringAndSize(argv0, n); -- if (a == NULL) -- Py_FatalError("no mem for sys.path insertion"); -- if (PyList_Insert(path, 0, a) < 0) -- Py_FatalError("sys.path.insert(0) failed"); -- Py_DECREF(a); -+ } -+ -+ /* Copy n bytes of arg to parent (the parent directory -+ * to be added to sys.path) */ -+ char parent[MAXPATHLEN+1]; -+ memcpy(parent, arg, n); -+ parent[n] = '\0'; -+ -+ /* Do some security checks before adding "parent" to sys.path */ -+#ifdef HAVE_STAT -+ struct stat parent_stat; -+ struct stat arg_stat; -+ struct stat program_stat; /* Python program */ -+ char warnmsg[MAXPATHLEN + 400]; -+ const char *lecture = "Untrusted users could put files in this " -+ "directory which might then be imported by your Python code. " -+ "As a general precaution from similar exploits, " -+ "you should not execute Python code from this directory"; -+ if (stat( (parent[0] != '\0') ? parent : ".", &parent_stat) != 0) { -+ snprintf(warnmsg, sizeof(warnmsg), "not adding '%s' to sys.path since its status cannot be determined", parent); -+ return PyErr_WarnEx(PyExc_RuntimeWarning, warnmsg, 1); -+ } -+ if (!S_ISDIR(parent_stat.st_mode)) { -+ snprintf(warnmsg, sizeof(warnmsg), "not adding '%s' to sys.path since it's not a directory", parent); -+ return PyErr_WarnEx(PyExc_RuntimeWarning, warnmsg, 1); -+ } -+ -+ if (given_arg[0] != '\0' && stat(given_arg, &arg_stat) == 0) { -+ /* Only keep group bits if the group is the same as the -+ * group of "parent" (otherwise the group is considered unsafe). */ -+ if (arg_stat.st_gid != parent_stat.st_gid) -+ arg_stat.st_mode &= 0707; -+ /* If parent does *not* have the sticky bit set, "arg" is at -+ * least as writable as "parent". This obviously only applies -+ * if "arg" is an existing file/directory inside "parent", which -+ * is the case here. */ -+ if (!(parent_stat.st_mode & S_ISVTX)) -+ arg_stat.st_mode |= parent_stat.st_mode; -+ } else { -+ /* given_arg was "" or stat() failed, manually set relevant -+ * stat members to sensible values. Set the mode to whatever -+ * it would be if we would create a new file, keeping in mind -+ * the current umask. */ -+ unsigned int mask = umask(0777); umask(mask); -+ arg_stat.st_mode = 0666 & ~mask; -+ arg_stat.st_uid = 0; -+ /* Only keep group bit if the current group ID is the same as -+ * the group of "parent" */ -+ if (getgid() != parent_stat.st_gid) -+ arg_stat.st_mode &= 0707; -+ } -+ -+ if (stat(Py_GetProgramFullPath(), &program_stat) == 0) { -+ /* Only keep group bits if the group is the same as the -+ * group of "parent" (otherwise the group is considered unsafe). */ -+ if (program_stat.st_gid != parent_stat.st_gid) -+ program_stat.st_mode &= 0707; -+ } else { -+ /* stat() failed, set relevant stat members to safe values. */ -+ program_stat.st_mode = 0644; -+ program_stat.st_uid = 0; -+ } -+ -+ /* Check permissions, check that the "parent" directory is not -+ * more permissive than the script "arg" or the Python program -+ * "program". Otherwise adding "parent" to sys.path is a security -+ * risk. */ -+ if (parent_stat.st_mode & 0002) { -+ /* (A) "parent" is world-writable */ -+ if ((arg_stat.st_mode & 0002) == 0 && (program_stat.st_mode & 0002) == 0) { -+ snprintf(warnmsg, sizeof(warnmsg), -+ "not adding directory '%s' to sys.path since everybody can write to it.\n%s", -+ parent, lecture); -+ return PyErr_WarnEx(PyExc_RuntimeWarning, warnmsg, 1); -+ } -+ } else if (parent_stat.st_mode & 0020) { -+ /* (B) "parent" is group-writable. Recall that the group -+ * permissions of "arg" and "program" refer to the group owning -+ * "parent". */ -+ if ((arg_stat.st_mode & 0022) == 0 && (program_stat.st_mode & 0022) == 0) { -+ snprintf(warnmsg, sizeof(warnmsg), -+ "not adding directory '%s' to sys.path since it's writable by an untrusted group.\n%s", -+ parent, lecture); -+ return PyErr_WarnEx(PyExc_RuntimeWarning, warnmsg, 1); -+ } -+ } else { -+ /* (C) parent is neither group-, neither world-writable. -+ * We are safe if "arg" or "program" is group- or -+ * world-writable or if "parent" is owned by a trusted user: -+ * either the same owner as "arg" or "program", -+ * or root, or the current user. */ -+ if ( -+ (arg_stat.st_mode & 0022) == 0 && -+ (program_stat.st_mode & 0022) == 0 && -+ parent_stat.st_uid != arg_stat.st_uid && -+ parent_stat.st_uid != program_stat.st_uid && -+ parent_stat.st_uid != 0 && -+ parent_stat.st_uid != getuid()) { -+ snprintf(warnmsg, sizeof(warnmsg), -+ "not adding directory '%s' to sys.path since it's not owned by a trusted user.\n%s", -+ parent, lecture); -+ return PyErr_WarnEx(PyExc_RuntimeWarning, warnmsg, 1); -+ } -+ } -+#endif /* HAVE_STAT */ -+ -+ PyObject *a = PyString_FromString(parent); -+ if (a == NULL) -+ Py_FatalError("no mem for sys.path insertion"); -+ if (PyList_Insert(path, 0, a) < 0) -+ return -1; -+ Py_DECREF(a); -+ -+ return 0; -+} -+ -+void -+PySys_SetArgvEx(int argc, char **argv, int updatepath) -+{ -+ PyObject *av = makeargvobject(argc, argv); -+ PyObject *path = PySys_GetObject("path"); -+ if (av == NULL) -+ Py_FatalError("no mem for sys.argv"); -+ if (PySys_SetObject("argv", av) != 0) -+ Py_FatalError("can't assign sys.argv"); -+ -+ if (updatepath && path != NULL) { -+ char *argv0; -+ if (argc <= 0 || argv[0] == NULL || strcmp(argv[0], "-c") == 0) { -+ /* If there is no argv[0] or argv[0] equals "-c", add "" to sys.path */ -+ argv0 = ""; -+ } -+ else { -+ argv0 = argv[0]; -+ } -+ if (PySys_UpdatePath(path, argv0)) { -+ /* No way to signal failure, so print exception and exit */ -+ PyErr_PrintEx(0); -+ exit(1); -+ } - } - Py_DECREF(av); - } diff --git a/build/pkgs/python2/patches/tinfo.patch b/build/pkgs/python2/patches/tinfo.patch deleted file mode 100644 index bc152f51622..00000000000 --- a/build/pkgs/python2/patches/tinfo.patch +++ /dev/null @@ -1,123 +0,0 @@ -diff --git a/Lib/distutils/ccompiler.py b/Lib/distutils/ccompiler.py -index 62506a6..f80230c 100644 ---- a/Lib/distutils/ccompiler.py -+++ b/Lib/distutils/ccompiler.py -@@ -842,9 +842,9 @@ main (int argc, char **argv) { - def library_filename(self, libname, lib_type='static', # or 'shared' - strip_dir=0, output_dir=''): - assert output_dir is not None -- if lib_type not in ("static", "shared", "dylib", "xcode_stub"): -+ if lib_type not in ("static", "shared", "dylib", "xcode_stub", "import"): - raise ValueError, ("""'lib_type' must be "static", "shared", """ -- """"dylib", or "xcode_stub".""") -+ """"dylib", "xcode_stub" or "import".""") - fmt = getattr(self, lib_type + "_lib_format") - ext = getattr(self, lib_type + "_lib_extension") - -diff --git a/Lib/distutils/unixccompiler.py b/Lib/distutils/unixccompiler.py -index 4c35676..54f8356 100644 ---- a/Lib/distutils/unixccompiler.py -+++ b/Lib/distutils/unixccompiler.py -@@ -80,10 +80,12 @@ class UnixCCompiler(CCompiler): - shared_lib_extension = ".so" - dylib_lib_extension = ".dylib" - xcode_stub_lib_extension = ".tbd" -- static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" -+ import_lib_extension = ".dll.a" -+ import_lib_format = static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" - xcode_stub_lib_format = dylib_lib_format - if sys.platform == "cygwin": - exe_extension = ".exe" -+ shared_lib_extension = ".dll" - - def preprocess(self, source, - output_file=None, macros=None, include_dirs=None, -@@ -249,6 +251,7 @@ class UnixCCompiler(CCompiler): - dylib_f = self.library_filename(lib, lib_type='dylib') - xcode_stub_f = self.library_filename(lib, lib_type='xcode_stub') - static_f = self.library_filename(lib, lib_type='static') -+ import_f = self.library_filename(lib, lib_type='import') - - if sys.platform == 'darwin': - # On OSX users can specify an alternate SDK using -@@ -279,6 +282,7 @@ class UnixCCompiler(CCompiler): - - - for dir in dirs: -+ implib = os.path.join(dir, import_f) - shared = os.path.join(dir, shared_f) - dylib = os.path.join(dir, dylib_f) - static = os.path.join(dir, static_f) -@@ -297,7 +301,9 @@ class UnixCCompiler(CCompiler): - # data to go on: GCC seems to prefer the shared library, so I'm - # assuming that *all* Unix C compilers do. And of course I'm - # ignoring even GCC's "-static" option. So sue me. -- if os.path.exists(dylib): -+ if os.path.exists(implib): -+ return implib -+ elif os.path.exists(dylib): - return dylib - elif os.path.exists(xcode_stub): - return xcode_stub -@@ -308,3 +314,22 @@ class UnixCCompiler(CCompiler): - - # Oops, didn't find it in *any* of 'dirs' - return None -+ -+ def implib_to_dll(self, dirs, implib, debug=0): -+ fp = os.popen("dlltool -I %s" % implib) -+ dlltool_output = fp.readlines() -+ ret = fp.close() -+ -+ if ret is None or ret >> 8 == 0: -+ for ln in dlltool_output: -+ for dir in dirs: -+ dll = os.path.join(dir, ln.rstrip('\n')) -+ # We're second-guessing the linker here, with not much hard -+ # data to go on: GCC seems to prefer the shared library, so I'm -+ # assuming that *all* Unix C compilers do. And of course I'm -+ # ignoring even GCC's "-static" option. So sue me. -+ if os.path.exists(dll): -+ return dll -+ -+ # Oops, didn't find it in *any* of 'dirs' -+ return None -diff --git a/setup.py b/setup.py -index aa08ada..f41d200 100644 ---- a/setup.py -+++ b/setup.py -@@ -726,8 +726,12 @@ class PyBuildExt(build_ext): - do_readline = self.compiler.find_library_file(lib_dirs, 'readline') - readline_termcap_library = "" - curses_library = "" -- # Determine if readline is already linked against curses or tinfo. -+ # Determine if readline is linked against curses or tinfo. - if do_readline and find_executable('ldd'): -+ # On Cygwin we have to find out which dll the implib point to -+ if host_platform == "cygwin" and find_executable('dlltool'): -+ do_readline = self.compiler.implib_to_dll(os.getenv('PATH').split(os.pathsep) + lib_dirs, do_readline) -+ - fp = os.popen("ldd %s" % do_readline) - ldd_output = fp.readlines() - ret = fp.close() -@@ -778,7 +782,10 @@ class PyBuildExt(build_ext): - - readline_libs = ['readline'] - if readline_termcap_library: -- pass # Issue 7384: Already linked against curses or tinfo. -+ if host_platform != "cygwin": -+ pass # Issue 7384: Already linked against curses or tinfo. -+ else: -+ readline_libs.append(readline_termcap_library) - elif curses_library: - readline_libs.append(curses_library) - elif self.compiler.find_library_file(lib_dirs + -@@ -1370,6 +1377,8 @@ class PyBuildExt(build_ext): - # _curses_panel.so must link with panelw. - panel_library = 'panelw' - curses_libs = [curses_library] -+ if readline_termcap_library == 'tinfo' and host_platform == "cygwin": -+ curses_libs.append(readline_termcap_library) - curses_incs = find_file('curses.h', inc_dirs, - [os.path.join(d, 'ncursesw') for d in inc_dirs]) - exts.append( Extension('_curses', ['_cursesmodule.c'], diff --git a/build/pkgs/python2/patches/trashcan_heap_type.patch b/build/pkgs/python2/patches/trashcan_heap_type.patch deleted file mode 100644 index b8f3ead3968..00000000000 --- a/build/pkgs/python2/patches/trashcan_heap_type.patch +++ /dev/null @@ -1,64 +0,0 @@ -https://github.com/python/cpython/pull/12725 - -commit 6f8de0b75e0ca987cf5e734cf9c4bb91762efcf3 -Author: Jeroen Demeyer -Date: Mon Apr 8 14:08:37 2019 +0200 - - bpo-36556: trashcan should not cause duplicated __del__ - -diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py -index 7e47b2d..3e4d0f9 100644 ---- a/Lib/test/test_gc.py -+++ b/Lib/test/test_gc.py -@@ -307,6 +307,24 @@ class GCTests(unittest.TestCase): - v = {1: v, 2: Ouch()} - gc.disable() - -+ def test_no_double_del(self): -+ # bpo-36556: instances of heap types should be deallocated once, -+ # even if the trashcan and __del__ are involved -+ class ObjectCounter(object): -+ count = 0 -+ def __init__(self): -+ type(self).count += 1 -+ def __del__(self): -+ # create temporary involving self, whose deallocation -+ # uses the trashcan -+ L = [self] -+ type(self).count -= 1 -+ L = None -+ for i in range(10000): -+ L = (L, ObjectCounter()) -+ del L -+ self.assertEqual(ObjectCounter.count, 0) -+ - @unittest.skipUnless(threading, "test meaningless on builds without threads") - def test_trashcan_threads(self): - # Issue #13992: trashcan mechanism should be thread-safe -diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-04-08-14-32-28.bpo-36556.lp-8oV.rst b/Misc/NEWS.d/next/Core and Builtins/2019-04-08-14-32-28.bpo-36556.lp-8oV.rst -new file mode 100644 -index 0000000..496bdc5 ---- /dev/null -+++ b/Misc/NEWS.d/next/Core and Builtins/2019-04-08-14-32-28.bpo-36556.lp-8oV.rst -@@ -0,0 +1,2 @@ -+When deleting highly nested objects (where the trashcan mechanism is -+involved), it is less likely that ``__del__`` is called multiple times. -diff --git a/Objects/typeobject.c b/Objects/typeobject.c -index 844fb00..43b35b1 100644 ---- a/Objects/typeobject.c -+++ b/Objects/typeobject.c -@@ -917,6 +917,14 @@ subtype_clear(PyObject *self) - return 0; - } - -+/* bpo-36556: lower the trashcan recursion limit for heap types: this gives -+ * __del__ 10 additional stack frames to work with. This makes it less likely -+ * that the trashcan is used in __del__. Otherwise, an object might seemingly -+ * be resurrected by __del__ when it's still referenced by an object in the -+ * trashcan. */ -+#undef PyTrash_UNWIND_LEVEL -+#define PyTrash_UNWIND_LEVEL 40 -+ - static void - subtype_dealloc(PyObject *self) - { diff --git a/build/pkgs/python2/patches/trashcan_subclass.patch b/build/pkgs/python2/patches/trashcan_subclass.patch deleted file mode 100644 index 401bdb5b865..00000000000 --- a/build/pkgs/python2/patches/trashcan_subclass.patch +++ /dev/null @@ -1,496 +0,0 @@ -See https://github.com/python/cpython/pull/12699 - -commit 324fc4bd0b1fa9cb9e60ab58f8852e8b7fdd8689 -Author: Jeroen Demeyer -Date: Wed Feb 13 15:12:48 2019 +0100 - - [2.7] bpo-35983: skip trashcan for subclasses (GH-11841) - -diff --git a/Include/object.h b/Include/object.h -index 807b241..cc2f3c5 100644 ---- a/Include/object.h -+++ b/Include/object.h -@@ -969,11 +969,11 @@ times. - - When deallocating a container object, it's possible to trigger an unbounded - chain of deallocations, as each Py_DECREF in turn drops the refcount on "the --next" object in the chain to 0. This can easily lead to stack faults, and -+next" object in the chain to 0. This can easily lead to stack overflows, - especially in threads (which typically have less stack space to work with). - --A container object that participates in cyclic gc can avoid this by --bracketing the body of its tp_dealloc function with a pair of macros: -+A container object can avoid this by bracketing the body of its tp_dealloc -+function with a pair of macros: - - static void - mytype_dealloc(mytype *p) -@@ -981,14 +981,14 @@ mytype_dealloc(mytype *p) - ... declarations go here ... - - PyObject_GC_UnTrack(p); // must untrack first -- Py_TRASHCAN_SAFE_BEGIN(p) -+ Py_TRASHCAN_BEGIN(p, mytype_dealloc) - ... The body of the deallocator goes here, including all calls ... - ... to Py_DECREF on contained objects. ... -- Py_TRASHCAN_SAFE_END(p) -+ Py_TRASHCAN_END // there should be no code after this - } - - CAUTION: Never return from the middle of the body! If the body needs to --"get out early", put a label immediately before the Py_TRASHCAN_SAFE_END -+"get out early", put a label immediately before the Py_TRASHCAN_END - call, and goto it. Else the call-depth counter (see below) will stay - above 0 forever, and the trashcan will never get emptied. - -@@ -1004,6 +1004,12 @@ notices this, and calls another routine to deallocate all the objects that - may have been added to the list of deferred deallocations. In effect, a - chain of N deallocations is broken into N / PyTrash_UNWIND_LEVEL pieces, - with the call stack never exceeding a depth of PyTrash_UNWIND_LEVEL. -+ -+Since the tp_dealloc of a subclass typically calls the tp_dealloc of the base -+class, we need to ensure that the trashcan is only triggered on the tp_dealloc -+of the actual class being deallocated. Otherwise we might end up with a -+partially-deallocated object. To check this, the tp_dealloc function must be -+passed as second argument to Py_TRASHCAN_BEGIN(). - */ - - /* This is the old private API, invoked by the macros before 2.7.4. -@@ -1020,26 +1026,38 @@ PyAPI_FUNC(void) _PyTrash_thread_destroy_chain(void); - #define PyTrash_UNWIND_LEVEL 50 - - /* Note the workaround for when the thread state is NULL (issue #17703) */ --#define Py_TRASHCAN_SAFE_BEGIN(op) \ -+#define Py_TRASHCAN_BEGIN_CONDITION(op, cond) \ - do { \ -- PyThreadState *_tstate = PyThreadState_GET(); \ -- if (!_tstate || \ -- _tstate->trash_delete_nesting < PyTrash_UNWIND_LEVEL) { \ -- if (_tstate) \ -- ++_tstate->trash_delete_nesting; -- /* The body of the deallocator is here. */ --#define Py_TRASHCAN_SAFE_END(op) \ -- if (_tstate) { \ -- --_tstate->trash_delete_nesting; \ -- if (_tstate->trash_delete_later \ -- && _tstate->trash_delete_nesting <= 0) \ -- _PyTrash_thread_destroy_chain(); \ -+ PyThreadState *_tstate = NULL; \ -+ /* If "cond" is false, then _tstate remains NULL and the deallocator \ -+ * is run normally without involving the trashcan */ \ -+ if (cond && (_tstate = PyThreadState_GET()) != NULL) { \ -+ if (_tstate->trash_delete_nesting >= PyTrash_UNWIND_LEVEL) { \ -+ /* Store the object (to be deallocated later) and jump past \ -+ * Py_TRASHCAN_END, skipping the body of the deallocator */ \ -+ _PyTrash_thread_deposit_object((PyObject*)op); \ -+ break; \ - } \ -+ ++_tstate->trash_delete_nesting; \ -+ } -+ /* The body of the deallocator is here. */ -+#define Py_TRASHCAN_END \ -+ if (_tstate) { \ -+ --_tstate->trash_delete_nesting; \ -+ if (_tstate->trash_delete_later && _tstate->trash_delete_nesting <= 0) \ -+ _PyTrash_thread_destroy_chain(); \ - } \ -- else \ -- _PyTrash_thread_deposit_object((PyObject*)op); \ - } while (0); - -+#define Py_TRASHCAN_BEGIN(op, dealloc) Py_TRASHCAN_BEGIN_CONDITION(op, \ -+ Py_TYPE(op)->tp_dealloc == (destructor)(dealloc)) -+ -+/* For backwards compatibility, these macros enable the trashcan -+ * unconditionally */ -+#define Py_TRASHCAN_SAFE_BEGIN(op) Py_TRASHCAN_BEGIN_CONDITION(op, 1) -+#define Py_TRASHCAN_SAFE_END(op) Py_TRASHCAN_END -+ -+ - #ifdef __cplusplus - } - #endif -diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py -index 5eb3f7d..7c63c55 100644 ---- a/Lib/test/test_capi.py -+++ b/Lib/test/test_capi.py -@@ -22,6 +22,41 @@ class CAPITest(unittest.TestCase): - def test_buildvalue_N(self): - _testcapi.test_buildvalue_N() - -+ def test_trashcan_subclass(self): -+ # bpo-35983: Check that the trashcan mechanism for "list" is NOT -+ # activated when its tp_dealloc is being called by a subclass -+ from _testcapi import MyList -+ L = None -+ for i in range(1000): -+ L = MyList((L,)) -+ -+ def test_trashcan_python_class(self): -+ # Check that the trashcan mechanism works properly for a Python -+ # subclass of a class using the trashcan (list in this test) -+ class PyList(list): -+ # Count the number of PyList instances to verify that there is -+ # no memory leak -+ num = 0 -+ def __init__(self, *args): -+ __class__.num += 1 -+ super().__init__(*args) -+ def __del__(self): -+ __class__.num -= 1 -+ -+ for parity in (0, 1): -+ L = None -+ # We need in the order of 2**20 iterations here such that a -+ # typical 8MB stack would overflow without the trashcan. -+ for i in range(2**20): -+ L = PyList((L,)) -+ L.attr = i -+ if parity: -+ # Add one additional nesting layer -+ L = (L,) -+ self.assertGreater(PyList.num, 0) -+ del L -+ self.assertEqual(PyList.num, 0) -+ - - @unittest.skipUnless(threading, 'Threading required for this test.') - class TestPendingCalls(unittest.TestCase): -diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-02-13-16-47-19.bpo-35983.bNxsXv.rst b/Misc/NEWS.d/next/Core and Builtins/2019-02-13-16-47-19.bpo-35983.bNxsXv.rst -new file mode 100644 -index 0000000..1138df6 ---- /dev/null -+++ b/Misc/NEWS.d/next/Core and Builtins/2019-02-13-16-47-19.bpo-35983.bNxsXv.rst -@@ -0,0 +1,3 @@ -+Added new trashcan macros to deal with a double deallocation that could occur -+when the `tp_dealloc` of a subclass calls the `tp_dealloc` of a base class -+and that base class uses the trashcan mechanism. Patch by Jeroen Demeyer. -diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c -index 67488e7..fb866fa 100644 ---- a/Modules/_testcapimodule.c -+++ b/Modules/_testcapimodule.c -@@ -2944,6 +2944,76 @@ static PyTypeObject test_structmembersType = { - }; - - -+/* Test bpo-35983: create a subclass of "list" which checks that instances -+ * are not deallocated twice */ -+ -+typedef struct { -+ PyListObject list; -+ int deallocated; -+} MyListObject; -+ -+static PyObject * -+MyList_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -+{ -+ PyObject* op = PyList_Type.tp_new(type, args, kwds); -+ ((MyListObject*)op)->deallocated = 0; -+ return op; -+} -+ -+void -+MyList_dealloc(MyListObject* op) -+{ -+ if (op->deallocated) { -+ /* We cannot raise exceptions here but we still want the testsuite -+ * to fail when we hit this */ -+ Py_FatalError("MyList instance deallocated twice"); -+ } -+ op->deallocated = 1; -+ PyList_Type.tp_dealloc((PyObject *)op); -+} -+ -+static PyTypeObject MyList_Type = { -+ PyVarObject_HEAD_INIT(NULL, 0) -+ "MyList", -+ sizeof(MyListObject), -+ 0, -+ (destructor)MyList_dealloc, /* tp_dealloc */ -+ 0, /* tp_print */ -+ 0, /* tp_getattr */ -+ 0, /* tp_setattr */ -+ 0, /* tp_reserved */ -+ 0, /* tp_repr */ -+ 0, /* tp_as_number */ -+ 0, /* tp_as_sequence */ -+ 0, /* tp_as_mapping */ -+ 0, /* tp_hash */ -+ 0, /* tp_call */ -+ 0, /* tp_str */ -+ 0, /* tp_getattro */ -+ 0, /* tp_setattro */ -+ 0, /* tp_as_buffer */ -+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ -+ 0, /* tp_doc */ -+ 0, /* tp_traverse */ -+ 0, /* tp_clear */ -+ 0, /* tp_richcompare */ -+ 0, /* tp_weaklistoffset */ -+ 0, /* tp_iter */ -+ 0, /* tp_iternext */ -+ 0, /* tp_methods */ -+ 0, /* tp_members */ -+ 0, /* tp_getset */ -+ 0, /* &PyList_Type */ /* tp_base */ -+ 0, /* tp_dict */ -+ 0, /* tp_descr_get */ -+ 0, /* tp_descr_set */ -+ 0, /* tp_dictoffset */ -+ 0, /* tp_init */ -+ 0, /* tp_alloc */ -+ MyList_new, /* tp_new */ -+}; -+ -+ - PyMODINIT_FUNC - init_testcapi(void) - { -@@ -2961,6 +3031,12 @@ init_testcapi(void) - test_capi to automatically call this */ - PyModule_AddObject(m, "_test_structmembersType", (PyObject *)&test_structmembersType); - -+ MyList_Type.tp_base = &PyList_Type; -+ if (PyType_Ready(&MyList_Type) < 0) -+ return NULL; -+ Py_INCREF(&MyList_Type); -+ PyModule_AddObject(m, "MyList", (PyObject *)&MyList_Type); -+ - PyModule_AddObject(m, "CHAR_MAX", PyInt_FromLong(CHAR_MAX)); - PyModule_AddObject(m, "CHAR_MIN", PyInt_FromLong(CHAR_MIN)); - PyModule_AddObject(m, "UCHAR_MAX", PyInt_FromLong(UCHAR_MAX)); -diff --git a/Objects/descrobject.c b/Objects/descrobject.c -index 8d6e6e3..38f11e9 100644 ---- a/Objects/descrobject.c -+++ b/Objects/descrobject.c -@@ -940,11 +940,11 @@ static void - wrapper_dealloc(wrapperobject *wp) - { - PyObject_GC_UnTrack(wp); -- Py_TRASHCAN_SAFE_BEGIN(wp) -+ Py_TRASHCAN_BEGIN(wp, wrapper_dealloc) - Py_XDECREF(wp->descr); - Py_XDECREF(wp->self); - PyObject_GC_Del(wp); -- Py_TRASHCAN_SAFE_END(wp) -+ Py_TRASHCAN_END - } - - static int -diff --git a/Objects/dictobject.c b/Objects/dictobject.c -index c544ecd..fa2b4b7 100644 ---- a/Objects/dictobject.c -+++ b/Objects/dictobject.c -@@ -1078,7 +1078,7 @@ dict_dealloc(register PyDictObject *mp) - Py_ssize_t fill = mp->ma_fill; - /* bpo-31095: UnTrack is needed before calling any callbacks */ - PyObject_GC_UnTrack(mp); -- Py_TRASHCAN_SAFE_BEGIN(mp) -+ Py_TRASHCAN_BEGIN(mp, dict_dealloc) - for (ep = mp->ma_table; fill > 0; ep++) { - if (ep->me_key) { - --fill; -@@ -1092,7 +1092,7 @@ dict_dealloc(register PyDictObject *mp) - free_list[numfree++] = mp; - else - Py_TYPE(mp)->tp_free((PyObject *)mp); -- Py_TRASHCAN_SAFE_END(mp) -+ Py_TRASHCAN_END - } - - static int -diff --git a/Objects/listobject.c b/Objects/listobject.c -index 24eff76..dc57270 100644 ---- a/Objects/listobject.c -+++ b/Objects/listobject.c -@@ -298,7 +298,7 @@ list_dealloc(PyListObject *op) - { - Py_ssize_t i; - PyObject_GC_UnTrack(op); -- Py_TRASHCAN_SAFE_BEGIN(op) -+ Py_TRASHCAN_BEGIN(op, list_dealloc) - if (op->ob_item != NULL) { - /* Do it backwards, for Christian Tismer. - There's a simple test case where somehow this reduces -@@ -314,7 +314,7 @@ list_dealloc(PyListObject *op) - free_list[numfree++] = op; - else - Py_TYPE(op)->tp_free((PyObject *)op); -- Py_TRASHCAN_SAFE_END(op) -+ Py_TRASHCAN_END - } - - static int -diff --git a/Objects/setobject.c b/Objects/setobject.c -index 31da3db..3dbbcf3 100644 ---- a/Objects/setobject.c -+++ b/Objects/setobject.c -@@ -551,7 +551,7 @@ set_dealloc(PySetObject *so) - Py_ssize_t fill = so->fill; - /* bpo-31095: UnTrack is needed before calling any callbacks */ - PyObject_GC_UnTrack(so); -- Py_TRASHCAN_SAFE_BEGIN(so) -+ Py_TRASHCAN_BEGIN(so, set_dealloc) - if (so->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) so); - -@@ -567,7 +567,7 @@ set_dealloc(PySetObject *so) - free_list[numfree++] = so; - else - Py_TYPE(so)->tp_free(so); -- Py_TRASHCAN_SAFE_END(so) -+ Py_TRASHCAN_END - } - - static int -diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c -index 6f4b18c..ae82163 100644 ---- a/Objects/tupleobject.c -+++ b/Objects/tupleobject.c -@@ -215,7 +215,7 @@ tupledealloc(register PyTupleObject *op) - register Py_ssize_t i; - register Py_ssize_t len = Py_SIZE(op); - PyObject_GC_UnTrack(op); -- Py_TRASHCAN_SAFE_BEGIN(op) -+ Py_TRASHCAN_BEGIN(op, tupledealloc) - if (len > 0) { - i = len; - while (--i >= 0) -@@ -234,7 +234,7 @@ tupledealloc(register PyTupleObject *op) - } - Py_TYPE(op)->tp_free((PyObject *)op); - done: -- Py_TRASHCAN_SAFE_END(op) -+ Py_TRASHCAN_END - } - - static int -diff --git a/Objects/typeobject.c b/Objects/typeobject.c -index 844fb00..c8335ba 100644 ---- a/Objects/typeobject.c -+++ b/Objects/typeobject.c -@@ -922,7 +922,6 @@ subtype_dealloc(PyObject *self) - { - PyTypeObject *type, *base; - destructor basedealloc; -- PyThreadState *tstate = PyThreadState_GET(); - - /* Extract the type; we expect it to be a heap type */ - type = Py_TYPE(self); -@@ -971,16 +970,7 @@ subtype_dealloc(PyObject *self) - /* UnTrack and re-Track around the trashcan macro, alas */ - /* See explanation at end of function for full disclosure */ - PyObject_GC_UnTrack(self); -- ++_PyTrash_delete_nesting; -- ++ tstate->trash_delete_nesting; -- Py_TRASHCAN_SAFE_BEGIN(self); -- --_PyTrash_delete_nesting; -- -- tstate->trash_delete_nesting; -- /* DO NOT restore GC tracking at this point. weakref callbacks -- * (if any, and whether directly here or indirectly in something we -- * call) may trigger GC, and if self is tracked at that point, it -- * will look like trash to GC and GC will try to delete self again. -- */ -+ Py_TRASHCAN_BEGIN(self, subtype_dealloc); - - /* Find the nearest base with a different tp_dealloc */ - base = type; -@@ -1053,11 +1043,7 @@ subtype_dealloc(PyObject *self) - Py_DECREF(type); - - endlabel: -- ++_PyTrash_delete_nesting; -- ++ tstate->trash_delete_nesting; -- Py_TRASHCAN_SAFE_END(self); -- --_PyTrash_delete_nesting; -- -- tstate->trash_delete_nesting; -+ Py_TRASHCAN_END - - /* Explanation of the weirdness around the trashcan macros: - -@@ -1094,67 +1080,6 @@ subtype_dealloc(PyObject *self) - looks like trash to gc too, and gc also tries to delete self - then. But we're already deleting self. Double deallocation is - a subtle disaster. -- -- Q. Why the bizarre (net-zero) manipulation of -- _PyTrash_delete_nesting around the trashcan macros? -- -- A. Some base classes (e.g. list) also use the trashcan mechanism. -- The following scenario used to be possible: -- -- - suppose the trashcan level is one below the trashcan limit -- -- - subtype_dealloc() is called -- -- - the trashcan limit is not yet reached, so the trashcan level -- is incremented and the code between trashcan begin and end is -- executed -- -- - this destroys much of the object's contents, including its -- slots and __dict__ -- -- - basedealloc() is called; this is really list_dealloc(), or -- some other type which also uses the trashcan macros -- -- - the trashcan limit is now reached, so the object is put on the -- trashcan's to-be-deleted-later list -- -- - basedealloc() returns -- -- - subtype_dealloc() decrefs the object's type -- -- - subtype_dealloc() returns -- -- - later, the trashcan code starts deleting the objects from its -- to-be-deleted-later list -- -- - subtype_dealloc() is called *AGAIN* for the same object -- -- - at the very least (if the destroyed slots and __dict__ don't -- cause problems) the object's type gets decref'ed a second -- time, which is *BAD*!!! -- -- The remedy is to make sure that if the code between trashcan -- begin and end in subtype_dealloc() is called, the code between -- trashcan begin and end in basedealloc() will also be called. -- This is done by decrementing the level after passing into the -- trashcan block, and incrementing it just before leaving the -- block. -- -- But now it's possible that a chain of objects consisting solely -- of objects whose deallocator is subtype_dealloc() will defeat -- the trashcan mechanism completely: the decremented level means -- that the effective level never reaches the limit. Therefore, we -- *increment* the level *before* entering the trashcan block, and -- matchingly decrement it after leaving. This means the trashcan -- code will trigger a little early, but that's no big deal. -- -- Q. Are there any live examples of code in need of all this -- complexity? -- -- A. Yes. See SF bug 668433 for code that crashed (when Python was -- compiled in debug mode) before the trashcan level manipulations -- were added. For more discussion, see SF patches 581742, 575073 -- and bug 574207. - */ - } - -diff --git a/Python/traceback.c b/Python/traceback.c -index fd5309a..38327b6 100644 ---- a/Python/traceback.c -+++ b/Python/traceback.c -@@ -23,11 +23,11 @@ static void - tb_dealloc(PyTracebackObject *tb) - { - PyObject_GC_UnTrack(tb); -- Py_TRASHCAN_SAFE_BEGIN(tb) -+ Py_TRASHCAN_BEGIN(tb, tb_dealloc) - Py_XDECREF(tb->tb_next); - Py_XDECREF(tb->tb_frame); - PyObject_GC_Del(tb); -- Py_TRASHCAN_SAFE_END(tb) -+ Py_TRASHCAN_END - } - - static int diff --git a/build/pkgs/python2/patches/uuid-issue_11063.patch b/build/pkgs/python2/patches/uuid-issue_11063.patch deleted file mode 100644 index 731c4325cb8..00000000000 --- a/build/pkgs/python2/patches/uuid-issue_11063.patch +++ /dev/null @@ -1,154 +0,0 @@ -diff --git a/Lib/uuid.py b/Lib/uuid.py -index 7432032..0cbf9f1 100644 ---- a/Lib/uuid.py -+++ b/Lib/uuid.py -@@ -437,68 +437,86 @@ def _netbios_getnode(): - return ((bytes[0]<<40L) + (bytes[1]<<32L) + (bytes[2]<<24L) + - (bytes[3]<<16L) + (bytes[4]<<8L) + bytes[5]) - --# Thanks to Thomas Heller for ctypes and for his help with its use here. -+_ctypes_lib = None - --# If ctypes is available, use it to find system routines for UUID generation. --_uuid_generate_time = _UuidCreate = None --try: -- import ctypes, ctypes.util -- import sys -+def _uuid_generate(attr): -+ """Find system routines for UUID generation""" -+ -+ # Thanks to Thomas Heller for ctypes and for his help with its use here. -+ try: -+ import ctypes -+ import ctypes.util -+ -+ global _ctypes_lib -+ -+ uuid = None -+ # The uuid_generate_* routines are provided by libuuid on at least -+ # Linux and FreeBSD, and provided by libc on Mac OS X. -+ for libname in ['uuid', 'c']: -+ try: -+ if _ctypes_lib is None: -+ _ctypes_lib = ctypes.CDLL(ctypes.util.find_library(libname)) -+ lib = _ctypes_lib -+ except: -+ continue -+ if hasattr(lib, attr): -+ uuid = getattr(lib, attr) -+ break # found what we were looking for -+ -+ # The uuid_generate_* functions are broken on MacOS X 10.5, as noted -+ # in issue #8621 the function generates the same sequence of values -+ # in the parent process and all children created using fork (unless -+ # those children use exec as well). -+ # -+ # Assume that the uuid_generate functions are broken from 10.5 onward, -+ # the test can be adjusted when a later version is fixed. -+ import sys -+ if sys.platform == 'darwin': -+ import os -+ if int(os.uname()[2].split('.')[0]) >= 9: -+ uuid = None -+ return uuid -+ except: -+ pass - -- # The uuid_generate_* routines are provided by libuuid on at least -- # Linux and FreeBSD, and provided by libc on Mac OS X. -- _libnames = ['uuid'] -- if not sys.platform.startswith('win'): -- _libnames.append('c') -- for libname in _libnames: -+ -+def _uuid_create(): -+ """Get random UUID on Windows platform.""" -+ -+ try: -+ # On Windows prior to 2000, UuidCreate gives a UUID containing the -+ # hardware address. On Windows 2000 and later, UuidCreate makes a -+ # random UUID and UuidCreateSequential gives a UUID containing the -+ # hardware address. These routines are provided by the RPC runtime. -+ # NOTE: at least on Tim's WinXP Pro SP2 desktop box, while the last -+ # 6 bytes returned by UuidCreateSequential are fixed, they don't appear -+ # to bear any relationship to the MAC address of any network device -+ # on the box. - try: -- lib = ctypes.CDLL(ctypes.util.find_library(libname)) -+ import ctypes -+ lib = ctypes.windll.rpcrt4 - except: -- continue -- if hasattr(lib, 'uuid_generate_time'): -- _uuid_generate_time = lib.uuid_generate_time -- break -- del _libnames -- -- # The uuid_generate_* functions are broken on MacOS X 10.5, as noted -- # in issue #8621 the function generates the same sequence of values -- # in the parent process and all children created using fork (unless -- # those children use exec as well). -- # -- # Assume that the uuid_generate functions are broken from 10.5 onward, -- # the test can be adjusted when a later version is fixed. -- if sys.platform == 'darwin': -- import os -- if int(os.uname()[2].split('.')[0]) >= 9: -- _uuid_generate_time = None -- -- # On Windows prior to 2000, UuidCreate gives a UUID containing the -- # hardware address. On Windows 2000 and later, UuidCreate makes a -- # random UUID and UuidCreateSequential gives a UUID containing the -- # hardware address. These routines are provided by the RPC runtime. -- # NOTE: at least on Tim's WinXP Pro SP2 desktop box, while the last -- # 6 bytes returned by UuidCreateSequential are fixed, they don't appear -- # to bear any relationship to the MAC address of any network device -- # on the box. -- try: -- lib = ctypes.windll.rpcrt4 -+ lib = None -+ uuid = getattr(lib, 'UuidCreateSequential', -+ getattr(lib, 'UuidCreate', None)) -+ return uuid - except: -- lib = None -- _UuidCreate = getattr(lib, 'UuidCreateSequential', -- getattr(lib, 'UuidCreate', None)) --except: -- pass -+ pass - - def _unixdll_getnode(): - """Get the hardware address on Unix using ctypes.""" -+ import ctypes - _buffer = ctypes.create_string_buffer(16) -- _uuid_generate_time(_buffer) -+ uuid_generate_time = _uuid_generate("uuid_generate_time") -+ uuid_generate_time(_buffer) - return UUID(bytes=_buffer.raw).node - - def _windll_getnode(): - """Get the hardware address on Windows using ctypes.""" -+ import ctypes - _buffer = ctypes.create_string_buffer(16) -- if _UuidCreate(_buffer) == 0: -+ UuidCreate = _uuid_create() -+ if UuidCreate(_buffer) == 0: - return UUID(bytes=_buffer.raw).node - - def _random_getnode(): -@@ -546,9 +564,12 @@ def uuid1(node=None, clock_seq=None): - - # When the system provides a version-1 UUID generator, use it (but don't - # use UuidCreate here because its UUIDs don't conform to RFC 4122). -- if _uuid_generate_time and node is clock_seq is None: -+ uuid_generate_time = _uuid_generate("uuid_generate_time") -+ -+ if uuid_generate_time and node is clock_seq is None: -+ import ctypes - _buffer = ctypes.create_string_buffer(16) -- _uuid_generate_time(_buffer) -+ uuid_generate_time(_buffer) - return UUID(bytes=_buffer.raw) - - global _last_timestamp diff --git a/build/pkgs/python2/spkg-build b/build/pkgs/python2/spkg-build deleted file mode 120000 index 909d1150687..00000000000 --- a/build/pkgs/python2/spkg-build +++ /dev/null @@ -1 +0,0 @@ -../python3/spkg-build \ No newline at end of file diff --git a/build/pkgs/python2/spkg-check b/build/pkgs/python2/spkg-check deleted file mode 120000 index dbd03823d14..00000000000 --- a/build/pkgs/python2/spkg-check +++ /dev/null @@ -1 +0,0 @@ -../python3/spkg-check \ No newline at end of file diff --git a/build/pkgs/python2/spkg-install b/build/pkgs/python2/spkg-install deleted file mode 120000 index 978e0367c57..00000000000 --- a/build/pkgs/python2/spkg-install +++ /dev/null @@ -1 +0,0 @@ -../python3/spkg-install \ No newline at end of file diff --git a/build/pkgs/python3/distros/alpine.txt b/build/pkgs/python3/distros/alpine.txt new file mode 100644 index 00000000000..596ce366935 --- /dev/null +++ b/build/pkgs/python3/distros/alpine.txt @@ -0,0 +1 @@ +python3-dev diff --git a/build/pkgs/python3/distros/cygwin.txt b/build/pkgs/python3/distros/cygwin.txt new file mode 100644 index 00000000000..9647f8a6a57 --- /dev/null +++ b/build/pkgs/python3/distros/cygwin.txt @@ -0,0 +1,2 @@ +# as of #27824, we use python3 for venv as well +python37-devel diff --git a/build/pkgs/python3/distros/fedora.txt b/build/pkgs/python3/distros/fedora.txt new file mode 100644 index 00000000000..07358a92e89 --- /dev/null +++ b/build/pkgs/python3/distros/fedora.txt @@ -0,0 +1 @@ +python3-devel diff --git a/build/pkgs/python3/distros/homebrew.txt b/build/pkgs/python3/distros/homebrew.txt new file mode 100644 index 00000000000..b9f04a4ea63 --- /dev/null +++ b/build/pkgs/python3/distros/homebrew.txt @@ -0,0 +1,2 @@ +# This installs /usr/local/bin/python3 -> python3.7 +python3 diff --git a/build/pkgs/python3/distros/opensuse.txt b/build/pkgs/python3/distros/opensuse.txt new file mode 100644 index 00000000000..07358a92e89 --- /dev/null +++ b/build/pkgs/python3/distros/opensuse.txt @@ -0,0 +1 @@ +python3-devel diff --git a/build/pkgs/python3/spkg-build b/build/pkgs/python3/spkg-build deleted file mode 100644 index 47d9cdf3a30..00000000000 --- a/build/pkgs/python3/spkg-build +++ /dev/null @@ -1,127 +0,0 @@ -# It is best to unset these environment variables, as they might confuse -# the Python installer. -unset PYTHONHOME -unset PYTHONPATH - -# Prevent use of the system hg and svn as it might make the installation fail -export HAS_HG=no -export SVNVERSION=no - -cd src - -if [ "$SAGE_DEBUG" = "yes" ]; then - echo "Building Python with pydebug" - PYTHON_CONFIGURE="$PYTHON_CONFIGURE --with-pydebug" -fi - -# pymalloc screws with valgrind, so let's disable it -if [ "$SAGE_VALGRIND" = "yes" ]; then - echo "Building Python without pymalloc" - PYTHON_CONFIGURE="$PYTHON_CONFIGURE --without-pymalloc" -fi - -# Use EXTRA_CFLAGS for user-defined CFLAGS since Python puts its own -# default flags like -O3 after CFLAGS but before EXTRA_CFLAGS. -# We also disable warnings about unused variables/functions which are -# common in Cython-generated code. -export EXTRA_CFLAGS="`testcflags.sh -Wno-unused` $CFLAGS" -unset CFLAGS - -if [ "$UNAME" = Darwin ]; then - PYTHON_CONFIGURE="--disable-toolbox-glue $PYTHON_CONFIGURE" - - mkdir "../include" - if [ -n "$OPENSSL_INCLUDE" ]; then - # If the user explicitely states where to get the openssl - # includes, use that. - cp -rp "$OPENSSL_INCLUDE" "../include" - export CFLAGS="-I../include" - else - # Otherwise try using homebrew version - brew_openssl="/usr/local/opt/openssl/include" - if [ -d "$brew_openssl" ]; then - export LDFLAGS="$LDFLAGS -L/usr/local/opt/openssl/lib" - export CPPFLAGS="$CPPFLAGS -I/usr/local/opt/openssl/include" - export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/openssl/lib/pkgconfig" - fi - fi - - if [ $MACOSX_VERSION -ge 16 ]; then - echo "OS X 10.$[$MACOSX_VERSION-4] Building with clang." - CC=clang - fi -elif [ "$UNAME" = SunOS ]; then - # Enable some C99 features on Solaris. This in particular enables - # the isinf() and isfinite() functions. It works both for C and - # C++ code (which is not true for -std=c99). See - # http://trac.sagemath.org/sage_trac/ticket/14265 - export CFLAGS="-D__C99FEATURES__ $CFLAGS" -fi - -# Remove old symbolic link: it is not needed and its presence can -# interfere with the Python build. -rm -f "$SAGE_LOCAL/lib/python" - -# Remove old libraries. We really need to do this before building Python -# since Python tries to import some modules (e.g. ctypes) at build-time. -# We need to make sure that the old installed libraries in local/lib are -# not used for that. See https://trac.sagemath.org/ticket/24605 -rm -f "$SAGE_LOCAL"/lib/lib"$PKG_BASE"* - - -if [ "$PKG_BASE" = "python2" ]; then - PYTHON_CONFIGURE="$PYTHON_CONFIGURE --enable-unicode=ucs4" -else - # Note: --without-ensurepip ensures that setuptools+pip are *not* installed - # automatically when installing python3. They will be installed instead by - # the separate setuptools and pip packages; see - # https://trac.sagemath.org/ticket/23398 - PYTHON_CONFIGURE="$PYTHON_CONFIGURE --without-ensurepip" -fi - -sdh_configure --enable-shared $PYTHON_CONFIGURE - -# Make sure -L. is placed before -L$SAGE_LOCAL/lib so that python and extension -# modules are linked with the right libpython; we pass this in at make time -# only, since we don't want -L. to be saved as one of the default LDFLAGS -# used for building third-party extension modules -sdh_make LDFLAGS="-L. $LDFLAGS" - -if [ "$UNAME" = "Darwin" ]; then - export DYLD_LIBRARY_PATH="." -else - export LD_LIBRARY_PATH="." -fi - -# When building on a case-insensitive filesystem (on any OS, not just Windows) -# the Python executable is output to the build directory as 'python.exe' -if [ -f "python.exe" ]; then - PYTHON="./python.exe" -else - PYTHON="./python" -fi - -# Make sure extension modules were built correctly. -# All these modules are important and if any one -# fails to build, Sage will not work. - -echo "Testing importing of various modules..." -import_errors=false -test_modules="ctypes math hashlib crypt readline socket" -if [ "$UNAME" = "Darwin" ]; then - test_modules="$test_modules _scproxy" -fi - -for module in $test_modules; do - if $PYTHON -c "import $module"; then - echo "$module module imported OK" - else - echo >&2 "$module module failed to import" - import_errors=true - fi -done - -if $import_errors; then - echo >&2 "Error: One or more modules failed to import." - exit 1 -fi diff --git a/build/pkgs/python3/spkg-build.in b/build/pkgs/python3/spkg-build.in new file mode 100644 index 00000000000..a1aadb82f41 --- /dev/null +++ b/build/pkgs/python3/spkg-build.in @@ -0,0 +1,127 @@ +# It is best to unset these environment variables, as they might confuse +# the Python installer. +unset PYTHONHOME +unset PYTHONPATH + +# Prevent use of the system hg and svn as it might make the installation fail +export HAS_HG=no +export SVNVERSION=no + +cd src + +if [ "$SAGE_DEBUG" = "yes" ]; then + echo "Building Python with pydebug" + PYTHON_CONFIGURE="$PYTHON_CONFIGURE --with-pydebug" +fi + +# pymalloc screws with valgrind, so let's disable it +if [ "$SAGE_VALGRIND" = "yes" ]; then + echo "Building Python without pymalloc" + PYTHON_CONFIGURE="$PYTHON_CONFIGURE --without-pymalloc" +fi + +# Use EXTRA_CFLAGS for user-defined CFLAGS since Python puts its own +# default flags like -O3 after CFLAGS but before EXTRA_CFLAGS. +# We also disable warnings about unused variables/functions which are +# common in Cython-generated code. +export EXTRA_CFLAGS="`testcflags.sh -Wno-unused` $CFLAGS" +unset CFLAGS + +if [ "$UNAME" = Darwin ]; then + PYTHON_CONFIGURE="--disable-toolbox-glue $PYTHON_CONFIGURE" + + mkdir "../include" + if [ -n "$OPENSSL_INCLUDE" ]; then + # If the user explicitely states where to get the openssl + # includes, use that. + cp -rp "$OPENSSL_INCLUDE" "../include" + export CFLAGS="-I../include" + else + # Otherwise try using homebrew version + brew_openssl="/usr/local/opt/openssl/include" + if [ -d "$brew_openssl" ]; then + export LDFLAGS="$LDFLAGS -L/usr/local/opt/openssl/lib" + export CPPFLAGS="$CPPFLAGS -I/usr/local/opt/openssl/include" + export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/openssl/lib/pkgconfig" + fi + fi + + if [ $MACOSX_VERSION -ge 16 ]; then + echo "OS X 10.$[$MACOSX_VERSION-4] Building with clang." + CC=clang + fi +elif [ "$UNAME" = SunOS ]; then + # Enable some C99 features on Solaris. This in particular enables + # the isinf() and isfinite() functions. It works both for C and + # C++ code (which is not true for -std=c99). See + # http://trac.sagemath.org/sage_trac/ticket/14265 + export CFLAGS="-D__C99FEATURES__ $CFLAGS" +fi + +# Remove old symbolic link: it is not needed and its presence can +# interfere with the Python build. +rm -f "$SAGE_LOCAL/lib/python" + +# Remove old libraries. We really need to do this before building Python +# since Python tries to import some modules (e.g. ctypes) at build-time. +# We need to make sure that the old installed libraries in local/lib are +# not used for that. See https://trac.sagemath.org/ticket/24605 +rm -f "$SAGE_LOCAL"/lib/lib"$PKG_BASE"* + + +if [ "$PKG_BASE" = "python2" ]; then + PYTHON_CONFIGURE="$PYTHON_CONFIGURE --enable-unicode=ucs4" +else + # Note: --without-ensurepip ensures that setuptools+pip are *not* installed + # automatically when installing python3. They will be installed instead by + # the separate setuptools and pip packages; see + # https://trac.sagemath.org/ticket/23398 + PYTHON_CONFIGURE="$PYTHON_CONFIGURE --without-ensurepip" +fi + +sdh_configure --enable-shared $PYTHON_CONFIGURE + +# Make sure -L. is placed before -L$SAGE_LOCAL/lib so that python and extension +# modules are linked with the right libpython; we pass this in at make time +# only, since we don't want -L. to be saved as one of the default LDFLAGS +# used for building third-party extension modules +sdh_make LDFLAGS="-L. $LDFLAGS" + +if [ "$UNAME" = "Darwin" ]; then + export DYLD_LIBRARY_PATH="." +else + export LD_LIBRARY_PATH="." +fi + +# When building on a case-insensitive filesystem (on any OS, not just Windows) +# the Python executable is output to the build directory as 'python.exe' +if [ -f "python.exe" ]; then + PYTHON="./python.exe" +else + PYTHON="./python" +fi + +# Make sure extension modules were built correctly. +# All these modules are important and if any one +# fails to build, Sage will not work. + +echo "Testing importing of various modules..." +import_errors=false +test_modules="ctypes math hashlib crypt readline socket zlib sqlite3" +if [ "$UNAME" = "Darwin" ]; then + test_modules="$test_modules _scproxy" +fi + +for module in $test_modules; do + if $PYTHON -c "import $module"; then + echo "$module module imported OK" + else + echo >&2 "$module module failed to import" + import_errors=true + fi +done + +if $import_errors; then + echo >&2 "Error: One or more modules failed to import." + exit 1 +fi diff --git a/build/pkgs/python3/spkg-check b/build/pkgs/python3/spkg-check deleted file mode 100644 index 9628d8a5617..00000000000 --- a/build/pkgs/python3/spkg-check +++ /dev/null @@ -1,8 +0,0 @@ -cd src -$MAKE test - -if [ $? -ne 0 ]; then - echo "An error occurred while testing Python" - exit 1 -fi - diff --git a/build/pkgs/python3/spkg-check.in b/build/pkgs/python3/spkg-check.in new file mode 100644 index 00000000000..11af4c50e28 --- /dev/null +++ b/build/pkgs/python3/spkg-check.in @@ -0,0 +1,2 @@ +cd src +$MAKE test diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 new file mode 100644 index 00000000000..2989803eba9 --- /dev/null +++ b/build/pkgs/python3/spkg-configure.m4 @@ -0,0 +1,158 @@ +SAGE_SPKG_CONFIGURE([python3], [ + SAGE_SPKG_DEPCHECK([sqlite libpng bzip2 xz libffi], [ + AS_IF([test $SAGE_PYTHON_VERSION = 2], [ + dnl If we use Python 2 for Sage, we install Python 3 too and do NOT attempt to do + dnl venv using system python3 over SAGE_LOCAL. + dnl (In particular, the setuptools and pip install scripts are not prepared for + dnl handling this situation.) + sage_spkg_install_python3=yes + ], [ + dnl Using Python 3 for Sage. Check if we can do venv with a system python3 + dnl instead of building our own copy. + check_modules="sqlite3, ctypes, math, hashlib, crypt, readline, socket, zlib, distutils.core" + AC_CACHE_CHECK([for python3 >= 3.6, < 3.8 with modules $check_modules], [ac_cv_path_PYTHON3], [ + AC_MSG_RESULT([]) + AC_PATH_PROGS_FEATURE_CHECK([PYTHON3], [python3.7 python3.6 python3], [ + AC_MSG_CHECKING([... whether $ac_path_PYTHON3 is good]) + python3_version=`"$ac_path_PYTHON3" --version 2>&1 \ + | $SED -n -e 's/\([[0-9]]*\.[[0-9]]*\.[[0-9]]*\).*/\1/p'` + AS_IF([test -n "$python3_version"], [ + AX_COMPARE_VERSION([$python3_version], [ge], [3.6.0], [ + AX_COMPARE_VERSION([$python3_version], [lt], [3.8.0], [ + dnl Because the system python is not used directly but rather in a venv without site-packages, + dnl we test whether the module will be available in a venv. + dnl Otherwise, some system site-package may be providing this module to the system python. + dnl m4_define([conftest_venv], [config-venv]) .... for debugging only + rm -rf conftest_venv + AS_IF(["$ac_path_PYTHON3" build/bin/sage-venv conftest_venv && conftest_venv/bin/python3 -c "import $check_modules"], [ + AC_LANG_PUSH([C]) + AC_LANG_CONFTEST([ + AC_LANG_SOURCE([[ +#define PY_SSIZE_T_CLEAN +#include +static PyMethodDef SpamMethods[] = { + {NULL, NULL, 0, NULL} /* Sentinel */ +}; +static struct PyModuleDef spammodule = { + PyModuleDef_HEAD_INIT, + "spam", /* name of module */ + NULL, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + SpamMethods +}; +PyMODINIT_FUNC +PyInit_spam(void) +{ + PyObject *m; + + m = PyModule_Create(&spammodule); + return m; +} + ]]) + ]) + AC_LANG_POP([C]) + cat > conftest.py <& AS_MESSAGE_LOG_FD + echo CC="$CC" CXX="$CXX" conftest_venv/bin/python3 conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD + AS_IF([CC="$CC" CXX="$CXX" conftest_venv/bin/python3 conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD 2>&1 ], [ + rm -rf conftest.* + AC_LANG_PUSH([C++]) + AC_LANG_CONFTEST([ + AC_LANG_SOURCE([[ +#define PY_SSIZE_T_CLEAN +#include +static PyMethodDef SpamMethods[] = { + {NULL, NULL, 0, NULL} /* Sentinel */ +}; +static struct PyModuleDef spammodule = { + PyModuleDef_HEAD_INIT, + "spam", /* name of module */ + NULL, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + SpamMethods +}; +PyMODINIT_FUNC +PyInit_spam(void) +{ + PyObject *m; + + m = PyModule_Create(&spammodule); + return m; +} +// Partial C++11 test, from ax_cxx_compile_stdcxx.m4 + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + ]]) + ]) + AC_LANG_POP([C++]) + cat > conftest.py <& AS_MESSAGE_LOG_FD 2>&1 ], [ + ac_cv_path_PYTHON3="$ac_path_PYTHON3" + ac_path_PYTHON3_found=: + AC_MSG_RESULT([yes]) + dnl introduction for AC_MSG_RESULT printed by AC_CACHE_CHECK + AC_MSG_CHECKING([for python3 >= 3.6, < 3.8 with modules $check_modules]) + ], [ + AC_MSG_RESULT([no, the version is in the supported range, and the modules can be imported, but distutils cannot build a C++ 11 extension]) + ]) + ], [ + AC_MSG_RESULT([no, the version is in the supported range, and the modules can be imported, but distutils cannot build a C extension]) + ]) + ], [ + AC_MSG_RESULT([no, the version is in the supported range but cannot import one of the required modules: $check_modules]) + ]) + ], [ + AC_MSG_RESULT([no, $python3_version is too recent]) + ]) + ], [ + AC_MSG_RESULT([no, $python3_version is too old]) + ]) + ], [ + AC_MSG_RESULT([no, "$ac_path_PYTHON3 --version" does not work]) + ]) + ]) + ]) + AS_IF([test -z "$ac_cv_path_PYTHON3"], + [sage_spkg_install_python3=yes]) + ]) + ]) +],, [ + dnl PRE +], [ + dnl POST + AS_IF([test x$sage_spkg_install_python3 = xno], [PYTHON_FOR_VENV="$ac_cv_path_PYTHON3"]) + AC_SUBST([PYTHON_FOR_VENV]) + + dnl These temporary directories are created by the check above + dnl and need to be cleaned up to prevent the "rm -f conftest*" + dnl (that a bunch of other checks do) from emitting warnings about + dnl conftest.dir and conftest_venv being directories. + rm -rf conftest.dir conftest_venv +]) diff --git a/build/pkgs/python3/spkg-install b/build/pkgs/python3/spkg-install.in similarity index 100% rename from build/pkgs/python3/spkg-install rename to build/pkgs/python3/spkg-install.in diff --git a/build/pkgs/python_igraph/SPKG.rst b/build/pkgs/python_igraph/SPKG.rst new file mode 100644 index 00000000000..04887d77367 --- /dev/null +++ b/build/pkgs/python_igraph/SPKG.rst @@ -0,0 +1,31 @@ + +python-igraph +============= + +Description +----------- + +igraph is a library for creating and manipulating graphs. It is intended +to be as powerful (ie. fast) as possible to enable the analysis of large +graphs. + +License +------- + +GPL version 2 + + +Upstream Contact +---------------- + +http://igraph.org/python/ + +Dependencies +------------ + +- python +- igraph + + +Special Update/Build Instructions +--------------------------------- diff --git a/build/pkgs/python_igraph/SPKG.txt b/build/pkgs/python_igraph/SPKG.txt deleted file mode 100644 index 8a6721bb25d..00000000000 --- a/build/pkgs/python_igraph/SPKG.txt +++ /dev/null @@ -1,22 +0,0 @@ -= python-igraph = - -== Description == - -igraph is a library for creating and manipulating graphs. -It is intended to be as powerful (ie. fast) as possible to enable the -analysis of large graphs. - -== License == - -GPL version 2 - -== Upstream Contact == - -http://igraph.org/python/ - -== Dependencies == - -* python -* igraph - -== Special Update/Build Instructions == diff --git a/build/pkgs/python_igraph/dependencies b/build/pkgs/python_igraph/dependencies index 661ac2bfdcf..99b6522bbbc 100644 --- a/build/pkgs/python_igraph/dependencies +++ b/build/pkgs/python_igraph/dependencies @@ -1,4 +1,4 @@ -igraph $(PYTHON) | pip +igraph $(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/python_igraph/spkg-check b/build/pkgs/python_igraph/spkg-check.in similarity index 100% rename from build/pkgs/python_igraph/spkg-check rename to build/pkgs/python_igraph/spkg-check.in diff --git a/build/pkgs/python_igraph/spkg-install b/build/pkgs/python_igraph/spkg-install.in similarity index 100% rename from build/pkgs/python_igraph/spkg-install rename to build/pkgs/python_igraph/spkg-install.in diff --git a/build/pkgs/python_openid/SPKG.rst b/build/pkgs/python_openid/SPKG.rst new file mode 100644 index 00000000000..5219fa1e117 --- /dev/null +++ b/build/pkgs/python_openid/SPKG.rst @@ -0,0 +1,14 @@ + +python-openid +============= + +Description +----------- + +OpenID support for servers and consumers. + +This is a set of Python packages to support use of the OpenID +decentralized identity system in your application. Want to enable single +sign-on for your web site? Use the openid.consumer package. Want to run +your own OpenID server? Check out openid.server. Includes example code +and support for a variety of storage back-ends. diff --git a/build/pkgs/python_openid/SPKG.txt b/build/pkgs/python_openid/SPKG.txt deleted file mode 100644 index 390a18695b1..00000000000 --- a/build/pkgs/python_openid/SPKG.txt +++ /dev/null @@ -1,11 +0,0 @@ -= python-openid = - -== Description == - -OpenID support for servers and consumers. - -This is a set of Python packages to support use of the OpenID decentralized -identity system in your application. Want to enable single sign-on for your -web site? Use the openid.consumer package. Want to run your own OpenID server? -Check out openid.server. Includes example code and support for a variety of -storage back-ends. diff --git a/build/pkgs/python_openid/dependencies b/build/pkgs/python_openid/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/python_openid/dependencies +++ b/build/pkgs/python_openid/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pathpy/spkg-install b/build/pkgs/python_openid/spkg-install.in similarity index 100% rename from build/pkgs/pathpy/spkg-install rename to build/pkgs/python_openid/spkg-install.in diff --git a/build/pkgs/python_openid/type b/build/pkgs/python_openid/type index a6a7b9cd726..134d9bc32d5 100644 --- a/build/pkgs/python_openid/type +++ b/build/pkgs/python_openid/type @@ -1 +1 @@ -standard +optional diff --git a/build/pkgs/pytz/SPKG.rst b/build/pkgs/pytz/SPKG.rst new file mode 100644 index 00000000000..ebeba1ad914 --- /dev/null +++ b/build/pkgs/pytz/SPKG.rst @@ -0,0 +1,16 @@ +pytz +==== + +Description +----------- + +World Timezone Definitions for Python + + +Special Update/Build Instructions +--------------------------------- + +The upstream tarball was repackaged after sanitizing the file +permissions with + +$ chmod go-w diff --git a/build/pkgs/pytz/SPKG.txt b/build/pkgs/pytz/SPKG.txt deleted file mode 100644 index 94190513f91..00000000000 --- a/build/pkgs/pytz/SPKG.txt +++ /dev/null @@ -1,12 +0,0 @@ -= pytz = - -== Description == - -World Timezone Definitions for Python - -== Special Update/Build Instructions == - -The upstream tarball was repackaged after sanitizing the file -permissions with - -$ chmod go-w diff --git a/build/pkgs/pytz/checksums.ini b/build/pkgs/pytz/checksums.ini index 08b15bb59c5..d4db734e428 100644 --- a/build/pkgs/pytz/checksums.ini +++ b/build/pkgs/pytz/checksums.ini @@ -1,4 +1,5 @@ -tarball=pytz-VERSION.tar.bz2 -sha1=8a01d7f19c8b8b189827e026b76c51be4b537b89 -md5=4b91594c440aa20c76ac92043efa75e1 -cksum=591864951 +tarball=pytz-VERSION.tar.gz +sha1=60f6db35e92688c4701c16bd3d2b9cbfac1fd604 +md5=0349106ac02f2bfe565dd6d5594e3a15 +cksum=1599443821 +upstream_url=https://pypi.io/packages/source/p/pytz/pytz-VERSION.tar.gz diff --git a/build/pkgs/pytz/dependencies b/build/pkgs/pytz/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/pytz/dependencies +++ b/build/pkgs/pytz/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pytz/package-version.txt b/build/pkgs/pytz/package-version.txt index b3d91a9d2f9..b0a4aa2fe24 100644 --- a/build/pkgs/pytz/package-version.txt +++ b/build/pkgs/pytz/package-version.txt @@ -1 +1 @@ -2018.7 +2020.1 diff --git a/build/pkgs/pickleshare/spkg-install b/build/pkgs/pytz/spkg-install.in similarity index 100% rename from build/pkgs/pickleshare/spkg-install rename to build/pkgs/pytz/spkg-install.in diff --git a/build/pkgs/pyx/SPKG.rst b/build/pkgs/pyx/SPKG.rst new file mode 100644 index 00000000000..f9a4a5b88d5 --- /dev/null +++ b/build/pkgs/pyx/SPKG.rst @@ -0,0 +1,9 @@ +pyx +=== + +Description +----------- + +Python package for the generation of PostScript, PDF, and SVG files + +https://pypi.python.org/pypi/PyX diff --git a/build/pkgs/pyx/SPKG.txt b/build/pkgs/pyx/SPKG.txt deleted file mode 100644 index 6f3e0998d88..00000000000 --- a/build/pkgs/pyx/SPKG.txt +++ /dev/null @@ -1,8 +0,0 @@ -= pyx = - -== Description == - -Python package for the generation of PostScript, PDF, and SVG files - -https://pypi.python.org/pypi/PyX - diff --git a/build/pkgs/pyx/requirements.txt b/build/pkgs/pyx/requirements.txt new file mode 100644 index 00000000000..f0c6de91d18 --- /dev/null +++ b/build/pkgs/pyx/requirements.txt @@ -0,0 +1 @@ +pyx diff --git a/build/pkgs/pyx/type b/build/pkgs/pyx/type index 4d6aa0d777e..134d9bc32d5 100644 --- a/build/pkgs/pyx/type +++ b/build/pkgs/pyx/type @@ -1 +1 @@ -pip \ No newline at end of file +optional diff --git a/build/pkgs/pyzmq/SPKG.rst b/build/pkgs/pyzmq/SPKG.rst new file mode 100644 index 00000000000..86594e23d02 --- /dev/null +++ b/build/pkgs/pyzmq/SPKG.rst @@ -0,0 +1,31 @@ +pyzmq +===== + +Description +----------- + +Python bindings for the zeromq networking library. + +License +------- + +LGPLv3+ + + +Upstream Contact +---------------- + +http://www.zeromq.org + +Dependencies +------------ + +- Python +- Cython +- zeromq + + +Special Update/Build Instructions +--------------------------------- + +None. diff --git a/build/pkgs/pyzmq/SPKG.txt b/build/pkgs/pyzmq/SPKG.txt deleted file mode 100644 index 86903427d03..00000000000 --- a/build/pkgs/pyzmq/SPKG.txt +++ /dev/null @@ -1,24 +0,0 @@ -= pyzmq = - -== Description == - -Python bindings for the zeromq networking library. - -== License == - -LGPLv3+ - -== Upstream Contact == - -http://www.zeromq.org - -== Dependencies == - -* Python -* Cython -* zeromq - -== Special Update/Build Instructions == - -None. - diff --git a/build/pkgs/pyzmq/checksums.ini b/build/pkgs/pyzmq/checksums.ini index 07d4c095b56..6e834f7ac78 100644 --- a/build/pkgs/pyzmq/checksums.ini +++ b/build/pkgs/pyzmq/checksums.ini @@ -1,4 +1,4 @@ tarball=pyzmq-VERSION.tar.gz -sha1=64799b73d6109fb6da5b7deb6101ba13cd7fe885 -md5=3c8039d007bbbd08a2275f52f5dc9a35 -cksum=1455168757 +sha1=b84077344ed67649f34d4af8f619d96acbd03e73 +md5=4650e45ebcf8e08620211c0e720d6066 +cksum=2135729608 diff --git a/build/pkgs/pyzmq/dependencies b/build/pkgs/pyzmq/dependencies index ee0a2bd37f3..ab148841d34 100644 --- a/build/pkgs/pyzmq/dependencies +++ b/build/pkgs/pyzmq/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) cython zeromq | setuptools pip +$(PYTHON) cython zeromq | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pyzmq/package-version.txt b/build/pkgs/pyzmq/package-version.txt index d4df1049f25..49e3587fb62 100644 --- a/build/pkgs/pyzmq/package-version.txt +++ b/build/pkgs/pyzmq/package-version.txt @@ -1 +1 @@ -18.1.0 +19.0.0 diff --git a/build/pkgs/pyzmq/spkg-install b/build/pkgs/pyzmq/spkg-install.in similarity index 100% rename from build/pkgs/pyzmq/spkg-install rename to build/pkgs/pyzmq/spkg-install.in diff --git a/build/pkgs/qepcad/SPKG.rst b/build/pkgs/qepcad/SPKG.rst new file mode 100644 index 00000000000..be23ccc167c --- /dev/null +++ b/build/pkgs/qepcad/SPKG.rst @@ -0,0 +1,49 @@ +qepcad +====== + +Description +----------- + +Qepcad is an implementation of quantifier elimination by partial +cylindrical algebraic decomposition + +License +------- + +QEPCAD B Copyright (c) 1990, 2008, Hoon Hong & Chris Brown (contact +wcbrown@usna.edu) + +Permission to use, copy, modify, and/or distribute this software, +including source files, README files, etc., for any purpose with or +without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +Upstream Contact +---------------- + +- Website: http://www.usna.edu/CS/qepcadweb/B/QEPCAD.html +- Alternative location (sometimes more up-to-date): + + https://www.usna.edu/Users/cs/wcbrown/qepcad/B/QEPCAD.html + +Dependencies +------------ + +- readline +- saclib + + +Special Update/Build Instructions +--------------------------------- + +One might need to set MAKE to "make -j1" fo this to be built +successfully. diff --git a/build/pkgs/qepcad/SPKG.txt b/build/pkgs/qepcad/SPKG.txt deleted file mode 100644 index 33bf57d8d30..00000000000 --- a/build/pkgs/qepcad/SPKG.txt +++ /dev/null @@ -1,39 +0,0 @@ -= qepcad = - -== Description == - -Qepcad is an implementation of quantifier elimination by partial cylindrical -algebraic decomposition - -== License == - -QEPCAD B -Copyright (c) 1990, 2008, Hoon Hong & Chris Brown (contact wcbrown@usna.edu) - -Permission to use, copy, modify, and/or distribute this software, including -source files, README files, etc., for any purpose with or without fee is -hereby granted, provided that the above copyright notice and this permission -notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -== Upstream Contact == - -- Website: http://www.usna.edu/CS/qepcadweb/B/QEPCAD.html -- Alternative location (sometimes more up-to-date): - https://www.usna.edu/Users/cs/wcbrown/qepcad/B/QEPCAD.html - -== Dependencies == - -* readline -* saclib - -== Special Update/Build Instructions == - -One might need to set MAKE to "make -j1" fo this to be built successfully. diff --git a/build/pkgs/qepcad/checksums.ini b/build/pkgs/qepcad/checksums.ini index 080531dee83..5ab616658b0 100644 --- a/build/pkgs/qepcad/checksums.ini +++ b/build/pkgs/qepcad/checksums.ini @@ -1,4 +1,5 @@ tarball=qepcad-VERSION.tar.gz -sha1=085f7063811d160b427d6cd92a3e92aaa580030f -md5=b426b8698ac2e012e2534af9634d743f -cksum=207677138 +sha1=7de9ff3a7ce61e751d91fe5e74079a706174e4fa +md5=61ebb23f407a72cee6142a3b144dea06 +cksum=2428332890 +upstream_url=http://www.usna.edu/Users/cs/wcbrown/qepcad/INSTALL/qepcad-VERSION.tar.gz diff --git a/build/pkgs/qepcad/package-version.txt b/build/pkgs/qepcad/package-version.txt index 1406597b159..b477f38083e 100644 --- a/build/pkgs/qepcad/package-version.txt +++ b/build/pkgs/qepcad/package-version.txt @@ -1 +1 @@ -B.1.71 +B.1.72 diff --git a/build/pkgs/qepcad/patches/0001-Don-t-use-timer_t-on-macOS.patch b/build/pkgs/qepcad/patches/0001-Don-t-use-timer_t-on-macOS.patch new file mode 100644 index 00000000000..a2b81959142 --- /dev/null +++ b/build/pkgs/qepcad/patches/0001-Don-t-use-timer_t-on-macOS.patch @@ -0,0 +1,34 @@ +From cc535b88a2ac93fdd390a7ef8ab6005d7a3cd8a0 Mon Sep 17 00:00:00 2001 +From: Matthias Koeppe +Date: Thu, 22 Aug 2019 22:18:17 -0400 +Subject: [PATCH 1/2] Don't use timer_t on macOS + +--- + source/main/MAIN.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/source/main/MAIN.c b/source/main/MAIN.c +index 723306d..73f8cef 100644 +--- a/source/main/MAIN.c ++++ b/source/main/MAIN.c +@@ -81,6 +81,9 @@ static void init_SIGINT_handler() + + static int sendSignalAfterInterval(int seconds, int signum) + { ++#ifdef __APPLE__ ++ return 1; ++#else + /* Create timer */ + timer_t timerid; + struct sigevent sev; +@@ -100,6 +103,7 @@ static int sendSignalAfterInterval(int seconds, int signum) + return 2; + + return 0; ++#endif + } + + int main(int argc, char **argv) +-- +2.19.0 + diff --git a/build/pkgs/qepcad/patches/0002-WIP-Don-t-add-lrt.patch b/build/pkgs/qepcad/patches/0002-WIP-Don-t-add-lrt.patch new file mode 100644 index 00000000000..54282fbf8f0 --- /dev/null +++ b/build/pkgs/qepcad/patches/0002-WIP-Don-t-add-lrt.patch @@ -0,0 +1,49 @@ +From 9fa8ce43a1ebbc1807528acf7d109abc19ba3ba4 Mon Sep 17 00:00:00 2001 +From: Matthias Koeppe +Date: Thu, 22 Aug 2019 22:39:21 -0400 +Subject: [PATCH 2/2] WIP: Don't add -lrt + +--- + source/Makefile | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/source/Makefile b/source/Makefile +index d184650..f13113b 100644 +--- a/source/Makefile ++++ b/source/Makefile +@@ -30,6 +30,14 @@ SPECIFLAGS = -I/opt/sfw/include + SPECLFLAGS = -lcurses # on solaris you have to link curses to get readline! + endif + ++LIBS = -lreadline ++ ++# On macOS, librt is not available, ++# see https://trac.sagemath.org/ticket/28388 ++#ifeq ($(findstring darwin,${OSTYPE}),) ++#LIBS += -lrt ++#endif ++ + # ============================================================ + # Do not touch below unless you know what you are doing + # ============================================================ +@@ -44,7 +52,7 @@ EXTLIBS = ${qe}/extensions/sfext/sfexto.a \ + ${qe}/extensions/adj2d/adj2do.a \ + ${qe}/extensions/rend/rendo.a \ + ${saclib}/lib/saclibo.a \ +- -lreadline -lrt \ ++ $(LIBS) \ + ${SPECLFLAGS} + + +@@ -54,7 +62,7 @@ EXTLIBSD = ${qe}/extensions/sfext/sfextd.a \ + ${qe}/extensions/adj2d/adj2dd.a \ + ${qe}/extensions/rend/rendd.a \ + ${saclib}/lib/saclibd.a \ +- -lreadline -lrt \ ++ $(LIBS) \ + ${SPECLFLAGS} + + NAME = qepcad +-- +2.19.0 + diff --git a/build/pkgs/qepcad/patches/qepcad-B-destructor.patch b/build/pkgs/qepcad/patches/qepcad-B-destructor.patch new file mode 100644 index 00000000000..2809a0af004 --- /dev/null +++ b/build/pkgs/qepcad/patches/qepcad-B-destructor.patch @@ -0,0 +1,125 @@ +--- a/extensions/rend/Rend_Sample.cc.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/rend/Rend_Sample.cc 2018-08-29 20:07:27.087257552 -0600 +@@ -40,11 +40,6 @@ Rend_Sample_1DS::Rend_Sample_1DS(Word C, + } + + +-Rend_Sample_1DS::~Rend_Sample_1DS() +-{ +- +-} +- + /************************************************************* + ** Refines I to 2^k or less, and returns binary rational + ** middle of interval. +@@ -112,13 +107,6 @@ Rend_Sample_1DO::Rend_Sample_1DO(Rend_Ce + L.W = NIL; + } + +- +-Rend_Sample_1DO::~Rend_Sample_1DO() +-{ +- +-} +- +- + Word Rend_Sample_1DO::coordinate(int k) + { + Word e,j1,j2,J,kp = k; +@@ -178,11 +166,6 @@ Rend_Sample_2DS::Rend_Sample_2DS(Word C) + } + + +-Rend_Sample_2DS::~Rend_Sample_2DS() +-{ +- +-} +- + /************************************************************* + ** Refines I to 2^k or less, and returns binary rational + ** lower endpoint. +@@ -237,11 +220,6 @@ Rend_Sample_2DC::Rend_Sample_2DC(Word C, + + } + +-Rend_Sample_2DC::~Rend_Sample_2DC() +-{ +- +-} +- + Word Rend_Sample_2DC::coordinate(int k) + { + return L.W; +@@ -265,11 +243,6 @@ Rend_Sample_BR::Rend_Sample_BR(Word a) + N.W = a; + } + +-Rend_Sample_BR::~Rend_Sample_BR() +-{ +- +-} +- + Word Rend_Sample_BR::coordinate(int k) + { + return N.W; +--- a/extensions/rend/Rend_Sample.h.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/rend/Rend_Sample.h 2018-08-29 20:07:27.087257552 -0600 +@@ -32,7 +32,7 @@ class Rend_Sample + virtual Word coordinate(int k) = 0; + virtual Word round(int k,int roundup) + { return this -> coordinate(k); } +- // virtual ~Rend_Sample() = 0; ++ virtual ~Rend_Sample() {} + }; + + class Rend_Sample_1DS : public Rend_Sample +@@ -49,7 +49,6 @@ private: + gcmemloc A,I; + public: + Rend_Sample_1DS(Word C, Word P); +- virtual ~Rend_Sample_1DS(); + virtual Word coordinate(int k); + virtual Word round(int k, int roundup); + Word weakcompare(Word R); +@@ -66,7 +65,6 @@ public: + gcmemloc L; + public: + Rend_Sample_1DO(Rend_Cell *dad); +- virtual ~Rend_Sample_1DO(); + virtual Word coordinate(int k); + + }; +@@ -86,7 +84,6 @@ private: + gcmemloc A,I; + public: + Rend_Sample_2DS(Word C); +- virtual ~Rend_Sample_2DS(); + virtual Word coordinate(int k); + + }; +@@ -101,7 +98,6 @@ public: + gcmemloc L; + public: + Rend_Sample_2DC(Word C,Word P); +- virtual ~Rend_Sample_2DC(); + virtual Word coordinate(int k); + void add_point(Word p); + void clear_points(); +@@ -116,7 +112,6 @@ private: + gcmemloc N; + public: + Rend_Sample_BR(Word a); +- virtual ~Rend_Sample_BR(); + virtual Word coordinate(int k); + }; + +--- a/plot2d/plot.cc.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/plot2d/plot.cc 2018-08-29 20:07:27.086257553 -0600 +@@ -37,6 +37,7 @@ class CADELT + public: + virtual bool read(istream &in) = 0; + virtual void glRend(const CADColors &C) = 0; ++ virtual ~CADELT() {} + }; + + class SNoverSR : public CADELT diff --git a/build/pkgs/qepcad/patches/qepcad-B-env.patch b/build/pkgs/qepcad/patches/qepcad-B-env.patch new file mode 100644 index 00000000000..ce32a5e8a87 --- /dev/null +++ b/build/pkgs/qepcad/patches/qepcad-B-env.patch @@ -0,0 +1,12 @@ +--- a/source/main/BEGINQEPCAD.c.orig 2018-01-30 14:04:58.000000000 -0700 ++++ b/source/main/BEGINQEPCAD.c 2018-08-29 20:03:05.718540333 -0600 +@@ -127,7 +127,8 @@ void BEGINQEPCAD(int &argc, char**& argv + void QEPCAD_ProcessRC(int argc, char **argv) + { + char *qepath = getenv("qe"); +- if (qepath == NULL) { FAIL("QEPCAD_ProcessRC","Environment variable qe not defined!"); } ++ if (qepath == NULL) { setenv("qe", "/usr/share/qepcad", 1); qepath = getenv("qe"); } ++ if (getenv("SINGULARPATH") == NULL) { setenv("SINGULARPATH", "@LIBDIR@/Singular", 1); } + string rcFileName = qepath + string("/default.qepcadrc"); + ifstream rcin(rcFileName.c_str()); + if (!rcin) { return; } diff --git a/build/pkgs/qepcad/patches/qepcad-B-gcc6.patch b/build/pkgs/qepcad/patches/qepcad-B-gcc6.patch new file mode 100644 index 00000000000..2caed816ca7 --- /dev/null +++ b/build/pkgs/qepcad/patches/qepcad-B-gcc6.patch @@ -0,0 +1,59 @@ +--- a/plot2d/plot.cc.orig 2018-08-29 20:10:06.396075816 -0600 ++++ b/plot2d/plot.cc 2018-08-29 20:11:48.982924732 -0600 +@@ -276,7 +276,7 @@ bool SNoverSR::read(istream &in) + V.resize(N); + for(int i = 0; i < N; i++) + in >> V[i]; +- return in; ++ return in.good(); + } + + void SNoverSR::glRend(const CADColors &C) +@@ -333,7 +333,7 @@ bool SRoverSR::read(istream &in) + } + } + +- return in; ++ return in.good(); + } + + void SRoverSR::glRend(const CADColors &C) +@@ -351,7 +351,7 @@ bool SRoverSN::read(istream &in) + { + in >> colorType >> a >> b.y; + b.x = a.x; +- return in; ++ return in.good(); + } + + void SRoverSN::glRend(const CADColors &C) +@@ -365,7 +365,8 @@ void SRoverSN::glRend(const CADColors &C + + bool SNoverSN::read(istream &in) + { +- return in >> colorType >> a; ++ in >> colorType >> a; ++ return in.good(); + } + + void SNoverSN::glRend(const CADColors &C) +@@ -380,7 +381,8 @@ void SNoverSN::glRend(const CADColors &C + + bool SN::read(istream &in) + { +- return in >> colorType >> x; ++ in >> colorType >> x; ++ return in.good(); + } + + void SN::glRend(const CADColors &C) +@@ -394,7 +396,8 @@ void SN::glRend(const CADColors &C) + + bool SR::read(istream &in) + { +- return in >> colorType >> x1 >> x2; ++ in >> colorType >> x1 >> x2; ++ return in.good(); + } + + void SR::glRend(const CADColors &C) diff --git a/build/pkgs/qepcad/patches/qepcad-B-parens.patch b/build/pkgs/qepcad/patches/qepcad-B-parens.patch new file mode 100644 index 00000000000..1d881f7467b --- /dev/null +++ b/build/pkgs/qepcad/patches/qepcad-B-parens.patch @@ -0,0 +1,132 @@ +--- a/extensions/adj2d/truthbytop/BOUNDARY2D.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/adj2d/truthbytop/BOUNDARY2D.c 2018-08-29 20:07:50.511232206 -0600 +@@ -106,7 +106,7 @@ Step6: /* Split cell list by dimension. + tc++; + else + fc++; } +- if (tc > 0 && fc > 0 || GVERTEXLABEL(v,G) == TRUE && tc == 0) ++ if ((tc > 0 && fc > 0) || (GVERTEXLABEL(v,G) == TRUE && tc == 0)) + GNEWLABEL(v,TRUE,G); + else + GNEWLABEL(v,FALSE,G); } +--- a/extensions/lift2D/IBPRRIOAP.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/lift2D/IBPRRIOAP.c 2018-08-29 20:07:50.510232207 -0600 +@@ -95,7 +95,7 @@ Step3: /* Isolate the roots of B(alpha,y + goto Return; } + + /* get trend of first root */ +- if (PDEG(B) % 2 == 0 && s == 1 || PDEG(B) % 2 == 1 && s == -1) ++ if ((PDEG(B) % 2 == 0 && s == 1) || (PDEG(B) % 2 == 1 && s == -1)) + t1 = -1; + else + t1 = 1; +--- a/extensions/lift2D/modIBPRRIOAP.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/lift2D/modIBPRRIOAP.c 2018-08-29 20:07:50.510232207 -0600 +@@ -97,7 +97,7 @@ Step3: /* Isolate the roots of B(alpha,y + goto Return; } + + /* get trend of first root */ +- if (PDEG(B) % 2 == 0 && s == 1 || PDEG(B) % 2 == 1 && s == -1) ++ if ((PDEG(B) % 2 == 0 && s == 1) || (PDEG(B) % 2 == 1 && s == -1)) + t1 = -1; + else + t1 = 1; +--- a/source/db/convenientstreams.h.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/db/convenientstreams.h 2018-08-29 20:07:50.511232206 -0600 +@@ -31,7 +31,7 @@ public: + string s = ""; + char c = in.get(); + if (opt == skipleadingws) +- while(c != EOF && (isspace(c) || c == '\\' && isspace(in.peek()))) c = in.get(); ++ while(c != EOF && (isspace(c) || (c == '\\' && isspace(in.peek())))) c = in.get(); + // States : 0 = normal, 1 = in comment, 2 = just read a backslash + int state = 0; + do { +--- a/source/db/SINGULAR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/db/SINGULAR.c 2018-08-29 20:07:50.511232206 -0600 +@@ -58,7 +58,7 @@ void SingularServer::reportStats(ostream + + char peekNonWS(istream &in) + { +- char c; while((c = in.peek()) && c == ' ' || c == '\t' || c == '\n') in.get(); return c; ++ char c; while(c = in.peek() && (c == ' ' || c == '\t' || c == '\n')) in.get(); return c; + } + + +--- a/source/proj/GROUPSAMEPJ.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/proj/GROUPSAMEPJ.c 2018-08-29 20:07:50.511232206 -0600 +@@ -46,7 +46,7 @@ BDigit PRJPNTEQUAL(Word A, Word B) + Word KR = LIST2(SECOND(aK),LIST2(1,1)); + Word sL = AFSIGN(aM,aI,AFPEMV(1,aM,G,KL)); + Word sR = AFSIGN(aM,aI,AFPEMV(1,aM,G,KR)); +- return EQUAL(KL,KR) && sL == 0 || sL == 1 && sR == -1 || sL == -1 && sR == 1; ++ return (EQUAL(KL,KR) && sL == 0) || (sL == 1 && sR == -1) || (sL == -1 && sR == 1); + } + + /* One primitive, the other not */ +@@ -75,10 +75,10 @@ Step1: /* Group. */ + { + ADV(Jt,&J2,&Jt); + Jt2 = LELTI(J2,PO_POLY); +- if (LELTI(J1,PO_TYPE) == PO_POINT && LELTI(J2,PO_TYPE) == PO_POINT +- && PRJPNTEQUAL(Js1,Jt2) || +- LELTI(J1,PO_TYPE) != PO_POINT && LELTI(J2,PO_TYPE) != PO_POINT +- && EQUAL(Js1,Jt2)) ++ if ((LELTI(J1,PO_TYPE) == PO_POINT && LELTI(J2,PO_TYPE) == PO_POINT ++ && PRJPNTEQUAL(Js1,Jt2)) || ++ (LELTI(J1,PO_TYPE) != PO_POINT && LELTI(J2,PO_TYPE) != PO_POINT ++ && EQUAL(Js1,Jt2))) + { + SLELTI(J2,PO_PARENT,CONC(LELTI(J2,PO_PARENT),LELTI(J1,PO_PARENT))); + t = 1; +--- a/source/proj/PROJMCECmod.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/proj/PROJMCECmod.c 2018-08-29 20:07:50.511232206 -0600 +@@ -67,7 +67,7 @@ Step1: /* Obtain coefficients. */ + t = 1; } + + /* If r = 2 OR r-1 is in free variable space, the leading coefficient is always enough! */ +- if (t && (r == 2 || (PCMZERROR && r-1 <= GVNFV)) ++ if ((t && (r == 2 || (PCMZERROR && r-1 <= GVNFV))) + || (experimentalExtensionFlag && qfrCheckNonNullified(r,Ap1,GVNA.W,GVNQFF.W,GVVL.W)) + ) + t = 0; +@@ -101,7 +101,7 @@ Step1: /* Obtain coefficients. */ + tf = tf || (Q == FULLDE || Q == FULLDA); + + /* Test 3: in free variable space when the PCMZERROR option is used */ +- tf = tf || PCMZERROR && rp <= GVNFV; ++ tf = tf || (PCMZERROR && rp <= GVNFV); + + /* Test 4: has no common zero with the system of all other coefficients */ + if (!tf) +--- a/source/proj/PROJMCmod.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/proj/PROJMCmod.c 2018-08-29 20:07:50.511232206 -0600 +@@ -57,7 +57,7 @@ Step1: /* Obtain coefficients. */ + t = 1; } + + /* If r = 2 OR r-1 is in free variable space, the leading coefficient is always enough! */ +- if (t && (r == 2 || (PCMZERROR && r-1 <= GVNFV)) ++ if ((t && (r == 2 || (PCMZERROR && r-1 <= GVNFV))) + || (experimentalExtensionFlag && qfrCheckNonNullified(r,Ap1,GVNA.W,GVNQFF.W,GVVL.W)) + ) + t = 0; +@@ -91,7 +91,7 @@ Step1: /* Obtain coefficients. */ + tf = tf || (Q == FULLDE || Q == FULLDA); + + /* Test 3: in free variable space when the PCMZERROR option is used */ +- tf = tf || PCMZERROR && rp <= GVNFV; ++ tf = tf || (PCMZERROR && rp <= GVNFV); + + /* Test 4: has no common zero with the system of all other coefficients */ + if (!tf) +--- a/source/ticad/INITPCAD.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/INITPCAD.c 2018-08-29 20:07:50.511232206 -0600 +@@ -14,7 +14,7 @@ Word QepcadCls::INITPCAD() + Word D, tv; + + Step0: /* Determine truth value! */ +- if (GVNA == FALSE || GVNA != NIL && LELTI(GVNA,1) == NEOP && LELTI(GVNA,2) == 0) tv = NA; ++ if (GVNA == FALSE || (GVNA != NIL && LELTI(GVNA,1) == NEOP && LELTI(GVNA,2) == 0)) tv = NA; + else if (LELTI(GVNQFF,1) == NEOP && LELTI(GVNQFF,2) == 0) tv = FALSE; + else if (LELTI(GVNQFF,1) == EQOP && LELTI(GVNQFF,2) == 0) tv = TRUE; + else tv = UNDET; diff --git a/build/pkgs/qepcad/patches/qepcad-B-return.patch b/build/pkgs/qepcad/patches/qepcad-B-return.patch new file mode 100644 index 00000000000..04a243a9b72 --- /dev/null +++ b/build/pkgs/qepcad/patches/qepcad-B-return.patch @@ -0,0 +1,56 @@ +--- a/extensions/sfext/formula/FTYPEINFO.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/formula/FTYPEINFO.c 2018-08-29 20:06:48.870298902 -0600 +@@ -23,6 +23,6 @@ Word FTYPEINFO(Word A) + return TRUE; + if (FIRST(A) == FALSE) + return FALSE; +- ++ FAIL("FTYPEINFO","Unknown formula type!"); + } + +--- a/source/db/AFUPSFNDB.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/db/AFUPSFNDB.c 2018-08-29 20:06:48.871298901 -0600 +@@ -5,7 +5,7 @@ AFUPSFN with Database. + ======================================================================*/ + #include "qepcad.h" + +-Word AFUPSFNDB(Word M, Word B, Word *t_, Word *Bt_, Word *F_) ++void AFUPSFNDB(Word M, Word B, Word *t_, Word *Bt_, Word *F_) + { + Word t,Bt,F; + +--- a/source/db/SingSacPolicy.h.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/db/SingSacPolicy.h 2018-08-29 20:06:48.872298900 -0600 +@@ -28,17 +28,17 @@ public: + Word IPRES(Word r, Word A, Word B) + { + if (r > 2) +- sing->IPRES(r,A,B); ++ return sing->IPRES(r,A,B); + else +- sac ->IPRES(r,A,B); ++ return sac ->IPRES(r,A,B); + } + + Word IPDSCR(Word r, Word A) + { + if (r > 2) +- sing->IPDSCR(r,A); ++ return sing->IPDSCR(r,A); + else +- sac ->IPDSCR(r,A); ++ return sac ->IPDSCR(r,A); + } + + Word IPFACTGB(Word r, Word I, Word N) +--- a/source/qepcad.h.orig 2018-08-29 20:04:27.596451754 -0600 ++++ b/source/qepcad.h 2018-08-29 20:06:48.871298901 -0600 +@@ -34,7 +34,7 @@ Word AFPNIPDB(Word Mb, Word B); + void AFUPGCDB(Word M, Word A, Word B, Word *C_, Word *Ab_, Word *Bb_); + void AFUPLM(Word M, Word A, Word *L_, Word *P_); + void AFUPRWR(Word a, Word v, Word A, Word I); +-Word AFUPSFNDB(Word M, Word B, Word *t_, Word *Bt_, Word *F_); ++void AFUPSFNDB(Word M, Word B, Word *t_, Word *Bt_, Word *F_); + Word APPEND(Word P, Word k, Word R); + void APPENDEC(Word P, Word k, Word R, Word *Ps_, Word *F_); + void ATOMFLWR(Word N, Word V, Word A); diff --git a/build/pkgs/qepcad/patches/qepcad-B-signed.patch b/build/pkgs/qepcad/patches/qepcad-B-signed.patch new file mode 100644 index 00000000000..4a424aec3aa --- /dev/null +++ b/build/pkgs/qepcad/patches/qepcad-B-signed.patch @@ -0,0 +1,78 @@ +--- a/plot2d/plot.cc.orig 2018-08-29 20:07:27.086257553 -0600 ++++ b/plot2d/plot.cc 2018-08-29 20:08:10.014211101 -0600 +@@ -134,7 +134,7 @@ void* readdata(void *x) + pthread_mutex_lock(&M); + swap(CE,E); + pthread_mutex_unlock(&M); +- for(int i = 0; i < E.size(); i++) delete E[i]; ++ for(size_t i = 0; i < E.size(); i++) delete E[i]; + E.clear(); } + else if (c == 'E') { /******* Exit! *******************/ + return 0; +@@ -155,7 +155,7 @@ void display() + { + pthread_mutex_lock(&M); + glClear(GL_COLOR_BUFFER_BIT); +- for(int i = 0; i < CE.size(); i++) ++ for(size_t i = 0; i < CE.size(); i++) + CE[i]->glRend(Colors); + pthread_mutex_unlock(&M); + glutSwapBuffers(); +@@ -286,7 +286,7 @@ void SNoverSR::glRend(const CADColors &C + // 128 or so points that can appear. + C.glSetColor(colorType,'D'); + glBegin(GL_LINE_STRIP); +- for(int i = 0; i < V.size(); i++) ++ for(size_t i = 0; i < V.size(); i++) + glVertex2(V[i]); + glEnd(); + } +@@ -328,7 +328,7 @@ bool SRoverSR::read(istream &in) + else + { + cerr << "Sector over sector in unknown format!" << endl; +- for(int i = 0; i < V.size(); i++) ++ for(size_t i = 0; i < V.size(); i++) + cerr << V[i] << endl; + exit(1); + } +--- a/source/db/SINGULAR.c.orig 2018-08-29 20:07:50.511232206 -0600 ++++ b/source/db/SINGULAR.c 2018-08-29 20:08:10.015211100 -0600 +@@ -67,7 +67,7 @@ Word readSingularPoly(Word r, Word V, is + Word A, t; + string s; + in >> s; +- for(int i = 0; i < s.length(); ++i) ++ for(size_t i = 0; i < s.length(); ++i) + if (s[i] == '*') s[i] = ' '; + s += ".\n"; + istringstream si(s); +@@ -108,7 +108,7 @@ string WritePolyForSingular(Word r, Word + out = sout.str(); + } + // Put in * symbols +- for(int i = 1; i < out.length() - 1; ++i) ++ for(size_t i = 1; i < out.length() - 1U; ++i) + if (out[i] == ' ' && out[i+1] != '+' && out[i+1] != '-' + && out[i-1] != '+' && out[i-1] != '-' + ) +--- a/source/saclib/gcword.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/saclib/gcword.c 2018-08-29 20:08:10.015211100 -0600 +@@ -30,7 +30,7 @@ void gcw_MARK(); + } + + static vector G; +-static int lim = 10; ++static size_t lim = 10; + + void clean() + { +@@ -62,7 +62,7 @@ void gcw_MARK() + { + // SWRITE("gcw size is: ");IWRITE(G.size()); SWRITE("\n"); + clean(); +- for(int i = 0; i < G.size(); i++) ++ for(size_t i = 0; i < G.size(); i++) + if (*G[i] > BETA && *G[i] < BETAp && (*G[i] & 1)) + MARK(*G[i]); + diff --git a/build/pkgs/qepcad/patches/qepcad-B-syntax.patch b/build/pkgs/qepcad/patches/qepcad-B-syntax.patch new file mode 100644 index 00000000000..832cdbee4e3 --- /dev/null +++ b/build/pkgs/qepcad/patches/qepcad-B-syntax.patch @@ -0,0 +1,22 @@ +--- a/source/main/BEGINQEPCAD.c.orig 2018-08-29 20:03:05.718540333 -0600 ++++ b/source/main/BEGINQEPCAD.c 2018-08-29 20:09:45.542106837 -0600 +@@ -53,7 +53,7 @@ void BEGINQEPCAD(int &argc, char**& argv + { + int tmp = system("bash -c 'exit $(stty size | cut -d\" \" -f2)'"); + tmp = WEXITSTATUS(tmp); +- if (10 <= tmp <= 512) ++ if (10 <= tmp && tmp <= 512) + cols = tmp; + } + +--- a/source/userint/USERINT.c.orig 2018-03-16 14:22:00.000000000 -0600 ++++ b/source/userint/USERINT.c 2018-08-29 20:09:45.543106835 -0600 +@@ -492,7 +492,7 @@ void VERTFILL2D(Word D) + Word L, S, T, CB; + + /* GET 1D CAD STACK */ +- L = L = LELTI(D,CHILD); ++ L = LELTI(D,CHILD); + if (L == NIL) + { + SWRITE("Must by a 2D CAD!\n"); diff --git a/build/pkgs/qepcad/patches/qepcad-B-tty.patch b/build/pkgs/qepcad/patches/qepcad-B-tty.patch new file mode 100644 index 00000000000..0bda2e973b8 --- /dev/null +++ b/build/pkgs/qepcad/patches/qepcad-B-tty.patch @@ -0,0 +1,45 @@ +--- a/source/db/SINGULAR.c.orig 2018-08-29 20:10:06.406075801 -0600 ++++ b/source/db/SINGULAR.c 2018-08-29 20:11:02.229992773 -0600 +@@ -1,6 +1,7 @@ + #include "SINGULAR.h" + #include + #include ++#include + using namespace std; + + +@@ -15,6 +16,12 @@ SingularServer::SingularServer(string Si + if (childpid == 0) { + intoSingular.setStdinToPipe(); + outofSingular.setStdoutToPipe(); ++ outofSingular.setStderrToPipe(); ++ intoSingular.closeIn(); ++ intoSingular.closeOut(); ++ outofSingular.closeIn(); ++ outofSingular.closeOut(); ++ setsid(); + + // Begin: Just for debug!! + //system("/home/wcbrown/bin/Singular -q --no-warn --min-time=0.001 --ticks-per-sec=1000 | tee /tmp/SingOutLog"); +@@ -30,9 +37,10 @@ SingularServer::SingularServer(string Si + "--ticks-per-sec=1000", + NULL); + perror("SingularServer Constructor: Singular startup failed! (Set SINGULAR environment variable)"); +- outofSingular.closeOut(); + exit(0); + } ++ intoSingular.closeIn(); ++ outofSingular.closeOut(); + } + + SingularServer::~SingularServer() +--- a/source/db/unnamedpipe.h.orig 2018-08-29 20:10:06.406075801 -0600 ++++ b/source/db/unnamedpipe.h 2018-08-29 20:11:02.229992773 -0600 +@@ -113,6 +113,7 @@ public: + int fdout() { return fd[1]; } + int setStdinToPipe() { return dup2(fdin(),fileno(stdin)); } + int setStdoutToPipe() { return dup2(fdout(),fileno(stdout)); } ++ int setStderrToPipe() { return dup2(fdout(),fileno(stderr)); } + void closeIn() { + if (_in) { delete _in; _in = 0; } + if (openmask[0]) { close(fd[0]); openmask[0] = false; } diff --git a/build/pkgs/qepcad/patches/qepcad-B-uninit.patch b/build/pkgs/qepcad/patches/qepcad-B-uninit.patch new file mode 100644 index 00000000000..f78e4c2239a --- /dev/null +++ b/build/pkgs/qepcad/patches/qepcad-B-uninit.patch @@ -0,0 +1,314 @@ +--- a/cad2d/src/TICAD.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/cad2d/src/TICAD.c 2018-08-29 20:05:52.862359504 -0600 +@@ -43,6 +43,8 @@ Step2: /* Choose. */ + if (L == 0) { /* Init for 1D Sectors */ + L = LELTI(D,CHILD); + d = 1; } ++ else ++ d = 0; + + if (d == 1 && L != NIL) { /* Choose next 1D sectors */ + c = FIRST(L); +@@ -51,6 +53,7 @@ Step2: /* Choose. */ + else + L = RED2(L); } + else if (d == 1 && L == NIL) { /* Init for 1D sections */ ++ c = NIL; + d = 0; + L = RED(LELTI(D,CHILD)); + } +--- a/extensions/adj2d/oldadj/ACMADJ2D.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/adj2d/oldadj/ACMADJ2D.c 2018-08-29 20:05:52.864359502 -0600 +@@ -29,6 +29,7 @@ Step1: /* Get (A,I) defining c. */ + FIRST2(Ip,&ip1,&ip2); + i1 = RNLBRN(ip1); + i2 = RNLBRN(ip2); ++ t = 0; + + Step2: /* Get sample points for c_l and c_r. */ + i_l = RNLBRN(SPRLC(c_l)); +--- a/extensions/lift2D/modHIPRRID.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/lift2D/modHIPRRID.c 2018-08-29 20:05:52.864359502 -0600 +@@ -28,6 +28,7 @@ Step1: /* Compute a bound for the positi + k = HIPPRB(n,A); + + Step2: /* Isolate the positive roots. */ ++ L = NIL; + if (k == NIL) { + L1 = NIL; + goto Step3; } +--- a/extensions/lift2D/modIBPRRIOAPSF.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/lift2D/modIBPRRIOAPSF.c 2018-08-29 20:05:52.863359503 -0600 +@@ -77,6 +77,7 @@ Step4: /* Isolate the real roots of eac + Step5: /* Refine roots? */ + if (k == NIL) + goto Return; ++ Js = NIL; + Ls = NIL; + for(Lp = L; Lp != NIL; Lp = RED(Lp)) + { +--- a/extensions/rend/WRITE_PS_INTERACTIVE.cc.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/rend/WRITE_PS_INTERACTIVE.cc 2018-08-29 20:05:52.863359503 -0600 +@@ -262,6 +262,8 @@ void WRITE_PS_INTERACTIVE(Rend_Cell &M, + if (t == 'p') { + SWRITE("Enter projetion factor by (level,index): "); + p = LREAD(); } ++ else ++ p = NIL; + + switch(c) { + case 'x': // 1D Sectors +--- a/extensions/sfext/addpol/MINPFSETNSC.c.orig 2018-08-29 20:03:25.110519357 -0600 ++++ b/extensions/sfext/addpol/MINPFSETNSC.c 2018-08-29 20:05:52.865359501 -0600 +@@ -42,7 +42,7 @@ Word MINPFSETNSC(Word P,Word S,Word D,Wo + Word x_s,js,Ls,O,Q,Q_i,Sp,Pp,i,Cp,*V,*Vp,**A,a,N,k,S_r,I,j,p; + + Step1: /* Initialization. */ +- C = NIL; Sltr = NIL; Pltr = NIL; N = LENGTH(K); ++ C = NIL; Sltr = NIL; S_r = NIL; Pltr = NIL; N = LENGTH(K); + + Step2: /* Loop over each level in D. */ + for(r = 1; r <= N; r++) { +--- a/extensions/sfext/espcad/PCAD2ESPCAD.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/espcad/PCAD2ESPCAD.c 2018-08-29 20:05:52.866359500 -0600 +@@ -44,6 +44,8 @@ Step2: /* Generate correct i-level signi + P_i = RED(P_i); + s = RED(s); } + s = INV(ss); } ++ else ++ s = NIL; + + Step3: /* Construct extended Sub-CAD cell structure. */ + EDs = LIST8(C,A,LELTI(Ds,SC_INX),NIL,s,NIL,UNDET,UNDET); +--- a/extensions/sfext/extlang/SCAD2ESCAD.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/extlang/SCAD2ESCAD.c 2018-08-29 20:05:52.864359502 -0600 +@@ -27,6 +27,8 @@ Step2: /* Generate correct i-level signi + P_i = RED(P_i); + s = RED(s); } + s = INV(ss); } ++ else ++ s = NIL; + + Step3: /* Construct extended Sub-CAD cell structure. */ + EDs = LIST7(C,A,LELTI(Ds,SC_INX),NIL,s,NIL,UNDET); +--- a/extensions/sfext/formula/FMAATOMREAD.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/formula/FMAATOMREAD.c 2018-08-29 20:05:52.865359501 -0600 +@@ -18,7 +18,7 @@ void FMAATOMREAD(Word Q, Word V, Word *F + /* hide r,s,t; */ + + Step1: /* Read the left polynomial. */ +- t = 1; r = LENGTH(V); ++ t = 1; r = LENGTH(V); F = NIL; + IPEXPREAD(r,V,&P1,&t); if (t == 0) goto Return; + + Step2: /* Read the relational operator. */ +@@ -71,6 +71,7 @@ Word POLYINDEX(Word P, Word p, Word r, W + p = SECOND(p); + + /* Is p already in P? */ ++ pp = NIL; + P_r = LELTI(P,r); *t = 0; + for(Pp = P_r; Pp != NIL; Pp = RED(Pp)) { + pp = FIRST(Pp); +--- a/extensions/sfext/formula/FMASMOOTH.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/formula/FMASMOOTH.c 2018-08-29 20:05:52.865359501 -0600 +@@ -16,6 +16,7 @@ Step1: /* Atoms and Constants. */ + if (ISLIST(F1) || F1 == TRUE || F1 == FALSE) { + G = F; + goto Return; } ++ G = NIL; + + Step2: /* AND's */ + if (F1 == ANDOP) { +--- a/extensions/sfext/sfcons/SFC3.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/sfcons/SFC3.c 2018-08-29 20:05:52.865359501 -0600 +@@ -90,7 +90,7 @@ Step3: /* The normal language. */ + switch(m) { + case (0) : SF = NECCONDS(Lt,Lf,LA,Pp); break; + case (1) : SF = NAIVESF(Lt,Lf,LA,Pp); break; +- case (2) : SWRITE("GEOTEST requires the extended language!\n"); goto Return; } } ++ case (2) : SF = NIL; SWRITE("GEOTEST requires the extended language!\n"); goto Return; } } + + Step4: /* Massage the formula. */ + pflag = 1; +--- a/extensions/sfext/sfcons/SFC3f.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/sfcons/SFC3f.c 2018-08-29 20:05:52.865359501 -0600 +@@ -88,7 +88,7 @@ Step3: /* The normal language. */ + switch(m) { + case (0) : SF = NECCONDS(Lt,Lf,LA,Pp); break; + case (1) : SF = NAIVESF(Lt,Lf,LA,Pp); break; +- case (2) : SWRITE("GEOTEST requires the extended language!\n"); goto Return; } } ++ case (2) : SF = NIL; SWRITE("GEOTEST requires the extended language!\n"); goto Return; } } + + Step4: /* Massage the formula. */ + pflag = 1; +--- a/extensions/sfext/sfcons/SFC4.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/sfcons/SFC4.c 2018-08-29 20:05:52.865359501 -0600 +@@ -31,6 +31,7 @@ void QepcadCls::SFC4(Word D, Word P, Wor + { + Word t,SF,Dp,Pp,Lt,Lf,LA,Q,D1,P1,D0,P0,J0,i,Lp,pflag; + char e,s,m,c; ++ e = s = m = c = '\0'; + T1 = T2 = T3 = T4 = 0; + F1 = 0; + +--- a/extensions/sfext/sfcons/SFCFULLDf.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/sfcons/SFCFULLDf.c 2018-08-29 20:05:52.865359501 -0600 +@@ -52,6 +52,7 @@ Step2: /* Extended language. */ + + if (Lt == NIL && Lf == NIL) { + SWRITE("No cells have truth values!\n"); ++ SF = NIL; + goto Return; } + t = ESPCADDOPFSUFF(Pp,LIST1(Dp)); + LA = LISTOETAmod(Pp,n,t==NIL); +--- a/source/io/ATOMFRDR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/ATOMFRDR.c 2018-08-29 20:05:52.866359500 -0600 +@@ -76,7 +76,7 @@ void ETFATOMRDR(Word V, Word P1, BDigit + Word t, F, r, j, P2, P2p, r1, r2, a, P, s; + + Step1: /* Prepare */ +- t = 1; r = LENGTH(V); ++ t = 1; r = LENGTH(V); F = NIL; + + + Step2: /* Read "_root_" */ +--- a/source/io/CATTRNRDR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/CATTRNRDR.c 2018-08-29 20:05:52.866359500 -0600 +@@ -39,7 +39,7 @@ Step2: /* Get the internal representatio + goto Return; + + Step3: /* Error exit. */ +- DIELOC(); t = 0; ++ DIELOC(); V = 0; t = 0; + + Return: /* Prepare for return. */ + *V_ = V; +--- a/source/io/DESIREDRDR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/DESIREDRDR.c 2018-08-29 20:05:52.867359499 -0600 +@@ -15,6 +15,7 @@ void DESIREDRDR(Word *F_, Word *t_) + /* hide C,C1,R,V1,V2,t; */ + + Step1: /* Atomic condition. */ ++ F = NIL; + t = 1; + C = CREADB(); if (C == '[') goto Step2; + BKSP(); +--- a/source/io/FREADR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/FREADR.c 2018-08-29 20:05:52.866359500 -0600 +@@ -23,7 +23,7 @@ void FREADR(Word V, Word f, Word *Fs_, W + /* hide C,i,q,r,t; */ + + Step1: /* Read quantifier list. */ +- t = 1; Q = NIL; r = LENGTH(V); ++ Fs = NIL; t = 1; Q = NIL; r = LENGTH(V); + for (i = f + 1; i <= r; i++) + { + C = CREADB(); +--- a/source/io/GREADR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/GREADR.c 2018-08-29 20:05:52.867359499 -0600 +@@ -33,7 +33,7 @@ Step2: /* Read digits and convert. */ + BKSP(); a = S * a; goto Return; + + Step3: /* Error. */ +- DIELOC(); t = 0; goto Return; ++ DIELOC(); a = 0; t = 0; goto Return; + + Return: /* Prepare for return. */ + *a_ = a; +--- a/source/io/LGOPRDR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/LGOPRDR.c 2018-08-29 20:05:52.866359500 -0600 +@@ -50,7 +50,7 @@ Step1: /* Read in. */ + { SWRITE("Error LGOPRDR: Logic operator was expected.\n"); goto Step2; } + + Step2: /* Error exit. */ +- DIELOC(); t = 0; goto Return; ++ DIELOC(); p = 0; t = 0; goto Return; + + Return: /* Prepare for return. */ + *p_ = p; +--- a/source/io/QFRDR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/QFRDR.c 2018-08-29 20:05:52.867359499 -0600 +@@ -39,7 +39,7 @@ Step1: /* Read in. */ + goto Return; + + Step2: /* Error exit. */ +- DIELOC(); t = 0; goto Return; ++ DIELOC(); q = 0; t = 0; goto Return; + + Return: /* Prepare for return. */ + *q_ = q; +--- a/source/io/RLOPRDR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/RLOPRDR.c 2018-08-29 20:05:52.866359500 -0600 +@@ -31,7 +31,7 @@ Step1: /* Read in. */ + goto Return; + + Step2: /* Error exit. */ +- DIELOC(); t = 0; goto Return; ++ DIELOC(); p = 0; t = 0; goto Return; + + Return: /* Prepare for return. */ + *p_ = p; +--- a/source/main/QEPCADauto.c.orig 2018-03-16 14:22:00.000000000 -0600 ++++ b/source/main/QEPCADauto.c 2018-08-29 20:05:52.867359499 -0600 +@@ -27,6 +27,8 @@ void QepcadCls::QEPCADauto(Word Fs, Word + char c1,c2; /* Chris variables. */ + + Step1: /* Normalize. */ ++ t = 0; ++ F_e = F_n = F_s = NIL; + FIRST4(Fs,&r,&f,&Q,&Fh); + F = NORMQFF(Fh); + if (GVUA != NIL) GVNA = NORMQFF(GVUA); +--- a/source/main/QEPCAD.c.orig 2018-03-16 14:22:00.000000000 -0600 ++++ b/source/main/QEPCAD.c 2018-08-29 20:05:52.867359499 -0600 +@@ -26,6 +26,8 @@ void QepcadCls::QEPCAD(Word Fs, Word *t_ + Word Cs,Ps,Qs,Pps,Cps,Qps,SF; /* Chris variables. */ + char c1,c2; /* Chris variables. */ + Step1: /* Normalize. */ ++ t = 0; ++ F_e = F_n = F_s = NIL; + FIRST4(Fs,&r,&f,&Q,&Fh); + /*Int*/ PCNSTEP = 1; + /*Int*/ if (INTERACT()) USERINT(LFS("Before Normalization"),'a'); +--- a/source/ticad/AFUPMPR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/AFUPMPR.c 2018-08-29 20:05:52.867359499 -0600 +@@ -27,6 +27,7 @@ void AFUPMPR(Word M, Word I, Word B, Wor + /* hide L1,Lp,j,jp,s,t,v,vp; */ + + Step1: /* Initialize. */ ++ j = 0; + FIRST2(J,&a,&b); + t = AFUPSR(M,I,B,b); + if (t == 0) +--- a/source/ticad/SUBST.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/SUBST.c 2018-08-29 20:05:52.867359499 -0600 +@@ -24,6 +24,7 @@ Word QepcadCls::SUBST(Word c, Word k, Wo + Word P,L,Sp,T1,T2,G,Q,f,i; + + Step1: /* Substitute. */ ++ f = UNDET; + L = NIL; + S = NIL; + Bp = B; +--- a/source/ticad/SUBSTR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/SUBSTR.c 2018-08-29 20:05:52.868359497 -0600 +@@ -24,6 +24,7 @@ Word QepcadCls::SUBSTR(Word c, Word k, W + Word P,L,Q,T1,T2,Sp,G,f,i; + + Step1: /* Do it. */ ++ f = UNDET; + L = NIL; + S = NIL; + Bp = B; +--- a/source/userint/PREQNCONSTL.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/userint/PREQNCONSTL.c 2018-08-29 20:05:52.868359497 -0600 +@@ -73,7 +73,7 @@ Return: /* Prepare for return. */ + + Word POLYLABEL(Word P, Word p, Word r, Word *t) + { +- Word P_r, Pp, pp; ++ Word P_r, Pp, pp = NIL; + + for(; PDEG(p) == 0; r--) + p = SECOND(p); diff --git a/build/pkgs/qepcad/patches/qepcad-B-unused.patch b/build/pkgs/qepcad/patches/qepcad-B-unused.patch new file mode 100644 index 00000000000..b14ade83ae3 --- /dev/null +++ b/build/pkgs/qepcad/patches/qepcad-B-unused.patch @@ -0,0 +1,1739 @@ +--- a/cad2d/src/CAD2D.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/cad2d/src/CAD2D.c 2018-08-29 20:10:06.394075819 -0600 +@@ -20,8 +20,8 @@ + + void QepcadCls2D::CAD2D(Word Fs, Word *t_, Word *F_e_, Word *F_n_, Word *F_s_) + { +- Word A,D,F,F_e,F_n,F_s,Fh,J,P,Q,Ths,f,i,r,t; +- Word L; ++ Word A,D,F,F_e,F_n,F_s,Fh,J,P,Q,f,i,r,t; ++ + Step1: /* Normalize. */ + FIRST4(Fs,&r,&f,&Q,&Fh); + F = NORMQFF(Fh); +@@ -70,9 +70,7 @@ Word AllAdjs(QepcadCls2D &Q, Word D,Word + + Word printgraph(Word D, Word L) + { +- Word A,R,c,Q,a,b; +- FILE* fp; +- fp = fopen("temp","w"); ++ Word R,c,Q,a,b; + + SWRITE("graph: {\n"); + +--- a/cad2d/src/CONSTRUCT.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/cad2d/src/CONSTRUCT.c 2018-08-29 20:10:06.395075818 -0600 +@@ -19,31 +19,10 @@ Construct a stack. + ======================================================================*/ + #include "cad2d.h" + +-static Word Chrisolate(Word M, BDigit p) +-{ +- Word n,q1,q2,S,L; +- BDigit *Mp; +- +-Step1: /* Convert the minimal polynomial to a software interval +- polynomial. */ +- n = PDEG(M); +- q1 = p + 3; +- q2 = q1 + q1; +- S = (n + 1) * q2 + 1; +- Mp = GETARRAY(S); +- IPSIP(M,p,Mp); +- +-Step2: /* Isolate roots! */ +- L = SIPRRID(Mp); +- FREEARRAY(Mp); +- return L; +-} +- + void QepcadCls2D::CONSTRUCT(Word c,Word k,Word f,Word Ps_,Word As) + { + BDigit p,t,Ths; +- Word A1,As1,At,B,b,E,I,Ip,I1,J,Jp,L,M,P1,Ps1,Pt,Pt1,S,s,T,Q; +- Word junk,a1,b1,t1,p1,j1; ++ Word A1,As1,At,B,b,E,I,Ip,I1,J,Jp,L,M,P1,Ps1,Pt,S,s,T,Q; + + Step1: /* Extract the projection factors from their attribute lists. */ + Word Ps = Ps_; +--- a/cad2d/src/PROJECT.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/cad2d/src/PROJECT.c 2018-08-29 20:10:06.395075818 -0600 +@@ -17,7 +17,7 @@ Projection Phase. + + void QepcadCls2D::PROJECT(Word r, Word A,Word *P_, Word *J_) + { +- Word D,F,J,P,Ps,J_k1,P_k,R,Ths,Thss,k,i; ++ Word D,F,J,P,J_k1,P_k,R,Ths,Thss,k,i; + + Step1: /* Initialize. */ + P = LLCOPY(A); +--- a/cad2d/src/TICAD.c.orig 2018-08-29 20:05:52.862359504 -0600 ++++ b/cad2d/src/TICAD.c 2018-08-29 20:10:06.394075819 -0600 +@@ -24,7 +24,7 @@ Output + + Word QepcadCls2D::TICAD(Word Q,Word F,Word f,Word P,Word A) + { +- Word As,D,Ps,Ths,Thss,c,cp,k,s,sh,sp,t,R,S; ++ Word As,D,Ps,c,k,s,sh; + Word L,d; + + Step1: /* Initialize. */ +--- a/extensions/adj2d/ADJ_2D1.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/adj2d/ADJ_2D1.c 2018-08-29 20:10:06.400075810 -0600 +@@ -16,6 +16,7 @@ Outputs + #include "adj2D.h" + #include "adj2D.h" + /**************************************/ ++/* + static FILE *OUTPUT; + + static void init() +@@ -30,6 +31,7 @@ static void uninit() + { + fclose(OUTPUT); + } ++*/ + /**************************************/ + + +--- a/extensions/adj2d/ADJ_2D1P2.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/adj2d/ADJ_2D1P2.c 2018-08-29 20:10:06.401075809 -0600 +@@ -9,7 +9,7 @@ void sa_send(const char* S); + + Word ADJ_2D1P2(Word U, Word V, Word w_l, Word B) + { +- Word Sol,S,v,J,w_v,u,w_u,I,Solp,t,f; ++ Word Sol,S,v,J,w_v,u,w_u,I,Solp,f; + + /*sa_send("[");*/ + +--- a/extensions/adj2d/ADJ_2D.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/adj2d/ADJ_2D.c 2018-08-29 20:10:06.400075810 -0600 +@@ -17,12 +17,6 @@ Outputs + ======================================================================*/ + #include "adj2D.h" + #include "adj2D.h" +-static void start(); +-static void stop(); +-static int k; +-static void zero() { k = 0; } +-void bump() { k++; } +-static void print() { SWRITE("\n\nk = "); IWRITE(k); SWRITE("\n\n"); } + + Word ADJ_2D(Word c, Word c_l, Word c_r, Word P, Word J) + { +@@ -37,18 +31,3 @@ Word ADJ_2D(Word c, Word c_l, Word c_r, + + return Sol; + } +- +-static Word ADJ_D_Time; +- +-void start() +-{ +- ADJ_D_Time = ACLOCK(); +-} +- +-void stop() +-{ +- ADJ_D_Time = ACLOCK() - ADJ_D_Time; +- SWRITE("\nADJ_2D took "); +- IWRITE(ADJ_D_Time); +- SWRITE(" millseconds.\n"); +-} +--- a/extensions/adj2d/ADJ_2D_PART.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/adj2d/ADJ_2D_PART.c 2018-08-29 20:10:06.401075809 -0600 +@@ -80,8 +80,3 @@ Step5: /* Clean up evidence of "shadow s + Return: /* Prepare to return. */ + return Sol; + } +- +-static void dummy() +-{ +- +-} +--- a/extensions/adj2d/oldadj/ACMADJ2D.c.orig 2018-08-29 20:05:52.864359502 -0600 ++++ b/extensions/adj2d/oldadj/ACMADJ2D.c 2018-08-29 20:10:06.400075810 -0600 +@@ -239,7 +239,7 @@ Step3: /* Make assignments. */ + + Word ASYS2(Word M, Word H, Word I, Word P2) + { +- Word P,p,tH,tI,h1,h2,i1,i2,L1p,L2p,L1n,L2n,n1p,n1n,n2p,n2n,p1,p2,L1,L2,t; ++ Word P,p,tH,tI,h1,h2,i1,i2; + + tH = -LBRNSIGN(IUPLBREVAL(M,FIRST(H))); + tI = LBRNSIGN(IUPLBREVAL(M,SECOND(I))); +--- a/extensions/adj2d/P1.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/adj2d/P1.c 2018-08-29 20:10:06.401075809 -0600 +@@ -25,7 +25,7 @@ Notes: P1 has three main cases to deal w + + Word P1(Word U, Word V, Word W, Word v_l, Word B) + { +- Word Sol,As,S,u,I,J,a,w,v,n,x,x_u,x_w,N,m_v,m_2v; ++ Word Sol,As,S,u,I,J,a,w,v,n,x,x_u,x_w,m_v,m_2v; + + Sol = NIL; As = NIL; + +--- a/extensions/adj2d/P3.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/adj2d/P3.c 2018-08-29 20:10:06.401075809 -0600 +@@ -29,7 +29,7 @@ Note: Program 3 is the first called, an + + Word P3(Word U, Word V, Word W, Word v_l, Word B) + { +- Word I,J,Sol,S,u,As; ++ Word J,Sol,S,u,As; + + Step1: /* Special Case: No adjacencies to determine. */ + if (U == NIL && W == NIL) +--- a/extensions/adj2d/P4.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/adj2d/P4.c 2018-08-29 20:10:06.400075810 -0600 +@@ -25,7 +25,7 @@ Notes: Program 4 tries to find solutions + + Word P4(Word U, Word V, Word W, Word v_l, Word B) + { +- Word I,Sol,S,w,As,J; ++ Word Sol,S,w,As,J; + + Step1: /* Loop. */ + J = LIST2(B,AD2D_N_In); Sol = NIL; As = NIL; +--- a/extensions/adj2d/sac_ext/IUPTSII.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/adj2d/sac_ext/IUPTSII.c 2018-08-29 20:10:06.401075809 -0600 +@@ -14,7 +14,7 @@ Outputs + + Word IUPTSII(Word A, Word I) + { +- Word i1,i2,Ap,t,a; ++ Word i1,i2,Ap,t; + + Step1: /* One-point interval. */ + FIRST2(I,&i1,&i2); +--- a/extensions/adj2d/truthbytop/ADJ2DITOEL.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/adj2d/truthbytop/ADJ2DITOEL.c 2018-08-29 20:10:06.400075810 -0600 +@@ -27,7 +27,7 @@ int bc(int a, int b) + + Word ADJ2DITOEL(Word L, Word c1, Word c0) + { +- Word *A, E,n,l,s,a,b,k1,k0,i,itop,ibot,S; ++ Word *A, E,n,l,a,b,k1,k0,i,itop,ibot,S; + + Step1: /* Initialize & Special Case. */ + E = NIL; +--- a/extensions/adj2d/truthbytop/GRAPHMODULE.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/adj2d/truthbytop/GRAPHMODULE.c 2018-08-29 20:10:06.400075810 -0600 +@@ -61,9 +61,9 @@ Return: /* Prepare to return. */ + + Word GSTACKHANDLE(Word i, Word G_) + { +- Word G,Gp,h; +- G = G_; +- for(Gp = NIL; G != NIL && (i > FIRST(FIRST(G))); G = RED(G)); ++ Word G,h; ++ ++ for(G = G_; G != NIL && (i > FIRST(FIRST(G))); G = RED(G)); + if (G == NIL || FIRST(FIRST(G)) != i) + h = NIL; + else +@@ -74,7 +74,7 @@ Word GSTACKHANDLE(Word i, Word G_) + + Word GVERTEXHANDLE(Word v, Word G_) + { +- Word G,i,j,Gp,T,h; ++ Word i,j,T,h; + + FIRST2(v,&i,&j); + h = GSTACKHANDLE(i,G_); +--- a/extensions/lift2D/IBPRRIOAPSF.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/lift2D/IBPRRIOAPSF.c 2018-08-29 20:10:06.399075811 -0600 +@@ -30,8 +30,8 @@ Outputs + + void IBPRRIOAPSF(Word M, Word I, Word B, BDigit p,BDigit k, Word *J_, Word *L_) + { +- BDigit *Mp,*bp,*c,i,m,n,q1,q2,S,s,t; +- Word b,Bp,I1,I2,J,K,L,Ls,Lp,T,Jp; ++ BDigit *Mp,*bp,*c,m,n,q1,q2,S,s,t; ++ Word b,J,L,Ls,Lp,Jp; + + Step1: /* Convert the minimal polynomial to a software interval + polynomial. */ +--- a/extensions/lift2D/LIFTSRD2D.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/lift2D/LIFTSRD2D.c 2018-08-29 20:10:06.399075811 -0600 +@@ -14,7 +14,7 @@ + + Word LIFTSRD2D(Word c, Word D, Word P, Word L) + { +- Word S_L,S_R,s_L,s_R,S,i,c_L,c_R,cp,flag,m_L,m_R,so,mo,m,s,j; ++ Word S_L,S_R,s_R,S,i,c_R,cp,so,m,s,j; + Word M,I,P2,Rp,t,R,Rs,Rt,SP,r,k,c1,c2,prev,Sp,sor,next,s2,nextc,X; + Word DL,Rps,pf,a,b,e,temp,count,J; + Word R_L; +@@ -113,7 +113,6 @@ if (PCVERBOSE) { SWRITE("Tried up to pre + /* Go through the neighboring stacks! */ + /**************************************/ + i = 0; +- flag = FALSE; + + /* LIMITATION OF CURRENT IMPLEMENTATION + make sure the larger stack is to the right */ +@@ -127,7 +126,6 @@ if (PCVERBOSE) { SWRITE("Tried up to pre + + for(S = NIL; S_R != NIL; ) { /******* BIG LOOP!!!! **********/ + +- c_L = FIRST(S_L); + c_R = FIRST(S_R); + + if (LELTI(c_R,MULSUB) != NIL && FIRST(FIRST(LELTI(c_R,MULSUB))) == J) +@@ -153,7 +151,6 @@ if (PCVERBOSE) { SWRITE("Tried up to pre + NOTDET, + LELTI(c_R,DEGSUB),LELTI(c_R,MULSUB)); + S = COMP(cp,S); +- flag = FALSE; + if (RED(S_L) == NIL && RED(S_R) != NIL) + S_R = RED(S_R); + else { +--- a/extensions/lift2D/LIFTSRR2D.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/lift2D/LIFTSRR2D.c 2018-08-29 20:10:06.400075810 -0600 +@@ -38,7 +38,7 @@ void debWRITEINTERVAL(Word I) { + + Word LIFTSRR2D(Word c, Word D, Word P) + { +- Word S_L,S_R,s_L,s_R,S,i,c_L,c_R,cp,flag,m_L,m_R,so,mo,m,s,j; ++ Word S_L,S_R,s_L,s_R,S,i,c_L,c_R,cp,so,m,s; + Word M,I,P2,Rp,t,R,Rs,Rt,SP,r,k,c1,c2,prev,Sp,sor,next,s2,nextc,X; + Word OC, OT,count; + +@@ -108,7 +108,6 @@ Word LIFTSRR2D(Word c, Word D, Word P) + /* Go through the neighboring stacks! */ + /**************************************/ + i = 0; +- flag = FALSE; + for(S = NIL; S_L != NIL; S_L = RED(S_L), S_R = RED(S_R)) { + + c_L = FIRST(S_L); +@@ -133,8 +132,7 @@ Word LIFTSRR2D(Word c, Word D, Word P) + CCONC(LELTI(c,INDX),LIST1(i)),COMP(FIRST(LELTI(c_L,SIGNPF)),LELTI(c,SIGNPF)), + NOTDET, + LELTI(c_L,DEGSUB),LELTI(c_L,MULSUB)); +- S = COMP(cp,S); +- flag = FALSE; } ++ S = COMP(cp,S); } + + else { + /************************************************************ +--- a/extensions/lift2D/modHIPRRID.c.orig 2018-08-29 20:05:52.864359502 -0600 ++++ b/extensions/lift2D/modHIPRRID.c 2018-08-29 20:10:06.400075810 -0600 +@@ -22,7 +22,7 @@ void modHIPRRID(BDigit n, interval *A, W + { + BDigit k,s,t; + interval *B,*C; +- Word b,I,J,L,L1,L2; ++ Word I,L,L1,L2; + + Step1: /* Compute a bound for the positive roots. */ + k = HIPPRB(n,A); +--- a/extensions/lift2D/modHIPRRISD.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/lift2D/modHIPRRISD.c 2018-08-29 20:10:06.399075811 -0600 +@@ -23,7 +23,7 @@ Outputs + + Word modHIPRRISD(BDigit n, interval *A, Word a, Word b) + { +- BDigit s,s2,t,u,v,Th,T0,sh; ++ BDigit s,s2,t,v,Th,T0,sh; + Word c,L,L1,L2; + interval *B,*C,J; + +--- a/extensions/lift2D/modIBPRRIOAPSF.c.orig 2018-08-29 20:05:52.863359503 -0600 ++++ b/extensions/lift2D/modIBPRRIOAPSF.c 2018-08-29 20:10:06.399075811 -0600 +@@ -31,8 +31,8 @@ Outputs + + void modIBPRRIOAPSF(Word M, Word I, Word B, Word p, Word k, BDigit *J_, BDigit *L_) + { +- BDigit *Mp,*bp,*c,i,m,n,q1,q2,S,s,t; +- Word a,b,Bp,I1,I2,J,K,L,Ls,Lp,T,Jp,Js,Ld; ++ BDigit *Mp,*bp,*c,m,n,q1,q2,S,s,t; ++ Word a,b,J,L,Ls,Lp,Jp,Js,Ld; + + Step1: /* Convert the minimal polynomial to a software interval + polynomial. */ +--- a/extensions/newadj/HAP2.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/newadj/HAP2.c 2018-08-29 20:10:06.406075801 -0600 +@@ -18,7 +18,7 @@ Outputs + + Word HAP2(Word U, Word V, Word w_l, Word B) + { +- Word Sol,S,v,J,w_v,u,w_u,I,Solp,t,f; ++ Word Sol,S,v,J,w_v,u,w_u,I,Solp,f; + + Sol = NIL; + S = NIL; +--- a/extensions/newadj/HATEST.c.orig 2018-08-29 20:03:25.111519356 -0600 ++++ b/extensions/newadj/HATEST.c 2018-08-29 20:10:06.406075801 -0600 +@@ -111,13 +111,11 @@ Sample point coordinate write. + + void SAMPLECWR(Word c) + { +- Word I,Ip,M,Mp,b,bp,k,s,F,j,Ms,Is; ++ Word I,Ip,M,Mp,b,bp,s,F,j,Ms,Is; + Word M1; +- /* hide k; */ + + Step1: /* Setup. */ + s = LELTI(c,SAMPLE); +- k = LELTI(c,LEVEL); + + Step2: /* Extended representation. */ + if (ISPRIMIT(s)) goto Step3; +@@ -160,7 +158,7 @@ Algebraic number field elements list wri + + void strippedAFLWR(Word M,Word I,Word N,Word a,Word A) + { +- Word A1,Ap,i,l,L,j; ++ Word A1,Ap,i; + /* hide i; */ + + Step1: /* Write. */ +@@ -196,7 +194,7 @@ Side effects + + void ANDWRITExx(Word M, Word I, Word n) + { +- Word J,R,Mp,K; ++ Word J,R,K; + Word a,a1,a2,b,b1,b2,d,d1,d2,e,f,m; + Word N; + +--- a/extensions/rend/CH_VIEW_WIN.cc.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/rend/CH_VIEW_WIN.cc 2018-08-29 20:10:06.397075815 -0600 +@@ -4,7 +4,7 @@ + Word CH_VIEW_WIN(Rend_Cell &M, Rend_Win &W, Word Xs, Word Xt, + Word Ys,Word Yt, Word e, Word P) + { +- Word mx,dx,dxp,dxp2,my,dy,d2x,d2y,L,i; ++ Word mx,my,d2x,d2y,L,i; + + mx = LBRNP2PROD( LBRNSUM(W.x.W,W.X.W) , -1 ); + my = LBRNP2PROD( LBRNSUM(W.y.W,W.Y.W) , -1 ); +--- a/extensions/rend/PLOT_2D_CAD.cc.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/rend/PLOT_2D_CAD.cc 2018-08-29 20:10:06.397075815 -0600 +@@ -42,9 +42,8 @@ void PLOT_2D_CAD(Word D, Word P, Word J, + { + char c; + Word N; +- Word i,j,I,ap,bp,a,b,L,p_n,n,A,Q,x,yl,Xs,Ys,Xt,Yt; +- char FIFO_NAME[20] = "/tmp/_plot_pipe_", +- RM_FIFO[25] = "rm "; ++ Word L,Xs,Ys,Xt,Yt; ++ char FIFO_NAME[20] = "/tmp/_plot_pipe_"; + longtostring(getpid(),FIFO_NAME+11); + int t = 1, first = 1; + int wcount = 0; +--- a/extensions/rend/Rend_Cell.cc.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/rend/Rend_Cell.cc 2018-08-29 20:10:06.398075813 -0600 +@@ -183,7 +183,7 @@ Word Rend_Cell::description(Rend_Win &W) + *************************************************************/ + void Rend_Cell::out_descrip(Rend_Win &W,ostream &out,Mapper &M) + { +- Word p,x,y,L,Lp,l,l1,l2,l3,x1,x2,y1,y2; ++ Word x,y,L,l,x1,x2,y1,y2; + + int px = 8; + int py = 8; +@@ -310,7 +310,7 @@ void Rend_Cell::out_descrip(Rend_Win &W, + *************************************************************/ + void Rend_Cell::out_descrip_ps(Rend_Win &W,ostream &out, Mapper &M) + { +- Word p,x,y,L,l,x1,x2,y1,y2; ++ Word p,L; + p = 12; + switch (level) { + case 0 : +@@ -382,7 +382,7 @@ void Rend_Cell::out_descrip_ps(Rend_Win + *************************************************************/ + void Rend_Cell::out_descrip_ps_color(Rend_Win &W,ostream &out, Mapper &M) + { +- Word p,x,y,L,l,x1,x2,y1,y2; ++ Word p,L; + p = 12; + switch (level) { + case 0 : +@@ -475,7 +475,7 @@ void Rend_Cell::out_descrip_ps_color(Ren + *************************************************************/ + void Rend_Cell::out_descrip_ps_raji(Rend_Win &W,ostream &out, Mapper &M) + { +- Word p,x,y,L,l,x1,x2,y1,y2; ++ Word p,L; + p = 12; + switch (level) { + case 0 : +@@ -511,7 +511,7 @@ void Rend_Cell::out_descrip_ps_raji(Rend + *************************************************************/ + void Rend_Cell::out_descrip_ps_standard(Rend_Win &W,ostream &out, Mapper &M) + { +- Word p,x,y,L,l,x1,x2,y1,y2; ++ Word p,L; + p = 12; + switch (level) { + case 0 : +--- a/extensions/rend/Rend_Sample.cc.orig 2018-08-29 20:07:27.087257552 -0600 ++++ b/extensions/rend/Rend_Sample.cc 2018-08-29 20:10:06.396075816 -0600 +@@ -46,7 +46,7 @@ Rend_Sample_1DS::Rend_Sample_1DS(Word C, + *************************************************************/ + Word Rend_Sample_1DS::coordinate(int k) + { +- Word P,J,j1,j2,js1,js2,s1,s2; ++ Word P,J,j1,j2; + + P = A.W; J = I.W; + +@@ -64,7 +64,7 @@ Step2: /* Save interval refinement and r + + Word Rend_Sample_1DS::round(int k, int roundup) + { +- Word P,J,j1,j2,js1,js2,s1,s2; ++ Word P,J,j1,j2; + + P = A.W; J = I.W; + +@@ -109,7 +109,7 @@ Rend_Sample_1DO::Rend_Sample_1DO(Rend_Ce + + Word Rend_Sample_1DO::coordinate(int k) + { +- Word e,j1,j2,J,kp = k; ++ Word /* e, */ j1,j2,J,kp = k; + do { + j1 = l -> sample -> round(kp,1); + j2 = r -> sample -> round(kp,0); +@@ -132,8 +132,8 @@ Word Rend_Sample_1DO::coordinate(int k) + *************************************************************/ + Rend_Sample_2DS::Rend_Sample_2DS(Word C) + { +- Word T,i,t1,t2,t3,j1,j2,s1,s2,s; +- Word tB,tJ,tA,tI,d1,d2,d3,d4,d5; ++ Word T,t1,t2,t3,j1,j2,s1,s2; ++ Word tB,tJ,tA,tI,d1; + + //-- Set A to the minpol of sample point, and I to isolating int --/ + T = LELTI( C , SAMPLE ); +@@ -172,7 +172,7 @@ Rend_Sample_2DS::Rend_Sample_2DS(Word C) + *************************************************************/ + Word Rend_Sample_2DS::coordinate(int k) + { +- Word J,j1,j2,JL,S,tB,tJ,tA,tI,d1,i; ++ Word J,S,tB,tJ,tA,tI,d1,i; + + + Step1: /* Initialize and decide if refinement is even necessary. */ +--- a/extensions/rend/WRITE_PS_INTERACTIVE.cc.orig 2018-08-29 20:05:52.863359503 -0600 ++++ b/extensions/rend/WRITE_PS_INTERACTIVE.cc 2018-08-29 20:10:06.399075811 -0600 +@@ -190,8 +190,8 @@ void WRITE_PS_INTERACTIVE(Rend_Cell &M, + **********************************************************/ + case 'a': + { +- Word OX, OY,xoff,yoff; +- double dum,Ox = 0, Oy = 0; ++ Word OX, OY; ++ double Ox = 0, Oy = 0; + SWRITE("Provide an origin: "); + qein() >> Ox >> Oy; + OX = IEEELBRN(Ox); +--- a/extensions/sfext/addpol/BPOLSETS.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/addpol/BPOLSETS.c 2018-08-29 20:10:06.405075803 -0600 +@@ -24,7 +24,7 @@ Outputs + + void BPOLSETS(Word L_, Word D, Word P, Word *T_, Word *N_) + { +- Word L,T,Q,q,a,b,I_a,I_b,i_a,i_b,n,P_N,S_a,S_b,s_a,s_b,t,N,p, ++ Word L,T,Q,q,a,b,I_a,I_b,i_a,i_b,n,P_N,t,N, + S_T,S_L,s_c,c,i,Tb,Tp; + + Step1: /* Initialization. */ +--- a/extensions/sfext/addpol/CFLCELLLIST.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/addpol/CFLCELLLIST.c 2018-08-29 20:10:06.405075803 -0600 +@@ -21,8 +21,7 @@ static Word comp(Word A, Word B); + + Word CFLCELLLIST(Word L_D) + { +- Word C,r,C_r,L,Lp,T,F,U,c,t,f,u,Fp,Up,h,Cb,Cp,Q; +- /* Time */ Word tm; ++ Word C,C_r,L,Lp,T,F,U,c,t,f,u,Fp,Up,h,Cb,Cp,Q; + + Step1: /* Initialize. */ + C = NIL; +--- a/extensions/sfext/addpol/CLEAN_BIGLOOP.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/addpol/CLEAN_BIGLOOP.c 2018-08-29 20:10:06.405075803 -0600 +@@ -15,8 +15,8 @@ Inputs + + void QepcadCls::CLEAN_BIGLOOP(Word Jb, Word Pb, Word P0, Word D0, Word N, Word *P_, Word *D_) + { +- Word P,D,G,C,T,N_T,Tb,Gb,Gbp,S_N_T,Tp,Gp,K,KT,p,i,S; +- Word Q,Q_i,Ps,Ds,Qb,Qb_i,Dsp,t,Ph; ++ Word P,D,G,C,T,N_T,Gb,K,KT,p,i,S; ++ Word Q,Ps,Ds,Dsp,t,Ph; + Word inum,tm,temp; + + Step1: /* Initialize. */ +--- a/extensions/sfext/addpol/KCONST.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/addpol/KCONST.c 2018-08-29 20:10:06.405075803 -0600 +@@ -53,8 +53,7 @@ static Word S1_EMPTY(Word S1) + + static Word S2_IN(Word o, Word S2) + { +- Word S; +- for(S = S2; S2 != NIL; S2 = RED(S2)) { ++ for( ; S2 != NIL; S2 = RED(S2)) { + if ( EQUAL(FIRST(FIRST(S2)),o) ) + return (1); } + return (0); +@@ -79,30 +78,6 @@ static Word S2_EMPTY(Word S2) + return ( (S2 == NIL) ); + } + +-/*--------- Is p a factor of q? ----------------*/ +-static Word ISPFACQ(Word p, Word q) +-{ +- Word l_p,R,r; +- l_p = LELTI(p,PO_LABEL); +- for(R = LELTI(q,PO_PARENT); R != NIL; R = RED(R)) { +- r = FIRST(R); +- if ( (FIRST(r) == PO_FAC) && +- EQUAL(LELTI(THIRD(r),PO_LABEL),l_p) ) +- return (1); } +- return (0); +-} +-/*--------- Is p the derivative of q? ----------------*/ +-static Word ISPDERQ(Word p, Word q) +-{ +- Word l_p,R,r; +- l_p = LELTI(p,PO_LABEL); +- for(R = LELTI(q,PO_PARENT); R != NIL; R = RED(R)) { +- r = FIRST(R); +- if ( (FIRST(r) == PO_DER) && +- EQUAL(LELTI(THIRD(r),PO_LABEL),l_p) ) +- return (1); } +- return (0); +-} + /*--------- List of derivative factor indices. --------*/ + + static Word LIST_OF_DI(Word o, Word J, Word P) +@@ -132,7 +107,7 @@ return (L); + + void KCONST(Word J, Word P, Word G, Word *K_, Word *KT_) + { +- Word S1,S2,Gp,o,L,l,KT,K,i,j; ++ Word S1,S2,Gp,o,L,l,KT,K; + + /* Initialization. */ + S1 = NIL; S2 = NIL; +--- a/extensions/sfext/addpol/MINPFSETNSC.c.orig 2018-08-29 20:05:52.865359501 -0600 ++++ b/extensions/sfext/addpol/MINPFSETNSC.c 2018-08-29 20:10:06.405075803 -0600 +@@ -38,8 +38,8 @@ static Word comp1(Word a,Word b) { + + Word MINPFSETNSC(Word P,Word S,Word D,Word K) + { +- Word C,Sltr,Pltr,r,L_r,Ls_r,L,l,l_t,l_s,ls,Kp,Js,x,Jsp,s_k,sk; +- Word x_s,js,Ls,O,Q,Q_i,Sp,Pp,i,Cp,*V,*Vp,**A,a,N,k,S_r,I,j,p; ++ Word C,Sltr,Pltr,r,L_r,Ls_r,l,l_t,l_s,ls,Kp,Js,Jsp,s_k,sk; ++ Word Ls,O,Q,Q_i,Sp,i,Cp,*V,*Vp,**A,a,N,k,S_r,j,p; + + Step1: /* Initialization. */ + C = NIL; Sltr = NIL; S_r = NIL; Pltr = NIL; N = LENGTH(K); +--- a/extensions/sfext/addpol/STRIPPED_BIGLOOP.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/addpol/STRIPPED_BIGLOOP.c 2018-08-29 20:10:06.405075803 -0600 +@@ -15,8 +15,8 @@ Inputs + + void QepcadCls::STRIPPED_BIGLOOP(Word Jb, Word Pb, Word P0, Word D0, Word N, Word *P_, Word *D_) + { +- Word P,D,G,C,T,N_T,Tb,Gb,Gbp,S_N_T,Tp,Gp,K,KT,p,i,S; +- Word Q,Q_i,Ps,Ds,Qb,Qb_i,Dsp,t,Ph; ++ Word P,D,G,C,T,N_T,Gb,K,KT,p,i; ++ Word Ps,t; + Word inum,tm,tt; + + Step1: /* Initialize. */ +--- a/extensions/sfext/crcads/CSORCELL.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/crcads/CSORCELL.c 2018-08-29 20:10:06.405075803 -0600 +@@ -19,7 +19,7 @@ Side Effects + + void QepcadCls::CSORCELLTR(Word c, Word Pp, Word PpO, Word PpN) + { +- Word f,s,sh,M,K,C,Pps,L,T,B,E,I,A,a,b,k; ++ Word s,sh,M,K,C,Pps,L,T,B,E,I,A,a,b,k; + + k = LELTI(c,LEVEL); + +--- a/extensions/sfext/crcads/CSORCELL_MOD.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/crcads/CSORCELL_MOD.c 2018-08-29 20:10:06.406075801 -0600 +@@ -25,8 +25,8 @@ static Word LLPFZC(Word c,Word P); + + void QepcadCls::CSORCELLTR_MOD(Word c, Word Pp, Word PpO, Word PpN, Word P) + { +- Word f,s,sh,M,K,C,Pps,L,T,B,E,I,A,a,b,k; +- Word PP,NP,L_P,TP,i,ta,t; ++ Word s,sh,T,B,A,a,b,k; ++ Word PP,NP,L_P,TP,i,ta; + + Step0: + k = LELTI(c,LEVEL); +--- a/extensions/sfext/crcads/LISTOFCWTV.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/crcads/LISTOFCWTV.c 2018-08-29 20:10:06.406075801 -0600 +@@ -14,7 +14,7 @@ Outputs + + void LISTOFCWTV(Word C, Word *Lt_, Word *Lf_) + { +- Word L,Cp,Lt,Lf,t,Ltp,Lfp; ++ Word Cp,Lt,Lf,t,Ltp,Lfp; + + Step1: /* If C is undetermined recurse on the children. */ + t = LELTI(C,TRUTH); +--- a/extensions/sfext/espcad/ESPCADLSNC.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/espcad/ESPCADLSNC.c 2018-08-29 20:10:06.406075801 -0600 +@@ -97,7 +97,7 @@ Output + + Word ESPCADCTLSNC(Word c1,Word c2,Word c3,Word i,Word P) + { +- Word S1,S2,S3,n,Sp1,Sp2,s1,s2,T,L,j,k; ++ Word S1,S2,n,Sp1,Sp2,s1,s2,T,L,j,k; + + Step1: /* Initialize. */ + if (LELTI(c1,SC_TMPM) == LELTI(c2,SC_TMPM) && +@@ -106,7 +106,6 @@ Step1: /* Initialize. */ + goto Return; } + S1 = LELTI(c1,SC_SIGN); + S2 = LELTI(c2,SC_SIGN); +- S3 = LELTI(c3,SC_SIGN); + + Step2: /* Figure out which polynomials are zero in c2. */ + for(n = 0, Sp1 = S1, Sp2 = S2, T = NIL; n < LENGTH(S1); n++) { +--- a/extensions/sfext/formula/FMAATOMREAD.c.orig 2018-08-29 20:05:52.865359501 -0600 ++++ b/extensions/sfext/formula/FMAATOMREAD.c 2018-08-29 20:10:06.403075806 -0600 +@@ -13,9 +13,9 @@ Word POLYINDEX(Word P, Word p, Word r, W + + void FMAATOMREAD(Word Q, Word V, Word *F_, Word *t_) + { +- Word F,P,P1,P2,R,a,r,s,t,k,pi; ++ Word F,P1,P2,R,r,t,k,pi; + char c; +- /* hide r,s,t; */ ++ /* hide r,t; */ + + Step1: /* Read the left polynomial. */ + t = 1; r = LENGTH(V); F = NIL; +--- a/extensions/sfext/formula/FMACELLEVAL.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/formula/FMACELLEVAL.c 2018-08-29 20:10:06.404075804 -0600 +@@ -15,7 +15,7 @@ Outputs + + Word FMACELLEVAL(Word F, Word C, Word P) + { +- Word t,L,i,j,k,op,s,S,ip,c,v,cp,n,t1,A1,A2,U_FLAG; ++ Word t,L,i,j,k,op,s,S,ip,c,cp,n,t1,A1,A2,U_FLAG; + + switch(FIRST(F)) { + +--- a/extensions/sfext/formula/FMAPOLLIST.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/formula/FMAPOLLIST.c 2018-08-29 20:10:06.404075804 -0600 +@@ -19,7 +19,7 @@ static Word subprog(Word F); + + Word FMAPOLLIST(Word F, Word P) + { +- Word Q,L,i,j,n,np,P_i,G; ++ Word Q,L,i,j,P_i; + + Q = NIL; + L = subprog(F); +--- a/extensions/sfext/formula/FMATRYDISTRIBUTE.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/formula/FMATRYDISTRIBUTE.c 2018-08-29 20:10:06.404075804 -0600 +@@ -45,7 +45,7 @@ Fp: the conjunction of C and F. The fun + Word FMADISTRIBUTE(Word F, Word C) + { + +- Word Fp, f, L, flag, fp,gp,I,X,X_C, I_C, op, A, Ap; ++ Word Fp, f, L, flag, gp,I,X,X_C, I_C, op, A, Ap; + f = FIRST(F); + + switch(f) +@@ -59,7 +59,6 @@ Word FMADISTRIBUTE(Word F, Word C) + L = NIL; + for(Fp = CINV(RED(F)); Fp != NIL; Fp = RED(Fp)) + { +- fp = FIRST(Fp); + gp = FMADISTRIBUTE(FIRST(Fp),C); + L = COMP(gp,L); + } +--- a/extensions/sfext/formula/FMAWRITE.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/formula/FMAWRITE.c 2018-08-29 20:10:06.404075804 -0600 +@@ -8,7 +8,7 @@ ForMulA write. + + void FMAWRITE(Word F, Word P, Word V) + { +- Word L,E,i,j,k,op,t,Fp,O,A; ++ Word i,j,k,op,t,Fp,O,A; + + switch(FTYPEINFO(F)) { + +--- a/extensions/sfext/formula/FMAWRITELATEX.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/formula/FMAWRITELATEX.c 2018-08-29 20:10:06.404075804 -0600 +@@ -8,7 +8,7 @@ ForMulA write. + + void FMAWRITELATEX(Word F, Word P, Word V) + { +- Word L,E,i,j,k,op,t,Fp,O,A; ++ Word i,j,k,op,t,Fp,O,A; + + switch(FTYPEINFO(F)) { + +@@ -62,7 +62,7 @@ void FMAWRITELATEX(Word F, Word P, Word + + void FMAWRITELATEXp(Word F, Word P, Word V, Word flag) + { +- Word L,E,i,j,k,op,t,Fp,O,A; ++ Word i,j,k,op,t,Fp,O,A; + + switch(FTYPEINFO(F)) { + +--- a/extensions/sfext/formula/FMAWRITENEWLINE.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/formula/FMAWRITENEWLINE.c 2018-08-29 20:10:06.404075804 -0600 +@@ -10,7 +10,7 @@ ForMulA write. + + void FMAWRITENEWLINE(Word F, Word P, Word V, Word n) + { +- Word L,E,i,j,k,op,t,Fp,O,A; ++ Word i,j,k,op,t,Fp,O,A; + + switch(FTYPEINFO(F)) { + +@@ -65,7 +65,7 @@ void FMAWRITENEWLINE(Word F, Word P, Wor + + void FMAWRITENEWLINEp(Word F, Word P, Word V, Word flag) + { +- Word L,E,i,j,k,op,t,Fp,O,A; ++ Word i,j,k,op,t,Fp,O,A; + + switch(FTYPEINFO(F)) { + +--- a/extensions/sfext/formula/FMAWRITEp.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/formula/FMAWRITEp.c 2018-08-29 20:10:06.404075804 -0600 +@@ -9,7 +9,7 @@ ForMulA write. + + void FMAWRITEp(Word F, Word P, Word V, Word flag) + { +- Word L,E,i,j,k,op,t,Fp,O,A; ++ Word i,j,k,op,t,Fp,O,A; + + switch(FTYPEINFO(F)) { + +--- a/extensions/sfext/formula/FMAWRITEQEIN.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/formula/FMAWRITEQEIN.c 2018-08-29 20:10:06.404075804 -0600 +@@ -7,7 +7,7 @@ ForMulA write. + + void FMAWRITEQEIN(Word F, Word P, Word V) + { +- Word Q,L,i,j,n,np,P_i,G; ++ Word Q,i,n,np,G; + + Step1: /* Get list of polynomials. */ + Q = FMAPOLLIST(F,P); +--- a/extensions/sfext/minhit/MINHITSETSRDR.c.orig 2018-08-29 20:03:25.109519358 -0600 ++++ b/extensions/sfext/minhit/MINHITSETSRDR.c 2018-08-29 20:10:06.402075807 -0600 +@@ -27,7 +27,7 @@ Note: The point is that not only sortin + static Word comp(Word a, Word b) __pure; + + static Word comp(Word a, Word b) { +- Word ap,bp,t,q; ++ Word ap,bp,t; + ap = a; bp = b; + t = BDCOMP(LENGTH(ap),LENGTH(bp)); + while ((t == 0) && (bp != NIL) && (ap != NIL)) { +--- a/extensions/sfext/pcadst/CADFPCAD.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/pcadst/CADFPCAD.c 2018-08-29 20:10:06.402075807 -0600 +@@ -21,7 +21,7 @@ Outputs + + Word CADFPCAD(Word D, Word P, Word S, Word I, Word Pb) + { +- Word Db,Is,N,Sb,Pb_N,Ts,L,p,i,is,Q,Ms,C,Cs,Ds,Ss; ++ Word Db,Is,N,Sb,Pb_N,Ts,L,p,i,is,Ms,C,Cs,Ds,Ss; + Word Mb,mb; + + Step1: /* Is D the root cell? */ +--- a/extensions/sfext/pcadst/CADFPCADWI.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/pcadst/CADFPCADWI.c 2018-08-29 20:10:06.401075809 -0600 +@@ -22,7 +22,7 @@ Outputs + + Word CADFPCADWI(Word D, Word P, Word S, Word I, Word Pb) + { +- Word Db,Is,N,Sb,Pb_N,Ts,L,p,i,is,Q,Ms,C,Cs,Ds,Ss; ++ Word Db,Is,N,Sb,Pb_N,Ts,L,p,i,is,Ms,C,Cs,Ds,Ss; + Word Mb,mb; + + Step1: /* Is D the root cell? */ +--- a/extensions/sfext/pcadst/CCADCONEXT.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/pcadst/CCADCONEXT.c 2018-08-29 20:10:06.402075807 -0600 +@@ -21,7 +21,7 @@ Outputs + + void CCADCONEXT(Word n, Word P, Word C, Word *Ps_, Word *Cs_, Word *N_) + { +- Word Ps,i,Cs,Q,S,c,L,Lp,b,d,bs,ds,cs,T1,T2,N,Np; ++ Word Ps,i,Cs,Q,S,L,Lp,bs,ds,cs,T1,T2,N; + + Word t; + +--- a/extensions/sfext/pcadst/CCADCONmod.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/pcadst/CCADCONmod.c 2018-08-29 20:10:06.402075807 -0600 +@@ -22,7 +22,7 @@ Outputs + + void CCADCONmod(Word n, Word P, Word C, Word *Ps_, Word *Cs_) + { +- Word Ps,i,Cs,Q,S,c,L,Lp,b,d,bs,ds,cs,T1,T2; ++ Word Ps,i,Cs,Q,S,c,L,Lp,b,d,bs,ds,T1,T2; + + Step1: /* Set Ps to the empty projection list, and Cs to the full + original CAD C. */ +--- a/extensions/sfext/pcadst/LTFOCALWTV.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/pcadst/LTFOCALWTV.c 2018-08-29 20:10:06.402075807 -0600 +@@ -16,7 +16,7 @@ Outputs + + void LTFOCALWTV(Word C, Word n, Word *Lt_, Word *Lf_) + { +- Word L,Cp,Lt,Lf,t,Ltp,Lfp; ++ Word Cp,Lt,Lf,t,Ltp,Lfp; + + Step1: /* If C is undetermined recurse on the children. */ + t = LELTI(C,SC_CDTV); +--- a/extensions/sfext/pcadst/LTFOCWTV.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/pcadst/LTFOCWTV.c 2018-08-29 20:10:06.402075807 -0600 +@@ -15,7 +15,7 @@ Outputs + + void LTFOCWTV(Word C, Word *Lt_, Word *Lf_) + { +- Word L,Cp,Lt,Lf,t,Ltp,Lfp; ++ Word Cp,Lt,Lf,t,Ltp,Lfp; + + Step1: /* If C is undetermined recurse on the children. */ + t = LELTI(C,SC_CDTV); +--- a/extensions/sfext/pcadst/PCADSCANL.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/pcadst/PCADSCANL.c 2018-08-29 20:10:06.402075807 -0600 +@@ -18,7 +18,7 @@ Outputs + + Word PCADSCANL(Word Cs, Word i) + { +- Word CC,CC1,CC2,L,c; ++ Word CC,CC1,L,c; + + Step1: /* Get list of children. */ + CC = LELTI(Cs,SC_CDTV); +--- a/extensions/sfext/pcadst/PCADWRITE.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/pcadst/PCADWRITE.c 2018-08-29 20:10:06.402075807 -0600 +@@ -7,7 +7,7 @@ + + void PCADWRITE(Word Cs, Word Ps) + { +- Word C,l,i,L,k,I; ++ Word l,L,k,I; + + Step1: /* */ + I = PCADCINDEX(Cs); +--- a/extensions/sfext/pcadst/SCADDSCON.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/pcadst/SCADDSCON.c 2018-08-29 20:10:06.401075809 -0600 +@@ -30,7 +30,7 @@ example, would be bad. + + Word SCADDSCON(Word C, Word A, Word n) + { +- Word c,a,i,Cs,X,L,t; ++ Word c,a,i,Cs,X,L; + + Step1: /* Construct a preliminary version of Cs, with all but the child + list filled in. */ +--- a/extensions/sfext/pcadst/SIMPLE_CAD_INFO.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/pcadst/SIMPLE_CAD_INFO.c 2018-08-29 20:10:06.401075809 -0600 +@@ -6,7 +6,7 @@ n : the level + + void SIMPLE_CAD_INFO(Word D, Word P, Word n, Word flag) + { +- Word Ps,Cs,t,Db; ++ Word Ps,Cs,t; + + Step1: /* Check for the trivial cases. */ + switch( DOPFSUFF(P,LIST1(D)) ) { +--- a/extensions/sfext/projpf/PPFLPROJ.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/projpf/PPFLPROJ.c 2018-08-29 20:10:06.403075806 -0600 +@@ -17,11 +17,10 @@ Side Effects + + void PPFLPROJ(Word L, Word i, Word P) + { +- Word A,T,p,B,b,t,tp,C; ++ Word A,p,B,b,t,tp,C; + + Step1: /* Loop over each proj. fac. p in level i of P. */ + A = LELTI(P,i); +- T = NIL; + while(A != NIL) { + ADV(A,&p,&A); + +--- a/extensions/sfext/sfcons/CYLIMPFORM.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/sfcons/CYLIMPFORM.c 2018-08-29 20:10:06.403075806 -0600 +@@ -9,7 +9,7 @@ A : a list of atoms from which to constr + + Word CYLIMPFORM(Word C, Word P, Word k, Word A) + { +- Word SF,L,Lp,c,S,t,Q,As,Ap,Fp,F,Lt,Lf,s,Si,Fi,Qp,SF2; ++ Word SF,L,Lp,c,S,t,F,Lt,Lf,s,Si,Fi,Qp,SF2; + + Step1: /* Set L to a list of all (k-1)-level cells over which there are + k-level cells with SC_TMPM of TRUE. */ +--- a/extensions/sfext/sfcons/GEOTEST.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/sfcons/GEOTEST.c 2018-08-29 20:10:06.403075806 -0600 +@@ -101,7 +101,7 @@ Word GEOMERGE(Word c,Word G,Word P) + + Word GEOFIT(Word c,Word G,Word P) + { +- Word Q,Qp,C_Q,C_Qp,Ap,App,R,Rp,T,Tp,t,Rpp,A,m,L,x,a; ++ Word Q,Qp,C_Q,C_Qp,Ap,App,R,Rp,T,Tp,t,Rpp,m,L,x,a; + FIRST5(G,&Q,&C_Q,&Ap,&R,&T); + FIRST5(c,&Qp,&C_Qp,&App,&Rp,&Tp); + Tp = SINTER(Tp,T); +@@ -128,8 +128,8 @@ Word GEOFIT(Word c,Word G,Word P) + + Word GEOTEST(Word C,Word P,Word k,Word A) + { +- Word L,Lp,c,S,t,G,Q,Ap,Qp,App,C_Q,C_Qp,SF,SF1, +- SF2,Lt,Lf,Si,Fi,T,R,Tp,Rp,Rpp,Bs,Bi,i,s,t1,t2,SFp,Ap1,Ap2,a; ++ Word L,Lp,c,S,t,G,Q,Ap,Qp,App,C_Q,SF,SF1, ++ SF2,Lt,Lf,T,Bs,Bi,i,s,t1,t2,Ap1,Ap2,a; + + Step1: /* Set L to a list of all (k-1)-level cells over which there are + k-level cells with SC_TMPM of TRUE. */ +--- a/extensions/sfext/sfcons/SFC2.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/sfcons/SFC2.c 2018-08-29 20:10:06.402075807 -0600 +@@ -5,7 +5,7 @@ Solution formula construction version 2 + + void QepcadCls::SFC2(Word D, Word P, Word J, Word n, Word sfm) + { +- Word t,SF,D0,P0,J0,D1,P1,Pp,Dp,Q,L,Lt,Lf; ++ Word t,SF,D1,P1,Pp,Dp,Q,L,Lt,Lf; + + Step1: /* Space is either empty or R^n. */ + t = DOPFSUFF(P,LIST1(D)); +--- a/extensions/sfext/sfcons/SFC4.c.orig 2018-08-29 20:05:52.865359501 -0600 ++++ b/extensions/sfext/sfcons/SFC4.c 2018-08-29 20:10:06.403075806 -0600 +@@ -29,7 +29,7 @@ static Word F1; /* Flag indicating that + + void QepcadCls::SFC4(Word D, Word P, Word J, Word n, Word L) + { +- Word t,SF,Dp,Pp,Lt,Lf,LA,Q,D1,P1,D0,P0,J0,i,Lp,pflag; ++ Word t,SF,Dp,Pp,Lt,Lf,LA,Q,D1,P1,D0,P0,J0,i; + char e,s,m,c; + e = s = m = c = '\0'; + T1 = T2 = T3 = T4 = 0; +--- a/extensions/sfext/sfcons/SFCFULLD.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/sfcons/SFCFULLD.c 2018-08-29 20:10:06.403075806 -0600 +@@ -19,8 +19,7 @@ static Word PLISTOETAmod(Word p,Word B,W + + void QepcadCls::SFCFULLD(Word D, Word P, Word J, Word n) + { +- Word t,SF,Dp,Pp,Lt,Lf,LA,Q,D1,P1,D0,P0,J0,i,Lp,pflag, L; +- char e,s,m,c; ++ Word t,SF,Dp,Pp,Lt,Lf,LA,L; + + Step1: /* Space is either empty or R^n. */ + t = DOPFSUFF_FULLD(P,LIST1(D)); +--- a/extensions/sfext/sfcons/SFCFULLDf.c.orig 2018-08-29 20:05:52.865359501 -0600 ++++ b/extensions/sfext/sfcons/SFCFULLDf.c 2018-08-29 20:10:06.403075806 -0600 +@@ -19,8 +19,7 @@ static Word PLISTOETAmod(Word p,Word B,W + + Word QepcadCls::SFCFULLDf(Word D, Word P, Word J, Word n) + { +- Word t,SF,Dp,Pp,Lt,Lf,LA,Q,D1,P1,D0,P0,J0,i,Lp,pflag, L; +- char e,s,m,c; ++ Word t,SF,Dp,Pp,Lt,Lf,LA,L; + + Step1: /* Space is either empty or R^n. */ + t = DOPFSUFF_FULLD(P,LIST1(D)); +--- a/extensions/sfext/sfcons/SOLEXTINT.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/sfcons/SOLEXTINT.c 2018-08-29 20:10:06.403075806 -0600 +@@ -10,7 +10,7 @@ method to call and, of course, calls it. + + void QepcadCls::SOLEXTINT() + { +- Word t,F_e,F_n,F_s, T, f, c; ++ Word T, f, c; + + Step1: /* Initialization */ + T = ACLOCK(); +--- a/extensions/sfext/sort/GMSDS.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/extensions/sfext/sort/GMSDS.c 2018-08-29 20:10:06.404075804 -0600 +@@ -19,7 +19,7 @@ static Word G23VSDS(Word *v,Word m,Word + + Word GMSDS(Word *A, Word m, Word (*C)(Word,Word)) + { +- Word *T,*T1,*T2,*A1,*A2,m1,m1p,m2,m2p,mp,k,l; ++ Word *T,*T1,*T2,*A1,*A2,m1,m1p,m2,m2p,mp,k; + + Step1: /* Split. */ + m1 = m >> 1; +--- a/plot2d/plot.cc.orig 2018-08-29 20:08:10.014211101 -0600 ++++ b/plot2d/plot.cc 2018-08-29 20:10:06.396075816 -0600 +@@ -112,7 +112,6 @@ void* readdata(void *x) + { + vector E; + istream &in = *inp; +- int flag; + char c; + do { + while( in >> c ) +--- a/source/db/SINGULAR.c.orig 2018-08-29 20:08:10.015211100 -0600 ++++ b/source/db/SINGULAR.c 2018-08-29 20:10:06.406075801 -0600 +@@ -162,8 +162,6 @@ Return: /* Prepare for return. */ + + void SingularServer::IPFAC(Word r, Word P, Word *s_, Word *c_, Word *L_) + { +- int T1 = serverTime(); +- + Word V = CreateVariableList(r); + string out = WritePolyForSingular(r,P,V); + +@@ -212,9 +210,6 @@ void SingularServer::IPFAC(Word r, Word + Word ct = IABSF(lcf); + sn *= ISIGNF(lcf); + +- // Figure out how long that took! +- int T2 = serverTime(); +- + // RETURN + *s_ = sn; + *c_ = ct; +--- a/source/db/unnamedpipe.h.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/db/unnamedpipe.h 2018-08-29 20:10:06.406075801 -0600 +@@ -118,7 +118,7 @@ public: + if (openmask[0]) { close(fd[0]); openmask[0] = false; } + } + void closeOut() { +- const char ts[2] = {EOF,'\n'}; ++ // const char ts[2] = {EOF,'\n'}; + if (_out) { delete _out; _out = 0; } + if (openmask[1]) { + // write(fd[1],ts,2); +--- a/source/io/DNFLWR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/DNFLWR.c 2018-08-29 20:10:06.407075800 -0600 +@@ -13,7 +13,7 @@ Disjuctive Normal Form Label Write. + + void DNFLWR(Word N, Word V, Word F) + { +- Word F1,F2,Fp,T; ++ Word F1,Fp,T; + + Step1: /* Classify the formula F. */ + T = FIRST(F); +--- a/source/io/DNFWR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/DNFWR.c 2018-08-29 20:10:06.408075798 -0600 +@@ -12,7 +12,7 @@ Disjunctive Normal Form Write. + + void DNFWR(Word V, Word F) + { +- Word F1,F2,Fp,T; ++ Word F1,Fp,T; + + Step1: /* Classify the formula F. */ + T = FIRST(F); +--- a/source/io/IPLLDWR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/IPLLDWR.c 2018-08-29 20:10:06.407075800 -0600 +@@ -12,7 +12,7 @@ Integral Polynomial, List of Lists, Dist + + void IPLLDWR(Word V, Word A) + { +- Word A1,A11,i,P,L,H; ++ Word A1,A11,i,P,H; + /* hide i,j,n,r; */ + + Step1: /* Write. */ +--- a/source/io/IPLLDWRMOD.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/IPLLDWRMOD.c 2018-08-29 20:10:06.407075800 -0600 +@@ -12,7 +12,7 @@ Integral Polynomial, List of Lists, Dist + + void IPLLDWRMOD(Word V, Word A) + { +- Word A1,A11,i,P,L,H; ++ Word A1,A11,i,P,H; + /* hide i,j,n,r; */ + + Step1: /* Write. */ +--- a/source/io/PCADSWR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/PCADSWR.c 2018-08-29 20:10:06.407075800 -0600 +@@ -11,7 +11,7 @@ Partial CAD signature write. + + void PCADSWR(Word c) + { +- Word M,cb,cp,p,s,S; ++ Word cb,cp,p,s,S; + /* hide p; */ + + Step1: /* common. */ +--- a/source/io/PCADWR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/PCADWR.c 2018-08-29 20:10:06.407075800 -0600 +@@ -11,7 +11,7 @@ Partial CAD write. + + void PCADWR(Word c) + { +- Word M,cb,cp,p,s; ++ Word cb,cp,p,s; + /* hide p; */ + + Step1: /* common. */ +--- a/source/io/PRODWR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/PRODWR.c 2018-08-29 20:10:06.407075800 -0600 +@@ -10,7 +10,8 @@ Product Write. + + void PRODWR(Word v) + { +- Word I,I1,P1,P11,Pp,R1,W,W1,i,j,n1,v1,vp; ++ /* Word P1,P11,Pp,i,j,n1; */ ++ Word I,I1,R1,W,W1,v1,vp; + /* hide i,j,n1; */ + + SWRITE("In \"PRODWR\"! This is dead code!\n"); exit(1); +--- a/source/io/SAMPLEWR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/io/SAMPLEWR.c 2018-08-29 20:10:06.408075798 -0600 +@@ -55,8 +55,7 @@ Return: /* Prepare for return. */ + + void QepcadCls::SAMPLEWR(Word c) + { +- Word I,Ip,M,Mp,b,bp,k,s,F,j,Ms,Is; +- Word M1; ++ Word k,s; + + s = LELTI(c,SAMPLE); + k = LELTI(c,LEVEL); +--- a/source/main/CADautoConst.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/main/CADautoConst.c 2018-08-29 20:10:06.409075797 -0600 +@@ -16,11 +16,7 @@ been set to something other than NIL, th + + void QepcadCls::CADautoConst() + { +- Word A,D,F,F_e,F_n,F_s,Fh,J,P,Q,Ths,f,i,r,t, T; +- /* hide Ths,i,t; */ +- Word cL,**cC,cr,ce,ci,*cT,cj,cs,cl,ct; /* Chris variables. */ +- Word Cs,Ps,Qs,Pps,Cps,Qps,SF; /* Chris variables. */ +- char c1,c2; /* Chris variables. */ ++ Word A,D,F,Fh,J,P,Q,f,r; + + Step1: /* Normalize. */ + FIRST4(GVF,&r,&f,&Q,&Fh); +--- a/source/main/INITCTRL.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/main/INITCTRL.c 2018-08-29 20:10:06.409075797 -0600 +@@ -7,12 +7,9 @@ Initialize Program/Trace Control. + + void INITCTRL() + { +- Word k; +- /* hide k; */ +- + Step1: /* Initialize program control */ +- Word PCDBUSE = 'y'; +- Word PCDBLIMIT = 10; ++ PCDBUSE = 'y'; ++ PCDBLIMIT = 10; + + Step2: /* Initialize Algorithm Trace Control Variables. */ + TCPROJ = NIL; +--- a/source/main/INITIO.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/main/INITIO.c 2018-08-29 20:10:06.409075797 -0600 +@@ -14,8 +14,6 @@ void OutputContextInit(ostream&); + + void INITIO() + { +- Word i; +- + Step1: /* Initialize Input. */ + if (isatty(0)) + InputContextInit(*(new readlineIstream())); +--- a/source/main/INITSTAT.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/main/INITSTAT.c 2018-08-29 20:10:06.409075797 -0600 +@@ -7,10 +7,6 @@ Initialize Statistics. + + void INITSTAT() + { +- Word i; +- /* hide i; */ +- +- + Step5: /* Statistics on Databases. */ + TMDBMNG = 0; + TMDBSAV = 0; +--- a/source/main/QEPCADauto.c.orig 2018-08-29 20:05:52.867359499 -0600 ++++ b/source/main/QEPCADauto.c 2018-08-29 20:10:06.409075797 -0600 +@@ -20,11 +20,8 @@ Quantifier Elimination by Partial Cylind + + void QepcadCls::QEPCADauto(Word Fs, Word *t_, Word *F_e_, Word *F_n_, Word *F_s_) + { +- Word A,D,F,F_e,F_n,F_s,Fh,J,P,Q,Ths,f,i,r,t, T; +- /* hide Ths,i,t; */ +- Word cL,**cC,cr,ce,ci,*cT,cj,cs,cl,ct; /* Chris variables. */ +- Word Cs,Ps,Qs,Pps,Cps,Qps,SF; /* Chris variables. */ +- char c1,c2; /* Chris variables. */ ++ Word A,D,F,F_e,F_n,F_s,Fh,J,P,Q,f,r,t; ++ /* hide t; */ + + Step1: /* Normalize. */ + t = 0; +--- a/source/main/QEPCAD.c.orig 2018-08-29 20:05:52.867359499 -0600 ++++ b/source/main/QEPCAD.c 2018-08-29 20:10:06.409075797 -0600 +@@ -22,9 +22,6 @@ void QepcadCls::QEPCAD(Word Fs, Word *t_ + { + Word A,D,F,F_e,F_n,F_s,Fh,J,P,Q,Ths,f,i,r,t, T; + /* hide Ths,i,t; */ +- Word cL,**cC,cr,ce,ci,*cT,cj,cs,cl,ct; /* Chris variables. */ +- Word Cs,Ps,Qs,Pps,Cps,Qps,SF; /* Chris variables. */ +- char c1,c2; /* Chris variables. */ + Step1: /* Normalize. */ + t = 0; + F_e = F_n = F_s = NIL; +--- a/source/proj/ECLEVEL.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/proj/ECLEVEL.c 2018-08-29 20:10:06.408075798 -0600 +@@ -14,10 +14,9 @@ Output + + Word ECLEVEL(Word L) + { +- Word k,Lp,L1; ++ Word k,L1; + + Step1: /* Initialize. */ +- Lp = L; + k = 0; + + Step2: /* Loop. */ +--- a/source/proj/PROJCO.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/proj/PROJCO.c 2018-08-29 20:10:06.408075798 -0600 +@@ -15,7 +15,7 @@ Collins' projection. + + Word QepcadCls::PROJCO(Word r, Word A) + { +- Word A1,A2,Ap,Ap1,Ap2,App,D,L,L1,P,P1,R,R1,R11,R2,R21,Rp,Rp1,Rp11,Rp2, ++ Word A1,A2,Ap,Ap1,Ap2,App,D,L,L1,P,R,R1,R11,R2,R21,Rp,Rp1,Rp11,Rp2, + Rpp,Rs2,S1,Sp1,T,W,ap1,b,d,i,i1,i2,k,t; + + Step1: /* $r = 2$. */ +--- a/source/proj/PROJECTauto.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/proj/PROJECTauto.c 2018-08-29 20:10:06.408075798 -0600 +@@ -17,7 +17,7 @@ Projection Phase. + + void QepcadCls::PROJECTauto(Word r, Word A, Word *P_, Word *J_) + { +- Word D,F,J,P,Ps,J_k1,P_k,R,Ths,Thss,k,i; ++ Word D,F,J,P,J_k1,P_k,R,k,i; + + Step1: /* Initialize. */ + P = LLCOPY(A); +--- a/source/proj/PROJECT.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/proj/PROJECT.c 2018-08-29 20:10:06.408075798 -0600 +@@ -19,7 +19,7 @@ Projection Phase. + + void QepcadCls::PROJECT(Word r, Word A, Word *P_, Word *J_) + { +- Word D,F,J,P,Ps,J_k1,P_k,R,Ths,Thss,k,i; ++ Word D,F,J,P,J_k1,P_k,R,Ths,Thss,k,i; + + Step1: /* Initialize. */ + P = LLCOPY(A); +--- a/source/proj/PROJMCECmod.c.orig 2018-08-29 20:07:50.511232206 -0600 ++++ b/source/proj/PROJMCECmod.c 2018-08-29 20:10:06.408075798 -0600 +@@ -18,8 +18,8 @@ not propogate! + + Word QepcadCls::PROJMCECmod(Word r, Word A) + { +- Word A1,A2,Ap,Ap1,Ap2,App,D,L,Lh,P,R,W,i,t,Q,j,S,Sp,C; +- Word Ls, Lc,LL,f,rp,fp,tf,T1,fef,esu,AssTmp,Sf,k; ++ Word A1,A2,Ap,Ap1,Ap2,App,D,L,Lh,P,R,W,i,t,Q,j,S,C; ++ Word Ls, Lc,LL,f,rp,fp,T1,fef,esu,AssTmp,Sf; + + Step0: /* Obtain pivot constraint: If pivot is not all of level k, we can't use it! */ + C = LELTI(GVPIVOT,r); +--- a/source/proj/PROJMCmod.c.orig 2018-08-29 20:07:50.511232206 -0600 ++++ b/source/proj/PROJMCmod.c 2018-08-29 20:10:06.409075797 -0600 +@@ -20,8 +20,8 @@ using namespace std; + + Word QepcadCls::PROJMCmod(Word r, Word A) + { +- Word A1,A2,Ap,Ap1,Ap2,App,D,L,Lh,P,R,W,i,t,Q,j,S,Sp; +- Word Ls, Lc,LL,f,rp,fp,tf,T1,fef,esu,AssTmp,Sf; ++ Word A1,A2,Ap,Ap1,Ap2,App,D,L,Lh,P,R,W,i,t,Q,j,S; ++ Word Ls, Lc,LL,f,rp,fp,T1,fef,esu,AssTmp,Sf; + + Step1: /* Obtain coefficients. */ + P = NIL; +--- a/source/proj/PROJMCx.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/proj/PROJMCx.c 2018-08-29 20:10:06.408075798 -0600 +@@ -15,7 +15,7 @@ McCallum's projection excluding leading + + Word QepcadCls::PROJMCx(Word r, Word A) + { +- Word A1,A2,Ap,Ap1,Ap2,App,D,L,Lh,P,R,W,i,t; ++ Word A1,A2,Ap,Ap1,Ap2,App,D,P,R,W; + + Step1: /* Obtain coefficients. */ + P = NIL; +--- a/source/saclib/GCSI.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/saclib/GCSI.c 2018-08-29 20:10:06.408075798 -0600 +@@ -23,10 +23,9 @@ extern void gcw_MARK(); + + void GCSI(Word s, char *EACSTACK) + { +- Word I,L,N,N1,Np,Np1,T,T1,c,**i,j,inc; ++ Word I,L,N,N1,Np,Np1,T,T1,c,inc; + char *a; +- GCArray *v; +- /* hide I,L,N,N1,Np,Np1,T,T1,c,i,j,inc,a,v; */ ++ /* hide I,L,N,N1,Np,Np1,T,T1,c,inc,a; */ + + Step1: /* Setup. */ + if (GCM == 1) { +--- a/source/sysolve/EVALSYS.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/sysolve/EVALSYS.c 2018-08-29 20:10:06.407075800 -0600 +@@ -20,7 +20,7 @@ Outputs: + + Word EVALSYS(Word S, BDigit t, Word R) + { +- Word r, L, i, S_i, Ap, a, A, is, As, F, f, ip, fp, s, c; ++ Word r, L, i, S_i, Ap, a, A, is, As; + + Step1: /* Construct the skelaton of L, the answer. */ + r = LENGTH(S); +--- a/source/sysolve/SYSSOLVE.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/sysolve/SYSSOLVE.c 2018-08-29 20:10:06.407075800 -0600 +@@ -21,7 +21,7 @@ Output + + Word SYSSOLVECAD(BDigit r, Word L, Word A, Word Vp, QepcadCls &Q) + { +- Word F, Fp, d, t, Lt, Lf, V, S; ++ Word F, Lt, Lf, V, S; + + /* Set variable list */ + if (LENGTH(Vp) < r) { +--- a/source/sysolve/VERIFYCONSTSIGN.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/sysolve/VERIFYCONSTSIGN.c 2018-08-29 20:10:06.406075801 -0600 +@@ -78,7 +78,7 @@ BDigit VERIFYPOSITIVITY(Word A, BDigit r + else if (FIRST(A) != IROOT) + { + /* atomic formula is "P_A T_A 0", where P_A is of level k_A. */ +- Word T_A,P_A,k_A,s,rp,Pp,P,a,T; ++ Word T_A,P_A,k_A; + FIRST3(A,&T_A,&P_A,&k_A); + if (r != k_A) { goto Return; } + +--- a/source/ticad/ACCCVBC.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/ACCCVBC.c 2018-08-29 20:10:06.411075794 -0600 +@@ -28,7 +28,7 @@ static Word SECTIONPOLS(Word k, Word c, + + Word QepcadCls::ACCCVBC(Word k, Word c, Word M, Word B1, Word b, Word* B1h) + { +- Word d, nnf, dV, IV, cp, i, I_i, d_i, c_i, L, Q, Qb, Qbs, F, Fp, a; ++ Word d, nnf, dV, IV, i, I_i, c_i, L, Q, Qb, Qbs, F, Fp, a; + + Step1: /* Initialization **********************************************/ + a = NIL; /* this is the pseudo-sample point we're building up *******/ +@@ -44,7 +44,6 @@ Step1: /* Initialization *************** + Step2: /* Loop over each level from 1 to k ****************************/ + for(i = 1; i <= k; i++) { + I_i = LELTI(IV,i); +- d_i = LELTI(dV,i); + c_i = LELTI(LELTI(c_i,CHILD),I_i); + + Step3: /* c_i is a section over a 0-dimensional cell ******************/ +@@ -108,11 +107,10 @@ L : the list of all k-level polynomials + ======================================================================*/ + static Word SECTIONPOLS(Word k, Word c, Word P) + { +- Word L,P_k,M,i,Mp; ++ Word L,P_k,M,i; + L = NIL; + P_k = LELTI(P,k); +- M = LELTI(c,MULSUB); +- for(Mp = M; M != NIL; M = RED(M)) { ++ for(M = LELTI(c,MULSUB); M != NIL; M = RED(M)) { + i = FIRST(FIRST(M)); + L = COMP(LELTI(P_k,i),L); } + return L; +--- a/source/ticad/ACCCVBCR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/ACCCVBCR.c 2018-08-29 20:10:06.409075797 -0600 +@@ -25,7 +25,7 @@ static Word SECTIONPOLS(Word k, Word c, + + Word QepcadCls::ACCCVBCR(Word k, Word c, Word B1, Word b, Word* B1h) + { +- Word d, nnf, dV, IV, cp, i, I_i, d_i, c_i, L, Q, Qb, Qbs, F, Fp, a; ++ Word d, nnf, dV, IV, i, I_i, c_i, L, Q, Qb, Qbs, F, Fp, a; + + Step1: /* Initialization **********************************************/ + a = NIL; /* this is the pseudo-sample point we're building up *******/ +@@ -41,7 +41,6 @@ Step2: /* Loop over each level from 1 to + c_i = GVPC; + for(i = 1; i <= k; i++) { + I_i = LELTI(IV,i); +- d_i = LELTI(dV,i); + c_i = LELTI(LELTI(c_i,CHILD),I_i); + + Step3: /* c_i is a section over a 0-dimensional cell ******************/ +@@ -99,11 +98,10 @@ L : the list of all k-level polynomials + ======================================================================*/ + static Word SECTIONPOLS(Word k, Word c, Word P) + { +- Word L,P_k,M,i,Mp; ++ Word L,P_k,M,i; + L = NIL; + P_k = LELTI(P,k); +- M = LELTI(c,MULSUB); +- for(Mp = M; M != NIL; M = RED(M)) { ++ for(M = LELTI(c,MULSUB); M != NIL; M = RED(M)) { + i = FIRST(FIRST(M)); + L = COMP(LELTI(P_k,i),L); } + return L; +--- a/source/ticad/APEQC.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/APEQC.c 2018-08-29 20:10:06.410075795 -0600 +@@ -21,7 +21,7 @@ Effect + + void QepcadCls::APEQC(Word c, Word k, Word P) + { +- Word c1,E,E1,E11,Ep,Ep1,h,i,j,m,M,Mp,S,t,tp,Ps,p; ++ Word c1,E,E1,E11,Ep,Ep1,i,j,m,M,Mp,S,t,Ps,p; + + Step1: /* Get (k+1)-level equational constraints, if none, return. */ + E = LELTI(GVEQNCONST,k+1); +--- a/source/ticad/CONSTRUCT.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/CONSTRUCT.c 2018-08-29 20:10:06.411075794 -0600 +@@ -24,8 +24,7 @@ Word IUPSBRRI(Word B, BDigit k); + void QepcadCls::CONSTRUCT(Word c, Word k, Word f, Word Ps_, Word As) + { + BDigit p,t,Ths; +- Word B,b,E,I,Ip,I1,J,Jp,L,M,Ps,P1,Ps1,S,s,T,Q; +- Word junk,a1,b1,t1,p1,j1; ++ Word B,b,E,I,Ip,I1,J,Jp,L,M,Ps,S,s,T; + + Step0: /* Root cell. */ + if (k == 0) { CONSTRUCT1(c,k,f,Ps_,As); goto Return; } +@@ -36,9 +35,7 @@ Step1: /* Extract the projection factors + for(Word Pt = CINV(Ps_); Pt != NIL; Pt = RED(Pt)) + { + Word Pt1 = FIRST(Pt); +- if (LELTI(Pt1,PO_TYPE) == PO_POINT) +- junk = 1; +- else ++ if (LELTI(Pt1,PO_TYPE) != PO_POINT) + Ps = COMP(LELTI(Pt1,PO_POLY),Ps); + } + +@@ -242,9 +239,8 @@ BDigit C1COMP(Word A, Word B) + /* Root cell. */ + void QepcadCls::CONSTRUCT1(Word c, Word k, Word f, Word Ps_, Word As) + { +- BDigit p,t,Ths; +- Word B,b,E,I,Ip,I1,J,Jp,L,M,Ps,P1,Ps1,S,s,T,Q,Pp; +- Word junk,a1,b1,t1,p1,j1; ++ BDigit Ths; ++ Word I,Ps,Pp; + + Step1: /* Extract the projection factors from their attribute lists. */ + Ps = NIL; /* Basis for real-root isolation - i.e. the polynomials */ +@@ -390,7 +386,7 @@ Word IUPSBRRIIR(Word t_B, BDigit p, BDig + if (Li == 0) { fail = true; goto Return; } + + for(BDigit t = (n%2==1 ? 1 : -1); Li != NIL; t *= -1, Li = RED(Li)) { +- Word I = FIRST(Li), i1, i2; ++ Word I = FIRST(Li); + Word J = SIPIR(C[i],I,t,k); + L = COMP(LIST3(i,J,t),L); + } +--- a/source/ticad/EC1.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/EC1.c 2018-08-29 20:10:06.411075794 -0600 +@@ -15,7 +15,7 @@ Establish Children of the root. + + void EC1(Word c, Word L, Word Bs) + { +- Word B,I,J,Lp,M,N,S,Sp,P,a,b,kp,l,r,rp,s,xb,xp,Lp1,OL; ++ Word B,I,J,Lp,M,N,S,Sp,P,a,b,kp,l,r,s,xb,xp,Lp1,OL; + /* hide kp,xp; */ + Word T; + +@@ -58,7 +58,6 @@ Step4: /* First section. */ + + Step5: /* Check if there are more roots. */ + if (Lp == NIL) goto Step9; +- rp = r; + + Step6: /* Next sector. */ + ADV(Lp,&Lp1,&Lp); +--- a/source/ticad/EC.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/EC.c 2018-08-29 20:10:06.410075795 -0600 +@@ -16,7 +16,7 @@ Establish Children. + + void EC(Word c, Word L, Word E, Word Bs) + { +- Word B,I,J,Lp,M,N,S,Sp,Pp,P,a,b,bp,kp,l,r,rp,s,sp,x,xb,xp; ++ Word B,I,J,Lp,M,N,S,Sp,Pp,P,a,b,bp,kp,l,r,s,sp,x,xb,xp; + /* hide kp,xp; */ + Word T; + +@@ -52,7 +52,6 @@ Step4: /* First section. */ + + Step5: /* Check if there are more roots. */ + if (Lp == NIL) goto Step9; +- rp = r; + + Step6: /* Next sector. */ + ADV2(Lp,&I,&B,&Lp); FIRST2(I,&l,&r); +--- a/source/ticad/ECR.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/ECR.c 2018-08-29 20:10:06.410075795 -0600 +@@ -16,7 +16,7 @@ Establish Children on a rational sample + + void ECR(Word c, Word L, Word E, Word Bs) + { +- Word B,I,J,Lp,M,N,S,Sp,Pp,P,a,b,bp,kp,l,r,rp,s,sp,x,xb,xp; ++ Word B,I,J,Lp,M,N,S,Sp,Pp,P,a,b,bp,kp,l,r,s,sp,x,xb,xp; + /* hide kp,xp; */ + Word T; + +@@ -55,7 +55,6 @@ Step4: /* First section. */ + + Step5: /* Check if there are more roots. */ + if (Lp == NIL) goto Step9; +- rp = r; + + Step6: /* Next sector. */ + ADV2(Lp,&I,&B,&Lp); FIRST2(I,&l,&r); +--- a/source/ticad/MAFUPGCD.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/MAFUPGCD.c 2018-08-29 20:10:06.409075797 -0600 +@@ -17,7 +17,7 @@ Output + + Word MAFUPGCD(Word p, Word M, Word A, Word B) + { +- Word C,A1,A2,A3,a,ap,r; ++ Word C,A1,A2,A3; + + Step1: /* A = 0 \/ B = 0 */ + if (A == 0) { +--- a/source/ticad/MAFUPMON.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/MAFUPMON.c 2018-08-29 20:10:06.410075795 -0600 +@@ -17,7 +17,7 @@ Output + + Word MAFUPMON(Word p, Word M, Word A) + { +- Word B,d,Ap,a,ap,r; ++ Word B,d,Ap,a,ap; + + Step1: /* A = 0 */ + if (A == 0) { +--- a/source/ticad/QFFTEV.c.orig 2018-08-29 20:03:25.112519355 -0600 ++++ b/source/ticad/QFFTEV.c 2018-08-29 20:10:06.411075794 -0600 +@@ -17,8 +17,8 @@ static Word ATOMETFEVAL(Word Q, Word D, + + Word QepcadCls::QFFTEV(Word F, Word c, Word k) + { +- Word F1,Fp,I,Pt,T,i,j,m,s,t,tp,z; +- /* hide m,t,tp,z; */ ++ Word F1,Fp,I,Pt,T,i,j,s,t,tp,z; ++ /* hide t,tp,z; */ + + Step1: /* Classify the formula $F$. */ + T = FIRST(F); +--- a/source/ticad/SIGNP1.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/SIGNP1.c 2018-08-29 20:10:06.410075795 -0600 +@@ -19,7 +19,7 @@ using namespace std; + + void SIGNP1(Word c, Word P1, Word I) + { +- Word S1,c1,cb,I1; ++ Word c1,cb,I1; + + Step1: /* Compute the signatures of $P_1$. */ + vector S; +--- a/source/ticad/SUBST.c.orig 2018-08-29 20:05:52.867359499 -0600 ++++ b/source/ticad/SUBST.c 2018-08-29 20:10:06.410075795 -0600 +@@ -20,7 +20,6 @@ Substitute the sample point into the pro + Word QepcadCls::SUBST(Word c, Word k, Word M, Word b, Word B) + { + Word B1,Bp,S,S1; +- Word dV,IV; + Word P,L,Sp,T1,T2,G,Q,f,i; + + Step1: /* Substitute. */ +--- a/source/ticad/SUBSTR.c.orig 2018-08-29 20:05:52.868359497 -0600 ++++ b/source/ticad/SUBSTR.c 2018-08-29 20:10:06.411075794 -0600 +@@ -20,7 +20,6 @@ Substitute the rational sample point int + Word QepcadCls::SUBSTR(Word c, Word k, Word b, Word B) + { + Word B1,Bp,S,S1; +- Word dV,IV; + Word P,L,Q,T1,T2,Sp,G,f,i; + + Step1: /* Do it. */ +--- a/source/ticad/TICADauto.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/TICADauto.c 2018-08-29 20:10:06.410075795 -0600 +@@ -24,7 +24,7 @@ Output + + Word QepcadCls::TICADauto(Word Q, Word F, Word f, Word P, Word A) + { +- Word As,D,Ps,Ths,Thss,c,cp,k,s,sh,sp,t,R,S; ++ Word As,D,Ps,c,k,s,sh,t; + + Step1: /* Initialize. */ + D = INITPCAD(); +--- a/source/ticad/TICAD.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/ticad/TICAD.c 2018-08-29 20:10:06.410075795 -0600 +@@ -24,7 +24,7 @@ Output + + Word QepcadCls::TICAD(Word Q, Word F, Word f, Word P, Word A) + { +- Word As,D,Ps,Ths,Thss,c,cp,k,s,sh,sp,t,R,S; ++ Word As,D,Ps,Ths,Thss,c,k,s,sh,t; + + Step1: /* Initialize. */ + D = INITPCAD(); +--- a/source/userint/PRDLFI.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/userint/PRDLFI.c 2018-08-29 20:10:06.411075794 -0600 +@@ -7,7 +7,7 @@ Process "display-level-factors i" comman + + void QepcadCls::PRDLFI() + { +- Word i,j,L; ++ Word i; + + Step1: /* Get level. */ + i = IREAD(); +--- a/source/userint/PREQNCONSTL.c.orig 2018-08-29 20:05:52.868359497 -0600 ++++ b/source/userint/PREQNCONSTL.c 2018-08-29 20:10:06.412075792 -0600 +@@ -90,7 +90,7 @@ Word POLYLABEL(Word P, Word p, Word r, W + + void QepcadCls::PREQNCONSTPOLY() + { +- Word t1,t2,t3,r,P1,E,k,pi; ++ Word t1,t2,t3,r,P1,E,pi; + + // Check if propagation of equational constraints was specified. + if (PCPROPEC == FALSE) { +--- a/source/userint/PRMCC.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/userint/PRMCC.c 2018-08-29 20:10:06.411075794 -0600 +@@ -10,7 +10,7 @@ Process "manually choose a cell" command + + void QepcadCls::PRMCC(Word *t_) + { +- Word C,c,cp,t; ++ Word c,t; + /* hide t; */ + + Step1: /* Read in an argument. */ +--- a/source/userint/PRPROPEC.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/userint/PRPROPEC.c 2018-08-29 20:10:06.412075792 -0600 +@@ -7,7 +7,7 @@ Process prop-eqn-const command. + + void QepcadCls::PRPROPEC() + { +- Word C,i,r; ++ Word i,r; + + Step1: /* Toggle the PCPROPEC global variable and initialize globals. */ + GVEQNCONST = GVPIVOT = NIL; +--- a/source/userint/PRRMPF.c.orig 2018-01-25 14:25:22.000000000 -0700 ++++ b/source/userint/PRRMPF.c 2018-08-29 20:10:06.411075794 -0600 +@@ -7,7 +7,7 @@ Process "Remove Projection Factor" comma + + void QepcadCls::PRRMPF() + { +- Word A_i,C,P_i,P_ij,i,j,t; ++ Word C,P_i,P_ij,i,j,t; + /* hide C,t; */ + + Step1: /* Read in arguments. */ diff --git a/build/pkgs/qepcad/spkg-install b/build/pkgs/qepcad/spkg-install deleted file mode 100644 index 5db42370b73..00000000000 --- a/build/pkgs/qepcad/spkg-install +++ /dev/null @@ -1,30 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting"; - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - - -cd src - -# QEPCAD needs these environment variables to be set -export saclib="$SAGE_LOCAL/lib/saclib" -export qe=$(pwd -P) - -# * Override SHELL: use /bin/sh instead of /bin/csh, see -# https://trac.sagemath.org/ticket/10224 -# * Add rpath to compiler flags, see -# https://trac.sagemath.org/ticket/22653 -$MAKE -j1 opt SHELL=/bin/sh FLAGSo="-O3 -g -Wl,-rpath,$SAGE_LOCAL/lib" -if [ $? -ne 0 ]; then - echo >&2 "Error building qepcad." - exit 1 -fi - - -# install binaries to the Sage tree -find . -name *.a -exec cp {} "$SAGE_LOCAL/lib/" \; -cp source/qepcad "$SAGE_LOCAL/bin/" -cp bin/qepcad.help "$SAGE_LOCAL/bin/" -chmod 0644 "$SAGE_LOCAL/bin/qepcad.help" -cp ../sage-qepcad "$SAGE_LOCAL/bin/" diff --git a/build/pkgs/qepcad/spkg-install.in b/build/pkgs/qepcad/spkg-install.in new file mode 100644 index 00000000000..5dfe9a0d5cc --- /dev/null +++ b/build/pkgs/qepcad/spkg-install.in @@ -0,0 +1,29 @@ +cd src + +# QEPCAD needs these environment variables to be set +export saclib="$SAGE_LOCAL/lib/saclib" +export qe=$(pwd -P) + +# * Override SHELL: use /bin/sh instead of /bin/csh, see +# https://trac.sagemath.org/ticket/10224 +# * Add rpath to compiler flags, see +# https://trac.sagemath.org/ticket/22653 +# * Use ARFLAGS that also work on macOS, avoiding the U option, see +# https://trac.sagemath.org/ticket/28388 +LIBS=-lreadline +if [ "$UNAME" = "Linux" ]; then + LIBS="$LIBS -lrt" +fi +$MAKE -j1 opt SHELL=/bin/sh FLAGSo="-O3 -g -Wl,-rpath,$SAGE_LOCAL/lib" ARFLAGS="crv" LIBS="$LIBS" +if [ $? -ne 0 ]; then + echo >&2 "Error building qepcad." + exit 1 +fi + + +# install binaries to the Sage tree +find . -name *.a -exec cp {} "$SAGE_LOCAL/lib/" \; +cp source/qepcad "$SAGE_LOCAL/bin/" +cp bin/qepcad.help "$SAGE_LOCAL/bin/" +chmod 0644 "$SAGE_LOCAL/bin/qepcad.help" +cp ../sage-qepcad "$SAGE_LOCAL/bin/" diff --git a/build/pkgs/qhull/SPKG.rst b/build/pkgs/qhull/SPKG.rst new file mode 100644 index 00000000000..03c42327734 --- /dev/null +++ b/build/pkgs/qhull/SPKG.rst @@ -0,0 +1,48 @@ +Qhull +===== + +Description +----------- + +From the README.txt of Qhull: + +Qhull computes convex hulls, Delaunay triangulations, Voronoi diagrams, +furthest-site Voronoi diagrams, and halfspace intersections about a +point. It runs in 2-d, 3-d, 4-d, or higher. It implements the Quickhull +algorithm for computing convex hulls. Qhull handles round-off errors +from floating point arithmetic. It can approximate a convex hull. + +The program includes options for hull volume, facet area, partial hulls, +input transformations, randomization, tracing, multiple output formats, +and execution statistics. + +Further notes: + +The qhull library is already shipped with the Python library scipy (from +version 1.4), see + +- http://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.ConvexHull.html +- http://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.Delaunay.html +- http://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.Voronoi.html + +There is also the Python interface Pyhull available on PyPI +https://pypi.python.org/pypi/pyhull (see also documentation at +http://pythonhosted.org/pyhull/). + + +Upstream Contact +---------------- + +C. Bradford Barber bradb@shore.net or qhull@qhull.org + +Dependencies +------------ + +Can be compiled with Qt support, but the Sage version currently doesn't +try to do this. + +License +------- + +Not a standard license, but Sage compatible. See the COPYING.txt file in +the source directory for details. diff --git a/build/pkgs/qhull/SPKG.txt b/build/pkgs/qhull/SPKG.txt deleted file mode 100644 index ab514bc1f69..00000000000 --- a/build/pkgs/qhull/SPKG.txt +++ /dev/null @@ -1,43 +0,0 @@ -= Qhull = - -== Description == - -From the README.txt of Qhull: - -Qhull computes convex hulls, Delaunay triangulations, Voronoi diagrams, -furthest-site Voronoi diagrams, and halfspace intersections about a point. -It runs in 2-d, 3-d, 4-d, or higher. It implements the Quickhull algorithm -for computing convex hulls. Qhull handles round-off errors from floating -point arithmetic. It can approximate a convex hull. - -The program includes options for hull volume, facet area, partial hulls, -input transformations, randomization, tracing, multiple output formats, and -execution statistics. - - -Further notes: - -The qhull library is already shipped with the Python library scipy (from -version 1.4), see - -- http://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.ConvexHull.html -- http://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.Delaunay.html -- http://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.Voronoi.html - -There is also the Python interface Pyhull available on PyPI -https://pypi.python.org/pypi/pyhull (see also documentation at http://pythonhosted.org/pyhull/). - -== Upstream Contact == - -C. Bradford Barber -bradb@shore.net -or qhull@qhull.org - -== Dependencies == - -Can be compiled with Qt support, but the Sage version currently doesn't try to do this. - -== License == - -Not a standard license, but Sage compatible. See the COPYING.txt file in the -source directory for details. diff --git a/build/pkgs/qhull/spkg-check b/build/pkgs/qhull/spkg-check deleted file mode 100644 index a9a5d79eaa2..00000000000 --- a/build/pkgs/qhull/spkg-check +++ /dev/null @@ -1,14 +0,0 @@ -cd src/ - -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -$MAKE test -if [ $? -ne 0 ]; then - echo "Error in testing qhull" - exit 1 -fi - diff --git a/build/pkgs/qhull/spkg-check.in b/build/pkgs/qhull/spkg-check.in new file mode 100644 index 00000000000..1d233337483 --- /dev/null +++ b/build/pkgs/qhull/spkg-check.in @@ -0,0 +1,3 @@ +cd src + +$MAKE test diff --git a/build/pkgs/qhull/spkg-install b/build/pkgs/qhull/spkg-install.in similarity index 100% rename from build/pkgs/qhull/spkg-install rename to build/pkgs/qhull/spkg-install.in diff --git a/build/pkgs/r/SPKG.rst b/build/pkgs/r/SPKG.rst new file mode 100644 index 00000000000..2b498e85e0f --- /dev/null +++ b/build/pkgs/r/SPKG.rst @@ -0,0 +1,37 @@ +R += + +Description +----------- + +R is a language and environment for statistical computing and graphics. +It is a GNU project which is similar to the S language and environment +which was developed at Bell Laboratories (formerly AT&T, now Lucent +Technologies) by John Chambers and colleagues. R can be considered as a +different implementation of S. There are some important differences, but +much code written for S runs unaltered under R. + +(taken from http://www.r-project.org/) + +License +------- + +- GPL v2 or GPL v3 + + +Upstream Contact +---------------- + +- R mailing list, #R in IRC + +Dependencies +------------ + +- GNU patch +- iconv +- Readline +- BLAS/LAPACK +- xz +- pcre +- curl +- https-capable SSL diff --git a/build/pkgs/r/SPKG.txt b/build/pkgs/r/SPKG.txt deleted file mode 100644 index a6b1e72ab2c..00000000000 --- a/build/pkgs/r/SPKG.txt +++ /dev/null @@ -1,29 +0,0 @@ -= R = - -== Description == -R is a language and environment for statistical computing and graphics. -It is a GNU project which is similar to the S language and environment -which was developed at Bell Laboratories (formerly AT&T, now Lucent -Technologies) by John Chambers and colleagues. R can be considered as a -different implementation of S. There are some important differences, but -much code written for S runs unaltered under R. - -(taken from http://www.r-project.org/) - -== License == - * GPL v2 or GPL v3 - -== Upstream Contact == - * R mailing list, #R in IRC - -== Dependencies == - - * GNU patch - * iconv - * Readline - * BLAS/LAPACK - * xz - * pcre - * curl - * https-capable SSL - diff --git a/build/pkgs/r/checksums.ini b/build/pkgs/r/checksums.ini index b97750a51de..69a1a7059dc 100644 --- a/build/pkgs/r/checksums.ini +++ b/build/pkgs/r/checksums.ini @@ -1,4 +1,5 @@ tarball=R-VERSION.tar.gz -sha1=8eda2af51d63877fcc6674274b6801af605173c5 -md5=90d23d138cee26d275da14b58296e521 -cksum=1222866983 +sha1=d2383dabc0d6c70f8a0171a0fb1bfdc31ddb5b52 +md5=506c9576ba33e1262ad5b5624db9d96a +cksum=2403187565 +upstream_url=https://cran.r-project.org/src/base/R-3/R-VERSION.tar.gz diff --git a/build/pkgs/r/distros/arch.txt b/build/pkgs/r/distros/arch.txt new file mode 100644 index 00000000000..4286f428e3b --- /dev/null +++ b/build/pkgs/r/distros/arch.txt @@ -0,0 +1 @@ +r diff --git a/build/pkgs/r/distros/conda.txt b/build/pkgs/r/distros/conda.txt new file mode 100644 index 00000000000..42e26b8d8ed --- /dev/null +++ b/build/pkgs/r/distros/conda.txt @@ -0,0 +1 @@ +r r-essentials diff --git a/build/pkgs/r/distros/cygwin.txt b/build/pkgs/r/distros/cygwin.txt new file mode 100644 index 00000000000..f532411f70a --- /dev/null +++ b/build/pkgs/r/distros/cygwin.txt @@ -0,0 +1,2 @@ +R +libtirpc-devel # needed for building extensions diff --git a/build/pkgs/r/distros/debian.txt b/build/pkgs/r/distros/debian.txt new file mode 100644 index 00000000000..261aefd2bd8 --- /dev/null +++ b/build/pkgs/r/distros/debian.txt @@ -0,0 +1,2 @@ +r-base-dev +r-cran-lattice # 29471 diff --git a/build/pkgs/r/distros/fedora.txt b/build/pkgs/r/distros/fedora.txt new file mode 100644 index 00000000000..a5148a636ee --- /dev/null +++ b/build/pkgs/r/distros/fedora.txt @@ -0,0 +1 @@ +R R-devel diff --git a/build/pkgs/r/distros/gentoo.txt b/build/pkgs/r/distros/gentoo.txt new file mode 100644 index 00000000000..124befdcc72 --- /dev/null +++ b/build/pkgs/r/distros/gentoo.txt @@ -0,0 +1 @@ +dev-lang/R diff --git a/build/pkgs/r/distros/homebrew.txt b/build/pkgs/r/distros/homebrew.txt new file mode 100644 index 00000000000..4286f428e3b --- /dev/null +++ b/build/pkgs/r/distros/homebrew.txt @@ -0,0 +1 @@ +r diff --git a/build/pkgs/r/package-version.txt b/build/pkgs/r/package-version.txt index e4097539c1a..4a788a01dad 100644 --- a/build/pkgs/r/package-version.txt +++ b/build/pkgs/r/package-version.txt @@ -1 +1 @@ -3.6.2.p0 +3.6.3 diff --git a/build/pkgs/r/spkg-check b/build/pkgs/r/spkg-check deleted file mode 100644 index 28144ca77ff..00000000000 --- a/build/pkgs/r/spkg-check +++ /dev/null @@ -1,12 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting"; - exit 1 -fi - -cd src -$MAKE check -if [ $? -ne 0 ]; then - echo >&2 "Error while running the R testsuite ... exiting" - exit 1 -fi - diff --git a/build/pkgs/r/spkg-check.in b/build/pkgs/r/spkg-check.in new file mode 100644 index 00000000000..27cd9419538 --- /dev/null +++ b/build/pkgs/r/spkg-check.in @@ -0,0 +1,2 @@ +cd src +$MAKE check 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/r/spkg-install b/build/pkgs/r/spkg-install deleted file mode 100644 index 0e115d142db..00000000000 --- a/build/pkgs/r/spkg-install +++ /dev/null @@ -1,117 +0,0 @@ -# r uses grep output during its build -unset GREP_OPTIONS - -# Make sure CPPFLAGS and LDFLAGS are set to something (even the empty -# string) to prevent R from overriding them. Note: LDFLAGS is set by default. -# The --with-readline configure option only understands yes/no -# and cannot be used to pass this path. -CPPFLAGS="$CPPFLAGS" - -# Optimization flags -if [ "$SAGE_DEBUG" = yes ]; then - CFLAGS="-g -O0 $CFLAGS" - FCFLAGS="-g -O0 $FCFLAGS" -else - CFLAGS="-g -O2 $CFLAGS" - FCFLAGS="-g -O2 $FCFLAGS" -fi - -export CFLAGS CPPFLAGS FCFLAGS LDFLAGS - -if [ "$UNAME" = "Darwin" ]; then - # Put fake java on the PATH for OSX - export PATH="$(pwd)/bin:$PATH" -fi - -# Note by Karl-Dieter Crisman, April 12th 2010. X support would be nice -# to have in OSX, but see -# http://CRAN.R-project.org/bin/macosx/RMacOSX-FAQ.html#X11-window-server-_0028optional_0029 -# for how differently this would have to be handled on different OSX -# versions, none trivially. In any case, aqua, which we enable, -# performs the same function on OSX. -# -# Also, see #12172: for now, anyway, we disable X support on OS X. -# -# Note by David Kirkby, Feb 16th 2010. /usr/include/X11/Xwindows.h does -# not exist on Solaris, but R configures OK with X support. Hence I've added -# a more specific test on Solaris, by testing for a library. That library -# exists both on Solaris 10 03/2005 (SPARC) and on OpenSolaris. -if [ "$UNAME" = "Darwin" ]; then - XSUPPORT=no -elif [ -f /usr/include/X11/Xwindows.h ]; then - XSUPPORT=yes -elif [ "$UNAME" = "SunOS" ] && [ -f /usr/X11/lib/libXv.so ] ; then - XSUPPORT=yes -else - XSUPPORT=no -fi - -R_CONFIGURE_BLAS="--with-blas=$(pkg-config --libs blas)" -R_CONFIGURE_LAPACK="--with-lapack=$(pkg-config --libs lapack)" -echo "R_CONFIGURE_BLAS=$R_CONFIGURE_BLAS" -echo "R_CONFIGURE_LAPACK=$R_CONFIGURE_LAPACK" - -if [ "$UNAME" = "Darwin" ]; then - # We don't want to install R as a library framework on OSX - R_CONFIGURE="--enable-R-framework=no $R_CONFIGURE" - # OS X 10.10 and/or Xcode 6.3 and over broke the R installation. See - # http://trac.sagemath.org/ticket/18254. - if [ $MACOSX_VERSION -ge 14 ]; then - echo "OS X 10.$[$MACOSX_VERSION-4] Configuring R without aqua support." - R_CONFIGURE="--with-aqua=no $R_CONFIGURE" - fi - if [ $MACOSX_VERSION -ge 16 ]; then - echo "OS X 10.$[$MACOSX_VERSION-4] Building with clang." - CC=clang - fi -fi - -if [ "$SAGE_FAT_BINARY" = yes ]; then - echo "Disabling ICU, OpenMP for a binary build" - R_CONFIGURE="--without-ICU --disable-openmp $R_CONFIGURE" -elif [ "$UNAME" = "SunOS" ]; then - # Note by David Kirkby, 16th Feb 2010. Even after adding the iconv library - # R would not build properly on Solaris 10, complaining of undefined symbols - # uiter_setUTF8 and ucol_strcollIter - # After an email to r-help@r-project.org, Ei-ji Nakama (rim.nakama@gmail.com) - # emailed me and said the option --without-ICU might help, which it did. I don't see - # this option documented, but for now at least, it does allow R to build. - - echo "Disabling ICU on Solaris, using an undocumented option --without-ICU" - echo "since the ICU library is not included on Solaris." - R_CONFIGURE="--without-ICU $R_CONFIGURE" -fi - -cd src - -if [ "$UNAME" = "Darwin" ]; then - # Fixing install_name(s) - sed -i -e 's:\"-install_name :\"-install_name ${libdir}/R/lib/:' configure - sed -i -e "/SHLIB_EXT/s/\.so/.dylib/" configure -fi - -# Don't override R_HOME_DIR in local/bin/R while building R. -# See patches/R.sh.patch -export SAGE_BUILDING_R=yes - -config() { - sdh_configure --enable-R-shlib --with-recommended-packages \ - --with-readline=yes --with-x=$XSUPPORT \ - "$R_CONFIGURE_BLAS" "$R_CONFIGURE_LAPACK" \ - $R_CONFIGURE -} - -if ! (config); then - echo "Configuring R without X11" - export XSUPPORT=no - config -fi - -# Build R -sdh_make R - -# needed for help system -sdh_make vignettes - -# Install new version -sdh_make_install diff --git a/build/pkgs/r/spkg-install.in b/build/pkgs/r/spkg-install.in new file mode 100644 index 00000000000..905fc2a6150 --- /dev/null +++ b/build/pkgs/r/spkg-install.in @@ -0,0 +1,131 @@ +# r uses grep output during its build +unset GREP_OPTIONS + +# Make sure CPPFLAGS and LDFLAGS are set to something (even the empty +# string) to prevent R from overriding them. Note: LDFLAGS is set by default. +# The --with-readline configure option only understands yes/no +# and cannot be used to pass this path. +CPPFLAGS="$CPPFLAGS" + +# Optimization flags +if [ "$SAGE_DEBUG" = yes ]; then + CFLAGS="-g -O0 $CFLAGS" + FCFLAGS="-g -O0 $FCFLAGS" +else + CFLAGS="-g -O2 $CFLAGS" + FCFLAGS="-g -O2 $FCFLAGS" +fi +# #29170: Compilation errors caused by a silently failing configure check +# "for type of 'hidden' Fortran character lengths" +# on ubuntu-bionic-minimal, ubuntu-eoan/focal-minimal, debian-buster/bullseye/sid-minimal, +# linuxmint-19.3-minimal, archlinux-latest-minimal +CFLAGS="$CFLAGS -fPIC" +FCFLAGS="$FCFLAGS -fPIC" + +export CFLAGS CPPFLAGS FCFLAGS LDFLAGS + +if [ "$UNAME" = "Darwin" ]; then + # Put fake java on the PATH for OSX + export PATH="$(pwd)/bin:$PATH" +fi + +# Note by Karl-Dieter Crisman, April 12th 2010. X support would be nice +# to have in OSX, but see +# http://CRAN.R-project.org/bin/macosx/RMacOSX-FAQ.html#X11-window-server-_0028optional_0029 +# for how differently this would have to be handled on different OSX +# versions, none trivially. In any case, aqua, which we enable, +# performs the same function on OSX. +# +# Also, see #12172: for now, anyway, we disable X support on OS X. +# +# Note by David Kirkby, Feb 16th 2010. /usr/include/X11/Xwindows.h does +# not exist on Solaris, but R configures OK with X support. Hence I've added +# a more specific test on Solaris, by testing for a library. That library +# exists both on Solaris 10 03/2005 (SPARC) and on OpenSolaris. +if [ "$UNAME" = "Darwin" ]; then + XSUPPORT=no +elif [ -f /usr/include/X11/Xwindows.h ]; then + XSUPPORT=yes +elif [ "$UNAME" = "SunOS" ] && [ -f /usr/X11/lib/libXv.so ] ; then + XSUPPORT=yes +else + XSUPPORT=no +fi + +R_CONFIGURE_BLAS="--with-blas=$(pkg-config --libs blas)" +R_CONFIGURE_LAPACK="--with-lapack=$(pkg-config --libs lapack)" +echo "R_CONFIGURE_BLAS=$R_CONFIGURE_BLAS" +echo "R_CONFIGURE_LAPACK=$R_CONFIGURE_LAPACK" + +if [ "$UNAME" = "Darwin" ]; then + # We don't want to install R as a library framework on OSX + R_CONFIGURE="--enable-R-framework=no $R_CONFIGURE" + # OS X 10.10 and/or Xcode 6.3 and over broke the R installation. See + # http://trac.sagemath.org/ticket/18254. + if [ $MACOSX_VERSION -ge 14 ]; then + echo "OS X 10.$[$MACOSX_VERSION-4] Configuring R without aqua support." + R_CONFIGURE="--with-aqua=no $R_CONFIGURE" + fi + if [ $MACOSX_VERSION -ge 16 ]; then + echo "OS X 10.$[$MACOSX_VERSION-4] Building with clang." + CC=clang + fi +fi + +if [ "$SAGE_FAT_BINARY" = yes ]; then + echo "Disabling ICU, OpenMP for a binary build" + R_CONFIGURE="--without-ICU --disable-openmp $R_CONFIGURE" +elif [ "$UNAME" = "SunOS" ]; then + # Note by David Kirkby, 16th Feb 2010. Even after adding the iconv library + # R would not build properly on Solaris 10, complaining of undefined symbols + # uiter_setUTF8 and ucol_strcollIter + # After an email to r-help@r-project.org, Ei-ji Nakama (rim.nakama@gmail.com) + # emailed me and said the option --without-ICU might help, which it did. I don't see + # this option documented, but for now at least, it does allow R to build. + + echo "Disabling ICU on Solaris, using an undocumented option --without-ICU" + echo "since the ICU library is not included on Solaris." + R_CONFIGURE="--without-ICU $R_CONFIGURE" +fi + +cd src + +if [ "$UNAME" = "Darwin" ]; then + # Fixing install_name(s) + sed -i -e 's:\"-install_name :\"-install_name ${libdir}/R/lib/:' configure + sed -i -e "/SHLIB_EXT/s/\.so/.dylib/" configure +fi + +# Don't override R_HOME_DIR in local/bin/R while building R. +# See patches/R.sh.patch +export SAGE_BUILDING_R=yes + +R_HOME="$SAGE_LOCAL"/lib/R +# Set LDFLAGS as it is done in sage-env for $SAGE_LOCAL/lib +LDFLAGS="-L$R_HOME/lib -Wl,-rpath,$R_HOME/lib $LDFLAGS" +if [ "$UNAME" = "Linux" ]; then + LDFLAGS="-Wl,-rpath-link,$R_HOME/lib $LDFLAGS" +fi +export LDFLAGS + +config() { + sdh_configure --enable-R-shlib --with-recommended-packages \ + --with-readline=yes --with-x=$XSUPPORT \ + "$R_CONFIGURE_BLAS" "$R_CONFIGURE_LAPACK" \ + $R_CONFIGURE +} + +if ! (config); then + echo "Configuring R without X11" + export XSUPPORT=no + config +fi + +# Build R +sdh_make R + +# needed for help system +sdh_make vignettes + +# Install new version +sdh_make_install diff --git a/build/pkgs/r/spkg-legacy-uninstall b/build/pkgs/r/spkg-legacy-uninstall.in similarity index 100% rename from build/pkgs/r/spkg-legacy-uninstall rename to build/pkgs/r/spkg-legacy-uninstall.in diff --git a/build/pkgs/r/spkg-src b/build/pkgs/r/spkg-src index 349ec3e5846..23ce98c4702 100755 --- a/build/pkgs/r/spkg-src +++ b/build/pkgs/r/spkg-src @@ -10,4 +10,4 @@ set -e wget http://cran.r-project.org/src/base/R-3/$SOURCE_TARBALL mv $SOURCE_TARBALL $SAGE_ROOT/upstream/$TARGET_TARBALL -sage -sh 'sage-fix-pkg-checksums' +sage --package fix-checksum diff --git a/build/pkgs/r_jupyter/SPKG.rst b/build/pkgs/r_jupyter/SPKG.rst new file mode 100644 index 00000000000..e48f758f0a8 --- /dev/null +++ b/build/pkgs/r_jupyter/SPKG.rst @@ -0,0 +1,26 @@ +r_jupyter +========= + +Description +----------- + +This package installs IRkernel, the R Jupyter kernel. + +It gets installed via R's package installer on top of Jupyter. + +License +------- + +MIT + +Upstream Contact +---------------- + +- https://github.com/IRkernel/IRkernel +- https://irkernel.github.io/ + +Dependencies +------------ + +- R +- notebook diff --git a/build/pkgs/r_jupyter/dependencies b/build/pkgs/r_jupyter/dependencies new file mode 100644 index 00000000000..981f46753f0 --- /dev/null +++ b/build/pkgs/r_jupyter/dependencies @@ -0,0 +1 @@ +notebook r \ No newline at end of file diff --git a/build/pkgs/r_jupyter/type b/build/pkgs/r_jupyter/type index 84f7e31d99b..134d9bc32d5 100644 --- a/build/pkgs/r_jupyter/type +++ b/build/pkgs/r_jupyter/type @@ -1 +1 @@ -script +optional diff --git a/build/pkgs/ratpoints/SPKG.rst b/build/pkgs/ratpoints/SPKG.rst new file mode 100644 index 00000000000..20ff846959f --- /dev/null +++ b/build/pkgs/ratpoints/SPKG.rst @@ -0,0 +1,39 @@ +ratpoints +========= + +Description +----------- + +Michael Stoll's program which searches for rational points on +hyperelliptic curves. + +NOTE: the ratpoints package has been assimilated by PARI/GP. Therefore, +this package (as Sage package) is deprecated. In the future, it will be +removed from Sage. + + +Upstream Contact +---------------- + +- Author: Michael Stoll +- Email: Michael.Stoll@uni-bayreuth.de +- Website: http://www.mathe2.uni-bayreuth.de/stoll/ + +Dependencies +------------ + +- GMP/MPIR +- (GNU) patch + + +Special Update/Build Instructions +--------------------------------- + + +Note on SSE2 instructions +~~~~~~~~~~~~~~~~~~~~~~~~~ + +- On several architectures, the SSE2 instructions used by ratpoints + cause + compiler errors. In the case that ratpoints fails to build with SSE2 + instructions enabled, the build is repeated with SSE2 disabled. diff --git a/build/pkgs/ratpoints/SPKG.txt b/build/pkgs/ratpoints/SPKG.txt deleted file mode 100644 index fed925f9bba..00000000000 --- a/build/pkgs/ratpoints/SPKG.txt +++ /dev/null @@ -1,72 +0,0 @@ -= ratpoints = - -== Description == - -Michael Stoll's program which searches for rational points on hyperelliptic -curves. - -NOTE: the ratpoints package has been assimilated by PARI/GP. Therefore, -this package (as Sage package) is deprecated. In the future, it will be -removed from Sage. - -== Upstream Contact == - - * Author: Michael Stoll - * Email: Michael.Stoll@uni-bayreuth.de - * Website: http://www.mathe2.uni-bayreuth.de/stoll/ - -== Dependencies == - - * GMP/MPIR - * (GNU) patch - -== Special Update/Build Instructions == - -=== Note on SSE2 instructions === - - * On several architectures, the SSE2 instructions used by ratpoints cause - compiler errors. In the case that ratpoints fails to build with SSE2 - instructions enabled, the build is repeated with SSE2 disabled. - -== Changelog == - -=== ratpoints-2.1.3.p3 (Leif Leonhardy, March 17th 2012) === - * #12682: Patch `Makefile` such that the `CC` (and `INSTALL_DIR`) environment - variable(s) override(s) the setting in the Makefile. - * Some clean-up; use `$MAKE` instead of `make`, also install the library - with `make` (i.e., `$MAKE`) rather than "by hand". - * TODO: - - The Makefile has a `test` target; don't know whether we should - run some tests, and whether it's worth an `spkg-check` script (for - which we'd presumably have to duplicate the whole `CCFLAGS*` setup). - -=== ratpoints-2.1.3.p2 (Jeroen Demeyer, 13 February 2012) === - * #12368: Add compiler flag -fnested-functions if and only if it is - supported by the C compiler. - * Copy $CFLAGS (which is the standard variable for this) to $CCFLAGS - (which is what ratpoints uses). - -=== ratpoints-2.1.3.p1 (Jaap Spies, 24 Feb 2010) === - * #8351 make ratpoints build 64 bit for Open Solaris x64 with SAGE64=yes - -=== ratpoints-2.1.3.p0 (William Stein, 14 Feb 2010) === - * Include change to spkg-install so that build works on Cygwin, - a fix that was in (trac #7015), and somehow got lost. See - trac #8267. - -=== ratpoints-2.1.3 (William Stein, 14 Feb 2010) === - * Evidently somebody updated ratpoints to 2.1.3 and didn't - update the SPKG.txt. Oops. See below! jsp - -=== ratpoints-2.1.2.p1 (William Stein, 8 July 2009) === - * add hgignore - * fix mistake in spkg-install (!= versus -ne) - * make work on 64-bit OS X - -=== ratpoints-2.1.2 (Robert Miller, 22 April 2009) === - * Initial spkg - * Work around SSE2 build issues by disabling on fail - * Helped Michael Stoll fix several memory leaks. - -=== ratpoints-2.1.3 (Robert Miller, 30 September 2009) === - * Updated to 2.1.3 diff --git a/build/pkgs/ratpoints/spkg-install b/build/pkgs/ratpoints/spkg-install.in similarity index 100% rename from build/pkgs/ratpoints/spkg-install rename to build/pkgs/ratpoints/spkg-install.in diff --git a/build/pkgs/readline/SPKG.rst b/build/pkgs/readline/SPKG.rst new file mode 100644 index 00000000000..8553446ae64 --- /dev/null +++ b/build/pkgs/readline/SPKG.rst @@ -0,0 +1,50 @@ +readline +======== + +Description +----------- + +The GNU Readline library provides a set of functions for use by +applications that allow users to edit command lines as they are typed +in. Both Emacs and vi editing modes are available. The Readline library +includes additional functions to maintain a list of previously-entered +command lines, to recall and perhaps reedit those lines, and perform +csh-like history expansion on previous commands. + +Website: http://tiswww.case.edu/php/chet/readline/rltop.html + +License +------- + +- GPL V3+ + + +Upstream Contact +---------------- + +- Chet Ramey at http://cnswww.cns.cwru.edu/~chet + +Dependencies +------------ + +- ncurses + + +Special Update/Build Instructions +--------------------------------- + +We build readline using ncurses. Readline needs to be told to link with +libtinfo (part of ncurses), this is what the patch 0002-ltinfo.patch +does. + +Patches +------- + +- 0001-macports.patch: Changes to shobj.conf for OS/X, from macports: + + https://trac.macports.org/browser/trunk/dports/devel/readline/files/patch-shobj-conf.diff + +- 0002-ltinfo.patch: We build readline using ncurses, and for that it + needs to be told to link with libtinfo (part of ncurses). + +- sigsetjmp.patch: Correctly define sigsetjmp and friends on Cygwin. diff --git a/build/pkgs/readline/SPKG.txt b/build/pkgs/readline/SPKG.txt deleted file mode 100644 index 800f7767002..00000000000 --- a/build/pkgs/readline/SPKG.txt +++ /dev/null @@ -1,39 +0,0 @@ -= readline = - -== Description == - -The GNU Readline library provides a set of functions for use by -applications that allow users to edit command lines as they are typed -in. Both Emacs and vi editing modes are available. The Readline -library includes additional functions to maintain a list of -previously-entered command lines, to recall and perhaps reedit those -lines, and perform csh-like history expansion on previous commands. - -Website: http://tiswww.case.edu/php/chet/readline/rltop.html - -== License == - - * GPL V3+ - -== Upstream Contact == - - * Chet Ramey at http://cnswww.cns.cwru.edu/~chet - -== Dependencies == - - * ncurses - -== Special Update/Build Instructions == - -We build readline using ncurses. Readline needs to be told to link -with libtinfo (part of ncurses), this is what the patch -0002-ltinfo.patch does. - - -== Patches == - - * 0001-macports.patch: Changes to shobj.conf for OS/X, from macports: - https://trac.macports.org/browser/trunk/dports/devel/readline/files/patch-shobj-conf.diff - * 0002-ltinfo.patch: We build readline using ncurses, and for that it - needs to be told to link with libtinfo (part of ncurses). - * sigsetjmp.patch: Correctly define sigsetjmp and friends on Cygwin. diff --git a/build/pkgs/readline/distros/arch.txt b/build/pkgs/readline/distros/arch.txt new file mode 100644 index 00000000000..0b5a58e278a --- /dev/null +++ b/build/pkgs/readline/distros/arch.txt @@ -0,0 +1 @@ +readline diff --git a/build/pkgs/readline/distros/cygwin.txt b/build/pkgs/readline/distros/cygwin.txt new file mode 100644 index 00000000000..1698f0e86b8 --- /dev/null +++ b/build/pkgs/readline/distros/cygwin.txt @@ -0,0 +1 @@ +libreadline-devel diff --git a/build/pkgs/readline/distros/homebrew.txt b/build/pkgs/readline/distros/homebrew.txt new file mode 100644 index 00000000000..0b5a58e278a --- /dev/null +++ b/build/pkgs/readline/distros/homebrew.txt @@ -0,0 +1 @@ +readline diff --git a/build/pkgs/readline/distros/slackware.txt b/build/pkgs/readline/distros/slackware.txt new file mode 100644 index 00000000000..0b5a58e278a --- /dev/null +++ b/build/pkgs/readline/distros/slackware.txt @@ -0,0 +1 @@ +readline diff --git a/build/pkgs/readline/spkg-check b/build/pkgs/readline/spkg-check.in similarity index 100% rename from build/pkgs/readline/spkg-check rename to build/pkgs/readline/spkg-check.in diff --git a/build/pkgs/readline/spkg-configure.m4 b/build/pkgs/readline/spkg-configure.m4 index 4d82c6d77be..68aab0a36ad 100644 --- a/build/pkgs/readline/spkg-configure.m4 +++ b/build/pkgs/readline/spkg-configure.m4 @@ -11,7 +11,7 @@ SAGE_SPKG_CONFIGURE([readline], [ [AC_CHECK_HEADERS([readline/readline.h], dnl rl_bind_keyseq is not present in macos's readline dnl and is not present in readline version 4 (the one in OpenBSD) - [AC_SEARCH_LIBS([rl_bind_keyseq], [readline], [break], + [AC_SEARCH_LIBS([rl_bind_keyseq], [readline], [], [sage_spkg_install_readline=yes])], [sage_spkg_install_readline=yes])], [sage_spkg_install_readline=yes]) diff --git a/build/pkgs/readline/spkg-install b/build/pkgs/readline/spkg-install.in similarity index 100% rename from build/pkgs/readline/spkg-install rename to build/pkgs/readline/spkg-install.in diff --git a/build/pkgs/requests/SPKG.rst b/build/pkgs/requests/SPKG.rst new file mode 100644 index 00000000000..f06fc13952f --- /dev/null +++ b/build/pkgs/requests/SPKG.rst @@ -0,0 +1,8 @@ +requests +======== + +Description +----------- + +Requests is the only Non-GMO HTTP library for Python, safe for human +consumption. diff --git a/build/pkgs/requests/SPKG.txt b/build/pkgs/requests/SPKG.txt deleted file mode 100644 index 5f5a73a5fc2..00000000000 --- a/build/pkgs/requests/SPKG.txt +++ /dev/null @@ -1,6 +0,0 @@ -= requests = - -== Description == - -Requests is the only Non-GMO HTTP library for Python, safe for human -consumption. diff --git a/build/pkgs/requests/dependencies b/build/pkgs/requests/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/requests/dependencies +++ b/build/pkgs/requests/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/prometheus_client/spkg-install b/build/pkgs/requests/spkg-install.in similarity index 100% rename from build/pkgs/prometheus_client/spkg-install rename to build/pkgs/requests/spkg-install.in diff --git a/build/pkgs/rpy2/SPKG.rst b/build/pkgs/rpy2/SPKG.rst new file mode 100644 index 00000000000..b3391ea8de7 --- /dev/null +++ b/build/pkgs/rpy2/SPKG.rst @@ -0,0 +1,38 @@ +rpy2 +==== + +Description +----------- + +rpy2 is a redesign and rewrite of rpy. It is providing a low-level +interface to R, a proposed high-level interface, including wrappers to +graphical libraries, as well as R-like structures and functions. + +Website: http://rpy.sourceforge.net/rpy2.html + +License +------- + +- GPL 2+ +- Note that we have deleted references to Mozilla PL as an option, + which we are allowed to do by the full rpy2 license in order to + remain GPL-compatible + + +Upstream Contact +---------------- + +- http://rpy.sourceforge.net/maillist.html + +Dependencies +------------ + + +Special Update/Build Instructions +--------------------------------- + +Patches +~~~~~~~ + +- setup.patch: takes care of a few parsing issues. +- cygwin.patch: let rpy2 build on Cygwin. diff --git a/build/pkgs/rpy2/SPKG.txt b/build/pkgs/rpy2/SPKG.txt deleted file mode 100644 index 9c78b19257c..00000000000 --- a/build/pkgs/rpy2/SPKG.txt +++ /dev/null @@ -1,32 +0,0 @@ -= rpy2 = - -== Description == -rpy2 is a redesign and rewrite of rpy. It is providing a low-level -interface to R, a proposed high-level interface, including wrappers to -graphical libraries, as well as R-like structures and functions. - -Website: http://rpy.sourceforge.net/rpy2.html - -== License == - * GPL 2+ - * Note that we have deleted references to Mozilla PL as an option, which we are allowed to do by the full rpy2 license in order to remain GPL-compatible - -== Upstream Contact == - * http://rpy.sourceforge.net/maillist.html - -== Dependencies == - -== Special Update/Build Instructions == - -=== Patches === - * setup.patch: takes care of a few parsing issues. - * cygwin.patch: let rpy2 build on Cygwin. - -== Changelog == - -=== rpy2-2.7.4 (Emmanuel Charpentier, December 1st, 2015) === - * Trac #19638 : upgrade to allow interaction with Jupyter notebook - * Updated cygwin.patch to add "|| #defined(__CyGWIN__)" to ALL occurrences - of "#defined(Win32) || #defined(Win64)". This should be reviewed by - someone who knows what (he|she)'s doing with Cygwin (I don't). - * Removed setup.patch, which as no target now. diff --git a/build/pkgs/rpy2/checksums.ini b/build/pkgs/rpy2/checksums.ini index c6670edbb27..9b7f0ea2ec3 100644 --- a/build/pkgs/rpy2/checksums.ini +++ b/build/pkgs/rpy2/checksums.ini @@ -1,4 +1,5 @@ tarball=rpy2-VERSION.tar.gz -sha1=24f7c538a734618dfcf2343304729c0b1e429717 -md5=729946166b43103db7697e69db8c5c45 -cksum=3858682977 +sha1=af1c7ffe900233ac5add5195f106299bd72fd575 +md5=8a5db07798763b9c6672b5f80451b944 +cksum=828149660 +upstream_url=https://pypi.io/packages/source/r/rpy2/rpy2-VERSION.tar.gz diff --git a/build/pkgs/rpy2/dependencies b/build/pkgs/rpy2/dependencies index e02edd2f9e4..215375ee02d 100644 --- a/build/pkgs/rpy2/dependencies +++ b/build/pkgs/rpy2/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) r six singledispatch | pip +$(PYTHON) r cffi tzlocal pytz | $(PYTHON_TOOLCHAIN) pycparser $(and $(filter-out no,$(SAGE_CHECK_rpy2)), pytest numpy) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/rpy2/package-version.txt b/build/pkgs/rpy2/package-version.txt index 4b18da28f6e..fa7adc7ac72 100644 --- a/build/pkgs/rpy2/package-version.txt +++ b/build/pkgs/rpy2/package-version.txt @@ -1 +1 @@ -2.8.2.p1 +3.3.5 diff --git a/build/pkgs/rpy2/patches/716.patch b/build/pkgs/rpy2/patches/716.patch new file mode 100644 index 00000000000..8446b281633 --- /dev/null +++ b/build/pkgs/rpy2/patches/716.patch @@ -0,0 +1,89 @@ +From 87d0f82e2f4be94893881913018ca9085c0ff8e5 Mon Sep 17 00:00:00 2001 +From: Matthias Koeppe +Date: Fri, 3 Jul 2020 12:47:41 -0700 +Subject: [PATCH] setup.py: Print CFFI configuration messages only on build + +--- + setup.py | 44 ++++++++++++++++++++++++++------------------ + 1 file changed, 26 insertions(+), 18 deletions(-) + +diff --git a/setup.py b/setup.py +index e4337838..7fead893 100755 +--- a/setup.py ++++ b/setup.py +@@ -21,6 +21,7 @@ + from rpy2 import situation + + from setuptools import setup ++from distutils.command.build import build as du_build + + PACKAGE_NAME = 'rpy2' + pack_version = __import__('rpy2').__version__ +@@ -111,7 +112,6 @@ def get_r_c_extension_status(): + + + cffi_mode = situation.get_cffi_mode() +-print('cffi mode: %s' % cffi_mode) + c_extension_status = get_r_c_extension_status() + if cffi_mode == situation.CFFI_MODE.ABI: + cffi_modules = ['rpy2/_rinterface_cffi_build.py:ffibuilder_abi'] +@@ -135,6 +135,30 @@ def get_r_c_extension_status(): + # This should never happen. + raise ValueError('Invalid value for cffi_mode') + ++class build(du_build): ++ ++ def run(self): ++ print('cffi mode: %s' % cffi_mode) ++ ++ du_build.run(self) ++ ++ print('---') ++ print(cffi_mode) ++ if cffi_mode in (situation.CFFI_MODE.ABI, ++ situation.CFFI_MODE.BOTH, ++ situation.CFFI_MODE.ANY): ++ print('ABI mode interface built.') ++ if cffi_mode in (situation.CFFI_MODE.API, ++ situation.CFFI_MODE.BOTH): ++ print('API mode interface built.') ++ if cffi_mode == situation.CFFI_MODE.ANY: ++ if c_extension_status == COMPILATION_STATUS.OK: ++ print('API mode interface built.') ++ else: ++ print('API mode interface not built because: %s' % c_extension_status) ++ print('To change the API/ABI build mode, set or modify the environment ' ++ 'variable RPY2_CFFI_MODE.') ++ + LONG_DESCRIPTION = """ + Python interface to the R language. + +@@ -168,6 +192,7 @@ def get_r_c_extension_status(): + install_requires=requires + ['cffi>=1.10.0'], + setup_requires=['cffi>=1.10.0'], + cffi_modules=cffi_modules, ++ cmdclass = dict(build=build), + package_dir=pack_dir, + packages=([PACKAGE_NAME] + + ['{pack_name}.{x}'.format(pack_name=PACKAGE_NAME, x=x) +@@ -193,20 +218,3 @@ def get_r_c_extension_status(): + package_data={'rpy2': ['rinterface_lib/R_API.h', + 'rinterface_lib/R_API_eventloop.h']} + ) +- +- print('---') +- print(cffi_mode) +- if cffi_mode in (situation.CFFI_MODE.ABI, +- situation.CFFI_MODE.BOTH, +- situation.CFFI_MODE.ANY): +- print('ABI mode interface built and installed.') +- if cffi_mode in (situation.CFFI_MODE.API, +- situation.CFFI_MODE.BOTH): +- print('API mode interface built and installed.') +- if cffi_mode == situation.CFFI_MODE.ANY: +- if c_extension_status == COMPILATION_STATUS.OK: +- print('API mode interface built and installed.') +- else: +- print('API mode interface not build because: %s' % c_extension_status) +- print('To change the API/ABI build mode, set or modify the environment ' +- 'variable RPY2_CFFI_MODE.') diff --git a/build/pkgs/rpy2/patches/cygwin.patch b/build/pkgs/rpy2/patches/cygwin.patch deleted file mode 100644 index d2088e419e1..00000000000 --- a/build/pkgs/rpy2/patches/cygwin.patch +++ /dev/null @@ -1,108 +0,0 @@ -diff -ru rpy2-2.7.4.orig/rpy/rinterface/na_values.c rpy2-2.7.4/rpy/rinterface/na_values.c ---- rpy2-2.7.4.orig/rpy/rinterface/na_values.c 2015-09-05 01:15:16.000000000 +0200 -+++ rpy2-2.7.4/rpy/rinterface/na_values.c 2015-12-01 11:46:40.675932500 +0100 -@@ -203,7 +203,7 @@ - 0, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ --#if defined(Win32) || defined(Win64) -+#if defined(Win32) || defined(Win64) || defined(__CYGWIN__) - NULL, - #else - &PyLong_Type, /*tp_base*/ -@@ -401,7 +401,7 @@ - 0, //NAInteger_methods, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ --#if defined(Win32) || defined(Win64) -+#if defined(Win32) || defined(Win64) || defined(__CYGWIN__) - NULL, - #else - &PyLong_Type, /*tp_base*/ -@@ -587,7 +587,7 @@ - 0, //NAInteger_methods, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ --#if defined(Win32) || defined(Win64) -+#if defined(Win32) || defined(Win64) || defined(__CYGWIN__) - NULL, - #else - &PyFloat_Type, /*tp_base*/ -@@ -689,7 +689,7 @@ - 0, //NAInteger_methods, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ --#if defined(Win32) || defined(Win64) -+#if defined(Win32) || defined(Win64) || defined(__CYGWIN__) - NULL, - #elif (PY_VERSION_HEX < 0x03010000) - &PyString_Type, /*tp_base*/ -@@ -856,7 +856,7 @@ - 0, //NAInteger_methods, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ --#if defined(Win32) || defined(Win64) -+#if defined(Win32) || defined(Win64) || defined(__CYGWIN__) - NULL, - #else - &PyComplex_Type, /*tp_base*/ -diff -ru rpy2-2.7.4.orig/rpy/rinterface/_rinterface.c rpy2-2.7.4/rpy/rinterface/_rinterface.c ---- rpy2-2.7.4.orig/rpy/rinterface/_rinterface.c 2015-09-12 23:23:55.000000000 +0200 -+++ rpy2-2.7.4/rpy/rinterface/_rinterface.c 2015-12-01 11:45:53.163820335 +0100 -@@ -1162,7 +1162,7 @@ - "and R_runHandlers (on other platforms)."); - - --#if defined(Win32) || defined(Win64) -+#if defined(Win32) || defined(Win64) || defined(__CYGWIN__) - void win32CallBack() - { - /* called during i/o, eval, graphics in ProcessEvents */ -@@ -3749,7 +3749,7 @@ - } - - /* NA types */ --#if defined(Win32) || defined(Win64) -+#if defined(Win32) || defined(Win64) || defined(__CYGWIN__) - NAInteger_Type.tp_base=&PyLong_Type; - #endif - if (PyType_Ready(&NAInteger_Type) < 0) { -@@ -3759,7 +3759,7 @@ - return NULL; - #endif - } --#if defined(Win32) || defined(Win64) -+#if defined(Win32) || defined(Win64) || defined(__CYGWIN__) - NALogical_Type.tp_base=&PyLong_Type; - #endif - if (PyType_Ready(&NALogical_Type) < 0) { -@@ -3769,7 +3769,7 @@ - return NULL; - #endif - } --#if defined(Win32) || defined(Win64) -+#if defined(Win32) || defined(Win64) || defined(__CYGWIN__) - NAReal_Type.tp_base=&PyFloat_Type; - #endif - if (PyType_Ready(&NAReal_Type) < 0) { -@@ -3779,7 +3779,7 @@ - return NULL; - #endif - } --#if defined(Win32) || defined(Win64) -+#if defined(Win32) || defined(Win64) || defined(__CYGWIN__) - NAComplex_Type.tp_base=&PyComplex_Type; - #endif - if (PyType_Ready(&NAComplex_Type) < 0) { -diff -ru rpy2-2.7.4.orig/setup rpy2-2.7.4/setup.py ---- rpy2-2.7.4.orig/setup.py 2016-01-15 01:45:48.447894400 -0800 -+++ rpy2-2.7.4/setup.py 2016-01-15 01:48:55.222874000 -0800 -@@ -160,7 +160,7 @@ - extra_link_args = [] - extra_compile_args = [] - include_dirs = [] -- libraries = [] -+ libraries = ['readline'] - library_dirs = [] - - #FIXME: crude way (will break in many cases) diff --git a/build/pkgs/rpy2/patches/setup-no-pytest.patch b/build/pkgs/rpy2/patches/setup-no-pytest.patch new file mode 100644 index 00000000000..91cb5127572 --- /dev/null +++ b/build/pkgs/rpy2/patches/setup-no-pytest.patch @@ -0,0 +1,13 @@ +diff --git a/setup.py b/setup.py +index a9f96f8..7ba69a1 100755 +--- a/setup.py ++++ b/setup.py +@@ -142,7 +142,7 @@ ipython. + if __name__ == '__main__': + pack_dir = {PACKAGE_NAME: os.path.join(package_prefix, 'rpy2')} + +- requires = ['pytest', 'jinja2', 'pytz', 'tzlocal'] ++ requires = ['jinja2', 'pytz', 'tzlocal'] + + setup( + name=PACKAGE_NAME, diff --git a/build/pkgs/rpy2/spkg-check.in b/build/pkgs/rpy2/spkg-check.in new file mode 100644 index 00000000000..e079f8a6038 --- /dev/null +++ b/build/pkgs/rpy2/spkg-check.in @@ -0,0 +1 @@ +pytest diff --git a/build/pkgs/rpy2/spkg-install b/build/pkgs/rpy2/spkg-install deleted file mode 100644 index 105eb0153d0..00000000000 --- a/build/pkgs/rpy2/spkg-install +++ /dev/null @@ -1,13 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -set -e - -cd src - -export RHOME="$SAGE_LOCAL"/lib/R - -sdh_pip_install . diff --git a/build/pkgs/rpy2/spkg-install.in b/build/pkgs/rpy2/spkg-install.in new file mode 100644 index 00000000000..3098724e3a4 --- /dev/null +++ b/build/pkgs/rpy2/spkg-install.in @@ -0,0 +1,7 @@ +set -e + +cd src + +export RHOME="$SAGE_LOCAL"/lib/R + +sdh_pip_install . diff --git a/build/pkgs/rst2ipynb/SPKG.rst b/build/pkgs/rst2ipynb/SPKG.rst new file mode 100644 index 00000000000..12dbaa5ee87 --- /dev/null +++ b/build/pkgs/rst2ipynb/SPKG.rst @@ -0,0 +1,42 @@ +rst2ipynb +========= + +Description +----------- + +The rst2pynb program converts a standalone reStructuredText file to a +Jupyter notebook file. + +This is currently achieved by converting to markdown with pandoc and +then to Jupyter notebook using notedown, plus some configuration and +tweaks. + +License +------- + +BSD 3-Clause License + + +Upstream Contact +---------------- + +Authors: Scott Sievert and Nicolas M. Thiéry Home page: +https://github.com/nthiery/rst-to-ipynb + +Dependencies +------------ + +- notedown +- pandoc + + +Special Update/Build Instructions +--------------------------------- + +Fetch tarball from https://pypi.python.org/pypi/rst2ipynb/ + +As it is written in Haskell, pandoc must be installed from the distro. + +The main rationale for having a notedown package in Sage (rather than +just let pip fetch it) is that the version on pipy (1.5.0, 2015-10-07) +is outdated and lacks important features / fixes for us. diff --git a/build/pkgs/rst2ipynb/SPKG.txt b/build/pkgs/rst2ipynb/SPKG.txt deleted file mode 100644 index b10dc5acacb..00000000000 --- a/build/pkgs/rst2ipynb/SPKG.txt +++ /dev/null @@ -1,34 +0,0 @@ -= rst2ipynb = - -== Description == - -The rst2pynb program converts a standalone reStructuredText file to a -Jupyter notebook file. - -This is currently achieved by converting to markdown with pandoc and -then to Jupyter notebook using notedown, plus some configuration and -tweaks. - -== License == - -BSD 3-Clause License - -== Upstream Contact == - -Authors: Scott Sievert and Nicolas M. Thiéry -Home page: https://github.com/nthiery/rst-to-ipynb - -== Dependencies == - - * notedown - * pandoc - -== Special Update/Build Instructions == - -Fetch tarball from https://pypi.python.org/pypi/rst2ipynb/ - -As it is written in Haskell, pandoc must be installed from the distro. - -The main rationale for having a notedown package in Sage (rather than -just let pip fetch it) is that the version on pipy (1.5.0, 2015-10-07) -is outdated and lacks important features / fixes for us. diff --git a/build/pkgs/rst2ipynb/dependencies b/build/pkgs/rst2ipynb/dependencies index df1cebfb5d4..4cf206f037d 100644 --- a/build/pkgs/rst2ipynb/dependencies +++ b/build/pkgs/rst2ipynb/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip notedown +$(PYTHON) pandoc | $(PYTHON_TOOLCHAIN) notedown ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/prompt_toolkit/spkg-install b/build/pkgs/rst2ipynb/spkg-install.in similarity index 100% rename from build/pkgs/prompt_toolkit/spkg-install rename to build/pkgs/rst2ipynb/spkg-install.in diff --git a/build/pkgs/rubiks/SPKG.rst b/build/pkgs/rubiks/SPKG.rst new file mode 100644 index 00000000000..b73e508cdb2 --- /dev/null +++ b/build/pkgs/rubiks/SPKG.rst @@ -0,0 +1,27 @@ +rubiks.spkg +=========== + +Description +----------- + +There are several programs for working with Rubik's cubes, by three +different people. Look inside the directories under /src to see specific +info and licensing. In summary the three contributers are: + +Michael Reid (GPL) +http://www.math.ucf.edu/~reid/Rubik/optimal_solver.html + +- optimal - uses many pre-computed tables to find an optimal + solution to the 3x3x3 Rubik's cube + +Dik T. Winter (MIT License) + +- cube - uses Kociemba's algorithm to iteratively find a short + solution to the 3x3x3 Rubik's cube +- size222 - solves a 2x2x2 Rubik's cube + +Eric Dietz (GPL) http://www.wrongway.org/?rubiksource + +- cu2 - A fast, non-optimal 2x2x2 solver +- cubex - A fast, non-optimal 3x3x3 solver +- mcube - A fast, non-optimal 4x4x4 solver diff --git a/build/pkgs/rubiks/SPKG.txt b/build/pkgs/rubiks/SPKG.txt deleted file mode 100644 index 7a98bd2aeb0..00000000000 --- a/build/pkgs/rubiks/SPKG.txt +++ /dev/null @@ -1,125 +0,0 @@ -= rubiks.spkg = - -== Description == - -There are several programs for working with Rubik's cubes, by three -different people. Look inside the directories under /src to see -specific info and licensing. In summary the three contributers are: - - -Michael Reid (GPL) http://www.math.ucf.edu/~reid/Rubik/optimal_solver.html - optimal - uses many pre-computed tables to find an optimal - solution to the 3x3x3 Rubik's cube - - -Dik T. Winter (MIT License) - cube - uses Kociemba's algorithm to iteratively find a short - solution to the 3x3x3 Rubik's cube - size222 - solves a 2x2x2 Rubik's cube - - -Eric Dietz (GPL) http://www.wrongway.org/?rubiksource - cu2 - A fast, non-optimal 2x2x2 solver - cubex - A fast, non-optimal 3x3x3 solver - mcube - A fast, non-optimal 4x4x4 solver - -== Changelog == - -=== rubiks-20070912.p18 (John Palmieri, 23 March 2012) === - * #12311: Remove explicit path to testcc.sh in spkg-install. - -=== rubiks-20070912.p17 (Jeroen Demeyer, 8 June 2011) === - * #11437: Apply workaround for versions 4.6.0 and 4.6.1 of gcc. - The bug is supposed to be fixed in the final gcc 4.6.1 but we still - apply the workaround for gcc 4.6.1 to support pre-release versions - of gcc 4.6.1. - -=== rubiks-20070912.p16 (Jeroen Demeyer, 3 May 2011) === - * #11168: Apply workaround for versions 4.6.x of gcc, not only - version 4.6.0. - -=== rubiks-20070912.p15 (Jeroen Demeyer, 25 April 2011) === - * #11168: Instead of using -O1 with gcc 4.6.0, use -O2 -fno-ivopts - The is because the bug is in the file gcc/tree-ssa-loop-ivopts.c, - see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48702 - -=== rubiks-20070912.p14 (David Kirkby, 23rd April 2011) === - * #11168: Drop optimisation level to -O1 on all platforms, but - only if gcc 4.6.0 is used. - -=== rubiks-20070912.p13 (David Kirkby, 10th April 2011) === - * #11168: Drop optimisation level on Solaris to -O1 as this - mis-compiles on OpenSolaris with gcc 4.6.0. It is not - worth making the test too specific, so the optimisation - level is dropped to -O1 on any sort of Solaris machine. - * Correct a few typos in SPKG.txt - -=== rubiks-20070912.p12 (David Kirkby, 30th June 2010) === - * #9388 Corrected my unportable use of 'mktemp' which I erroneously - believed was portable, but it is not defined as a command (only - a system call) by POSIX. The revised version avoids this. - - -=== rubiks-20070912.p11 (David Kirkby, 30th May 2010) === - * #9030 rubiks is building part 32-bit and part 64-bit on OpenSolaris x64. - Added ${CFLAG64} to src/dik/makefile. CFLAG64 gets set to -m64 (by default) - if the variable SAGE64 is "yes". So this patch adds an -m64 which is - needed to build rubiks fully 64-bit. - -=== rubiks-20070912.p10 ???????????????????????????????? === - -=== rubiks-20070912.p9 (William Stein, June 14, 2009) === - * Make too many changes to the Makefiles to list really! - * Remove hard coding of CC, which was set to g++ in some places - There was a total mix-up of variable names for compilers. - * Remove the hard-coding of gcc, which was supposedly done - by Micheal to allow it to build on Solaris (see - rubiks-20070912.p1 below). With the intention later to - build Sage on Solaris with the Sun compiler, why the hell - he hard-coded g++ I do not know. - * Added -Wall to build if using g++ - * Checks there are not a mix of Sun and GNU compilers - * Adds -m64 if SAGE64 is set to yes. - * Print out what things like CC, CXX etc are set to - The Makefile do not modify these in this case. - -=== rubiks-20070912.p9 (William Stein, June 14, 2009) === - * use $MAKE environment variable so that parallel build works - -=== rubiks-20070912.p8 (Michael Abshoff, September 1st, 2008) === - * work around install problem on Solaris - -=== rubiks-20070912.p7 (William Stein, May 16th, 2008) === - * Add Cygwin build support (#3241) - -=== rubiks-20070912.p6 (Michael Abshoff, April 14th, 2008) === - * remove binary crap from reid solver (fixes #2985) - * build Reid solvers with "-O2 -g" - -=== rubiks-20070912.p5 (Michael Abshoff, April 14th, 2008) === - * fix gcc 4.3 build. The patch has been applied to the source tree. It need to be send upstream. - -=== rubiks-20070912.p4 (Michael Abshoff, April 1st, 2008) === - * Debian amd64 fixes for rubiks (Tim Abbott, #2763) - -=== rubiks-20070912.p3 (Michael Abshoff, March 21st, 2008) === - * SAGE_LOCAL check (#633) - * remove binary crap - * rename cube to dikcube to avoid name clash with polymake (#2595) - * detect the location of install instead of hardcoding it (#2287) - -=== rubiks-20070912.p2 (Tim Abbott, Feb. 17th, 2008) === - * Convert normal spkg-install to using new Makefile - -=== rubiks-20070912.p1 (Michael Abshoff, Jan. 28th, 2008) === - * fix Solaris build by setting CC to gcc and changing Dik's makefile to use $CC instead of cc - -=== rubiks-20070912.p0 (Michael Abshoff) === - * clean up SPKG.txt - * remove *DS*Store and various prebuild binaries from tree - * remove global hg repo (that included src!) - -=== rubiks-20070912 === - * initial version - - diff --git a/build/pkgs/rubiks/spkg-install b/build/pkgs/rubiks/spkg-install.in similarity index 100% rename from build/pkgs/rubiks/spkg-install rename to build/pkgs/rubiks/spkg-install.in diff --git a/build/pkgs/rw/SPKG.rst b/build/pkgs/rw/SPKG.rst new file mode 100644 index 00000000000..03ca7382412 --- /dev/null +++ b/build/pkgs/rw/SPKG.rst @@ -0,0 +1,20 @@ +rw +== + +Description +----------- + +rw is a program that calculates rank-width and rank-decompositions. + +http://pholia.tdi.informatik.uni-frankfurt.de/~philipp/software/rw.shtml + +License +------- + +GPL version 2 or later + + +Upstream Contact +---------------- + +Philipp Klaus Krause (philipp@informatik.uni-frankfurt.de) diff --git a/build/pkgs/rw/SPKG.txt b/build/pkgs/rw/SPKG.txt deleted file mode 100644 index 7ab5243f2d4..00000000000 --- a/build/pkgs/rw/SPKG.txt +++ /dev/null @@ -1,15 +0,0 @@ -= rw = - -== Description == - -rw is a program that calculates rank-width and rank-decompositions. - -http://pholia.tdi.informatik.uni-frankfurt.de/~philipp/software/rw.shtml - -== License == - -GPL version 2 or later - -== Upstream Contact == - -Philipp Klaus Krause (philipp@informatik.uni-frankfurt.de) diff --git a/build/pkgs/rw/spkg-install b/build/pkgs/rw/spkg-install.in similarity index 100% rename from build/pkgs/rw/spkg-install rename to build/pkgs/rw/spkg-install.in diff --git a/build/pkgs/saclib/SPKG.rst b/build/pkgs/saclib/SPKG.rst new file mode 100644 index 00000000000..5301ddd82b3 --- /dev/null +++ b/build/pkgs/saclib/SPKG.rst @@ -0,0 +1,41 @@ +saclib +====== + +Description +----------- + +Saclib is a library of C programs for computer algebra derived from the +SAC2 system. It is mainly used as a dependency of qepcad. + +License +------- + +Saclib 2.2 Copyright (c) 1993, 2008, RISC-Linz (contact +wcbrown@usna.edu) + +Permission to use, copy, modify, and/or distribute this software, +including source files, README files, etc., for any purpose with or +without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +Upstream Contact +---------------- + +- Website: http://www.usna.edu/CS/qepcadweb/B/QEPCAD.html +- Alternative location (sometimes more up-to-date): + + https://www.usna.edu/Users/cs/wcbrown/qepcad/B/QEPCAD.html + +Dependencies +------------ + +None. diff --git a/build/pkgs/saclib/SPKG.txt b/build/pkgs/saclib/SPKG.txt deleted file mode 100644 index d8f6c7baf44..00000000000 --- a/build/pkgs/saclib/SPKG.txt +++ /dev/null @@ -1,35 +0,0 @@ -= saclib = - -== Description == - -Saclib is a library of C programs for computer algebra derived from the SAC2 -system. It is mainly used as a dependency of qepcad. - -== License == - -Saclib 2.2 -Copyright (c) 1993, 2008, RISC-Linz (contact wcbrown@usna.edu) - -Permission to use, copy, modify, and/or distribute this software, including -source files, README files, etc., for any purpose with or without fee is -hereby granted, provided that the above copyright notice and this permission -notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -== Upstream Contact == - -- Website: http://www.usna.edu/CS/qepcadweb/B/QEPCAD.html -- Alternative location (sometimes more up-to-date): - https://www.usna.edu/Users/cs/wcbrown/qepcad/B/QEPCAD.html - -== Dependencies == - -None. - diff --git a/build/pkgs/saclib/spkg-install b/build/pkgs/saclib/spkg-install deleted file mode 100644 index e4267348497..00000000000 --- a/build/pkgs/saclib/spkg-install +++ /dev/null @@ -1,24 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting"; - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - - -# build saclib -cd src - -saclib=$(pwd -P) -export saclib -bin/sconf && bin/mkproto && bin/mkmake && bin/mklib all -if [ $? -ne 0 ]; then - echo >&2 "Error building saclib." - exit 1 -fi - - -# install saclib to the Sage tree -cd .. -rm -rf $SAGE_LOCAL/lib/saclib -mv src $SAGE_LOCAL/lib/saclib - diff --git a/build/pkgs/saclib/spkg-install.in b/build/pkgs/saclib/spkg-install.in new file mode 100644 index 00000000000..4fba5f22478 --- /dev/null +++ b/build/pkgs/saclib/spkg-install.in @@ -0,0 +1,16 @@ +cd src + +saclib=$(pwd -P) +export saclib +bin/sconf && bin/mkproto && bin/mkmake && bin/mklib all +if [ $? -ne 0 ]; then + echo >&2 "Error building saclib." + exit 1 +fi + + +# install saclib to the Sage tree +cd .. +rm -rf $SAGE_LOCAL/lib/saclib +mv src $SAGE_LOCAL/lib/saclib + diff --git a/build/pkgs/sage_brial/SPKG.rst b/build/pkgs/sage_brial/SPKG.rst new file mode 100644 index 00000000000..3cc3c9181e9 --- /dev/null +++ b/build/pkgs/sage_brial/SPKG.rst @@ -0,0 +1,24 @@ +BRiAl +===== + +Description +----------- + +BRiAl is the successor to PolyBoRi. + +The core of PolyBoRi is a C++ library, which provides high-level data +types for Boolean polynomials and monomials, exponent vectors, as well +as for the underlying polynomial rings and subsets of the powerset of +the Boolean variables. This SPKG is a (sage) python wrapper around the +functionality of the C++ library. + +License +------- + +GPL version 2 or later + + +Upstream Contact +---------------- + +https://github.com/BRiAl/BRiAl diff --git a/build/pkgs/sage_brial/checksums.ini b/build/pkgs/sage_brial/checksums.ini new file mode 100644 index 00000000000..2f3607207f7 --- /dev/null +++ b/build/pkgs/sage_brial/checksums.ini @@ -0,0 +1,5 @@ +tarball=brial-VERSION.tar.bz2 +sha1=ea69faff56fb7068536723f3fb5b3583b8467831 +md5=d6c6a01d4fc80062550e02d9185bfbff +cksum=318826732 +upstream_url=https://github.com/BRiAl/BRiAl/releases/download/VERSION/brial-VERSION.tar.bz2 diff --git a/build/pkgs/sage_brial/dependencies b/build/pkgs/sage_brial/dependencies new file mode 100644 index 00000000000..a3a1eda874c --- /dev/null +++ b/build/pkgs/sage_brial/dependencies @@ -0,0 +1,5 @@ +brial $(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/sage_brial/package-version.txt b/build/pkgs/sage_brial/package-version.txt new file mode 100644 index 00000000000..db6fb4a9113 --- /dev/null +++ b/build/pkgs/sage_brial/package-version.txt @@ -0,0 +1 @@ +1.2.8 diff --git a/build/pkgs/sage_brial/spkg-install.in b/build/pkgs/sage_brial/spkg-install.in new file mode 100644 index 00000000000..314863c1a77 --- /dev/null +++ b/build/pkgs/sage_brial/spkg-install.in @@ -0,0 +1,7 @@ +# +# BRiAl consists of a both C++ library and a SageMath-specific python +# module. This spkg installs only the python module; the C++ library +# is installed by the "brial" spkg. +# +export PYTHON=sage-python23 +cd src/sage-brial && sdh_pip_install . diff --git a/build/pkgs/sage_brial/spkg-legacy-uninstall.in b/build/pkgs/sage_brial/spkg-legacy-uninstall.in new file mode 100644 index 00000000000..0e63fe75dec --- /dev/null +++ b/build/pkgs/sage_brial/spkg-legacy-uninstall.in @@ -0,0 +1,2 @@ +echo "Cleaning out old PolyBoRi and BRiAl installations" +rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/{polybori,brial} diff --git a/build/pkgs/flask/type b/build/pkgs/sage_brial/type similarity index 100% rename from build/pkgs/flask/type rename to build/pkgs/sage_brial/type diff --git a/build/pkgs/sage_conf/dependencies b/build/pkgs/sage_conf/dependencies index e52a88f906c..03d9b278764 100644 --- a/build/pkgs/sage_conf/dependencies +++ b/build/pkgs/sage_conf/dependencies @@ -1 +1 @@ -$(PYTHON) ../pkgs/sage_conf/src/sage_conf.py ../pkgs/sage_conf/src/setup.cfg | pip setuptools +$(PYTHON) ../pkgs/sage_conf/src/sage_conf.py ../pkgs/sage_conf/src/setup.cfg | $(PYTHON_TOOLCHAIN) diff --git a/build/pkgs/sage_conf/spkg-install b/build/pkgs/sage_conf/spkg-install index bdb5c41ac95..468970ba085 100755 --- a/build/pkgs/sage_conf/spkg-install +++ b/build/pkgs/sage_conf/spkg-install @@ -9,4 +9,4 @@ if [ $? -ne 0 ]; then echo >&2 "Is $SAGE_ROOT the correct SAGE_ROOT?" exit 1 fi -cd $SAGE_ROOT/build/pkgs/sage_conf/src && sdh_pip_install . +cd src && sdh_pip_install . diff --git a/build/pkgs/sage_conf/src/sage_conf.py.in b/build/pkgs/sage_conf/src/sage_conf.py.in index 44c8e8c7625..957b0814e5d 100644 --- a/build/pkgs/sage_conf/src/sage_conf.py.in +++ b/build/pkgs/sage_conf/src/sage_conf.py.in @@ -4,10 +4,19 @@ VERSION = "@PACKAGE_VERSION@" MAXIMA = "@prefix@/bin/maxima" +ARB_LIBRARY = "@SAGE_ARB_LIBRARY@" + +SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@" + +# Used in sage.repl.ipython_kernel.install +JSMOL_DIR = "@prefix@/share/jsmol" +MATHJAX_DIR = "@prefix@/share/mathjax" +THREEJS_DIR = "@prefix@/share/threejs" + # The following must not be used during build to determine source or installation # location of sagelib. See comments in SAGE_ROOT/src/Makefile.in SAGE_LOCAL = "@prefix@" -SAGE_ROOT = "@abs_top_srcdir@" +SAGE_ROOT = "@SAGE_ROOT@" # Entry point 'sage-config'. It does not depend on any packages. diff --git a/build/pkgs/sage_conf/type b/build/pkgs/sage_conf/type index 84f7e31d99b..a6a7b9cd726 100644 --- a/build/pkgs/sage_conf/type +++ b/build/pkgs/sage_conf/type @@ -1 +1 @@ -script +standard diff --git a/build/pkgs/sage_numerical_backends_coin/dependencies b/build/pkgs/sage_numerical_backends_coin/dependencies index 9fdf4cc25a1..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) setuptools pip 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_coin/spkg-check b/build/pkgs/sage_numerical_backends_coin/spkg-check.in similarity index 100% rename from build/pkgs/sage_numerical_backends_coin/spkg-check rename to build/pkgs/sage_numerical_backends_coin/spkg-check.in diff --git a/build/pkgs/sage_numerical_backends_coin/spkg-install b/build/pkgs/sage_numerical_backends_coin/spkg-install deleted file mode 100644 index 38966d96e37..00000000000 --- a/build/pkgs/sage_numerical_backends_coin/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -#Install new version -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing sage_numerical_backends_coin." - exit 1 -fi diff --git a/build/pkgs/sage_numerical_backends_coin/spkg-install.in b/build/pkgs/sage_numerical_backends_coin/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/sage_numerical_backends_coin/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/sage_numerical_backends_cplex/dependencies b/build/pkgs/sage_numerical_backends_cplex/dependencies index 3af0aae4b6a..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) setuptools pip 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_cplex/spkg-check b/build/pkgs/sage_numerical_backends_cplex/spkg-check.in similarity index 100% rename from build/pkgs/sage_numerical_backends_cplex/spkg-check rename to build/pkgs/sage_numerical_backends_cplex/spkg-check.in diff --git a/build/pkgs/sage_numerical_backends_cplex/spkg-install b/build/pkgs/sage_numerical_backends_cplex/spkg-install deleted file mode 100644 index 6c72df71f1c..00000000000 --- a/build/pkgs/sage_numerical_backends_cplex/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -#Install new version -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing sage_numerical_backends_cplex." - exit 1 -fi diff --git a/build/pkgs/sage_numerical_backends_cplex/spkg-install.in b/build/pkgs/sage_numerical_backends_cplex/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/sage_numerical_backends_cplex/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/sage_numerical_backends_gurobi/dependencies b/build/pkgs/sage_numerical_backends_gurobi/dependencies index 3af0aae4b6a..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) setuptools pip 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/spkg-check b/build/pkgs/sage_numerical_backends_gurobi/spkg-check.in similarity index 100% rename from build/pkgs/sage_numerical_backends_gurobi/spkg-check rename to build/pkgs/sage_numerical_backends_gurobi/spkg-check.in diff --git a/build/pkgs/sage_numerical_backends_gurobi/spkg-install b/build/pkgs/sage_numerical_backends_gurobi/spkg-install deleted file mode 100644 index 0133d5b2066..00000000000 --- a/build/pkgs/sage_numerical_backends_gurobi/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -#Install new version -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing sage_numerical_backends_gurobi." - exit 1 -fi diff --git a/build/pkgs/sage_numerical_backends_gurobi/spkg-install.in b/build/pkgs/sage_numerical_backends_gurobi/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/sage_numerical_backends_gurobi/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/sagelib/dependencies b/build/pkgs/sagelib/dependencies new file mode 100644 index 00000000000..8032ab0008c --- /dev/null +++ b/build/pkgs/sagelib/dependencies @@ -0,0 +1,10 @@ +FORCE $(SCRIPTS) arb boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap givaro glpk gmpy2 gsl iml jinja2 jupyter_core lcalc lrcalc libbraiding libhomfly libpng linbox m4ri m4rie mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy pycygwin pynac $(PYTHON) ratpoints readline rw sage_brial sage_conf singular symmetrica zn_poly $(PCFILES) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. + +The above are the *build-time* dependencies of the Sage library. These are, +on the one hand, programs needed for the build/install process of the +Sage library (e.g. CYTHON, JINJA2), and on the other hand all +dependencies for Cython files (e.g. PARI, NTL, MP_LIBRARY). diff --git a/build/pkgs/sagelib/package-version.txt b/build/pkgs/sagelib/package-version.txt new file mode 100644 index 00000000000..755bef9528c --- /dev/null +++ b/build/pkgs/sagelib/package-version.txt @@ -0,0 +1 @@ +9.2.beta8 diff --git a/build/pkgs/sagelib/spkg-install b/build/pkgs/sagelib/spkg-install new file mode 100755 index 00000000000..0fa42a7e912 --- /dev/null +++ b/build/pkgs/sagelib/spkg-install @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +cd "$SAGE_SRC" +## All sagelib-building is done by setup.py. +## This is so that sagelib can be installed by standard Python procedures, +## such as "./setup.py install" or "pip install ." +## +## We poison all environment variables that have paths to the sage source and build directories. +## In this way we make sure that all of the sagelib build's source paths are communicated through +## the current directory (for the source tree). +## Building takes places in the build/ subdirectory. +## +## As a special exception, we feed SAGE_PKGS and SAGE_SPKG_INST. +## They are needed by src/sage_setup/optional_extension.py +## via src/sage/misc/package.py. See meta-ticket #28815 for planned changes to this. + +export SAGE_PKGS="$SAGE_ROOT"/build/pkgs +export SAGE_ROOT=/doesnotexist +export SAGE_SRC=/doesnotexist +export SAGE_SRC_ROOT=/doesnotexist +export SAGE_DOC_SRC=/doesnotexist +export SAGE_BUILD_DIR=/doesnotexist + +export PYTHON="$SAGE_LOCAL/bin/python3" + +# We also poison all directories below SAGE_LOCAL. + +export SAGE_ETC=/doesnotexist +export SAGE_PKGCONFIG=/doesnotexist +export SAGE_PKG_CONFIG_PATH=/doesnotexist +export SAGE_SPKG_SCRIPTS=/doesnotexist +export SAGE_DOC=/doesnotexist +export SAGE_SHARE=/doesnotexist + +# Trac #29411: We cannot poison SAGE_LOCAL because the pkg-config script +# installed by the pkgconf spkg, generated from build/pkgs/pkgconf/patches/pkg-config.in, +# uses this variable. +# export SAGE_LOCAL=/doesnotexist + +time "$PYTHON" -u setup.py --no-user-cfg build install || exit 1 +if [ "$UNAME" = "CYGWIN" ]; then + sage-rebase.sh "$SAGE_LOCAL" 2>/dev/null; +fi diff --git a/build/pkgs/flask_autoindex/type b/build/pkgs/sagelib/type similarity index 100% rename from build/pkgs/flask_autoindex/type rename to build/pkgs/sagelib/type diff --git a/build/pkgs/sagenb/SPKG.txt b/build/pkgs/sagenb/SPKG.txt deleted file mode 100644 index 92ab236a0e8..00000000000 --- a/build/pkgs/sagenb/SPKG.txt +++ /dev/null @@ -1,40 +0,0 @@ -= Sage Notebook = - -== Description == - -The Sage Notebook is a web-based graphical user interface for -mathematical software. - -== License == - -GPLv3+ - -== Upstream Contact == - - * Keshav Kini - * Homepage: https://github.com/sagemath/sagenb - -== Dependencies == - -Build-time dependencies: - - * Python - * setuptools - * twisted - * flask - - flask-autoindex - - flask-babel - - flask-openid - - flask-oldsessions - -Run-time dependencies: - - * Sage - * jinja2 - * pexpect - * docutils - * sphinx - -Optional dependency: - - * OpenSSL (including headers) diff --git a/build/pkgs/sagenb/checksums.ini b/build/pkgs/sagenb/checksums.ini deleted file mode 100644 index 5b0930947d3..00000000000 --- a/build/pkgs/sagenb/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=sagenb-VERSION.tar.bz2 -sha1=722b7a15f2e1f911224ab6f397457f69d65027d7 -md5=0bdc2b44f3ce961c6b74bfc736ac2e09 -cksum=2540078270 diff --git a/build/pkgs/sagenb/dependencies b/build/pkgs/sagenb/dependencies deleted file mode 100644 index c5727e022e8..00000000000 --- a/build/pkgs/sagenb/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(STARTED) | pip babel flask flask_autoindex flask_babel flask_oldsessions flask_openid mathjax twisted sphinx - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/sagenb/package-version.txt b/build/pkgs/sagenb/package-version.txt deleted file mode 100644 index 781dcb07cd8..00000000000 --- a/build/pkgs/sagenb/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -1.1.3 diff --git a/build/pkgs/sagenb/spkg-install b/build/pkgs/sagenb/spkg-install deleted file mode 100644 index 414edd66f01..00000000000 --- a/build/pkgs/sagenb/spkg-install +++ /dev/null @@ -1,23 +0,0 @@ -cd src - -sdh_pip_install . - -cd doc -$MAKE html || sdh_die "Error building the documentation" -docdir="$SAGE_LOCAL/share/doc/sagenb" -rm -rf "$docdir" -mv build/html "$docdir" - -cd "${SAGE_DESTDIR}" - -# SageNB installation path, relative either to / (for real install) -# or to SAGE_DESTDIR. -SAGENB_INSTALL=$(echo ."$SAGE_LOCAL"/lib/python*/site-packages/sagenb) - -# Completely remove the old sagenb installation, including bad mathjax -# installations. -( cd / && rm -rf "$SAGENB_INSTALL" ) - -# Let sagenb use mathjax -ln -s -n "$SAGE_SHARE/mathjax/" "$SAGENB_INSTALL/data/mathjax" || \ - sdh_die "Error: Cannot symlink mathjax into the SageNB data directory." diff --git a/build/pkgs/sagenb_export/SPKG.rst b/build/pkgs/sagenb_export/SPKG.rst new file mode 100644 index 00000000000..8f1a7e77dd7 --- /dev/null +++ b/build/pkgs/sagenb_export/SPKG.rst @@ -0,0 +1,13 @@ +sagenb_export +============= + +Description +----------- + +This is a tool to convert SageNB notebooks to other formats, in +particular IPython/Jupyter notebooks. + +It includes a Jupyter notebook extension to provide a UI for the import +of SageNB notebooks. + +https://github.com/vbraun/ExportSageNB diff --git a/build/pkgs/sagenb_export/SPKG.txt b/build/pkgs/sagenb_export/SPKG.txt deleted file mode 100644 index 4c471abd0ae..00000000000 --- a/build/pkgs/sagenb_export/SPKG.txt +++ /dev/null @@ -1,11 +0,0 @@ -= sagenb_export = - -== Description == - -This is a tool to convert SageNB notebooks to other formats, in -particular IPython/Jupyter notebooks. - -It includes a Jupyter notebook extension to provide a UI for the -import of SageNB notebooks. - -https://github.com/vbraun/ExportSageNB diff --git a/build/pkgs/sagenb_export/dependencies b/build/pkgs/sagenb_export/dependencies index 7ac1370dd68..24a4982eff7 100644 --- a/build/pkgs/sagenb_export/dependencies +++ b/build/pkgs/sagenb_export/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) nbconvert ipython six | pip +$(PYTHON) nbconvert ipython six | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sagenb_export/spkg-install b/build/pkgs/sagenb_export/spkg-install.in similarity index 100% rename from build/pkgs/sagenb_export/spkg-install rename to build/pkgs/sagenb_export/spkg-install.in diff --git a/build/pkgs/sagetex/SPKG.rst b/build/pkgs/sagetex/SPKG.rst new file mode 100644 index 00000000000..c0e9d9ef8e1 --- /dev/null +++ b/build/pkgs/sagetex/SPKG.rst @@ -0,0 +1,64 @@ +SageTeX +======= + +Description +----------- + +The SageTeX package allows you to embed code, results of computations, +and plots from Sage into LaTeX documents. + +License +------- + +The *source code* of the SageTeX package may be redistributed and/or +modified 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. To view a copy of this license, see +http://www.gnu.org/licenses/ or send a letter to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +The *documentation* of the SageTeX package is licensed under the +Creative Commons Attribution-Share Alike 3.0 License. To view a copy of +this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or +send a letter to Creative Commons, 171 Second Street, Suite 300, San +Francisco, California, 94105, USA. + + +SPKG Maintainers +---------------- + +Dan Drake (dr.dan.drake at gmail) and SageMath developers +(sage-devel@googlegroups.com) + + +Upstream Contact +---------------- + +Author: Dan Drake. Web: https://github.com/sagemath/sagetex + +Dependencies +------------ + +To install, nothing more than a standard Sage install. The +``spkg-check`` script will exit without actually testing anything if it +cannot find "latex" in your path. + +Notes +----- + +To use SageTeX, both Sage and LaTeX need to know about it. SageTeX comes +standard with Sage, so you only need to make sure LaTeX can find what it +needs. Full details are in the Sage installation guide at +http://doc.sagemath.org/html/en/installation/ and +http://doc.sagemath.org/html/en/tutorial/sagetex.html . + +The directory ``$SAGE_ROOT/local/share/doc/sagetex`` contains +documentation and an example file. See +``$SAGE_ROOT/local/share/texmf/tex/latex/sagetex`` for the source code +and some possibly useful scripts. If you have problems or suggestions +see `the sage-support +group `__. + +If you want to help develop SageTeX, please clone the github repository +(see the "Upstream Contact" above) and send me patches based on that. diff --git a/build/pkgs/sagetex/SPKG.txt b/build/pkgs/sagetex/SPKG.txt deleted file mode 100644 index e4bb1dc683f..00000000000 --- a/build/pkgs/sagetex/SPKG.txt +++ /dev/null @@ -1,56 +0,0 @@ -= SageTeX = - -== Description == - -The SageTeX package allows you to embed code, results of computations, -and plots from Sage into LaTeX documents. - -== License == - -The ''source code'' of the SageTeX package may be redistributed and/or -modified 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. To view a copy of this license, see -[[http://www.gnu.org/licenses/]] or send a letter to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301, USA. - -The ''documentation'' of the SageTeX package is licensed under the -Creative Commons Attribution-Share Alike 3.0 License. To view a copy of -this license, visit [[http://creativecommons.org/licenses/by-sa/3.0/]] -or send a letter to Creative Commons, 171 Second Street, Suite 300, San -Francisco, California, 94105, USA. - -== SPKG Maintainers == - -Dan Drake (dr.dan.drake at gmail) -and -SageMath developers (sage-devel@googlegroups.com) - -== Upstream Contact == - -Author: Dan Drake. Web: [[https://github.com/sagemath/sagetex]] - -== Dependencies == - -To install, nothing more than a standard Sage install. The -`spkg-check` script will exit without actually testing anything if -it cannot find "latex" in your path. - -== Notes == - -To use SageTeX, both Sage and LaTeX need to know about it. SageTeX comes -standard with Sage, so you only need to make sure LaTeX can find what it -needs. Full details are in the Sage installation guide at -http://doc.sagemath.org/html/en/installation/ and -http://doc.sagemath.org/html/en/tutorial/sagetex.html . - -The directory `$SAGE_ROOT/local/share/doc/sagetex` contains -documentation and an example file. See -`$SAGE_ROOT/local/share/texmf/tex/latex/sagetex` for the source code and -some possibly useful scripts. If you have problems or suggestions see -[[http://groups.google.com/group/sage-support|the sage-support group]]. - -If you want to help develop SageTeX, please clone the github -repository (see the "Upstream Contact" above) and send me -patches based on that. diff --git a/build/pkgs/sagetex/spkg-check b/build/pkgs/sagetex/spkg-check deleted file mode 100644 index 91d965b87f7..00000000000 --- a/build/pkgs/sagetex/spkg-check +++ /dev/null @@ -1,46 +0,0 @@ -# this runs Sage on the generated .sage files to see if they work ok - -typeset() -{ - latex -interaction=nonstopmode $1 - if [ $? -ne 0 ] - then - echo "Error typesetting $1!" - exit 1 - fi -} - -checkdotsage() -{ - sage $1.sagetex.sage - if [ $? -ne 0 ] - then - echo "Error running Sage on $1.sagetex.sage!" - exit 1 - else - echo "No problem that spkg-check can detect running Sage on $1.sagetex.sage." - fi -} - -latex -version -if [ $? -ne 0 ] -then - echo "LaTeX isn't installed (or isn't in \$PATH). Skipping test of SageTeX spkg." - exit 0 -fi - -cd src - -typeset example.tex -checkdotsage example -typeset example.tex -typeset example.tex - -typeset sagetex.dtx -checkdotsage sagetex -typeset sagetex.dtx -typeset sagetex.dtx - -# if we get here, we assume the .sage files are good, and exit successfully -exit 0 - diff --git a/build/pkgs/sagetex/spkg-check.in b/build/pkgs/sagetex/spkg-check.in new file mode 100644 index 00000000000..b502aed9df8 --- /dev/null +++ b/build/pkgs/sagetex/spkg-check.in @@ -0,0 +1,50 @@ +# this runs Sage on the generated .sage files to see if they work ok + +typeset() +{ + latex -interaction=nonstopmode $1 + if [ $? -ne 0 ] + then + echo "Error typesetting $1!" + exit 1 + fi +} + +checkdotsage() +{ + sage $1.sagetex.sage + if [ $? -ne 0 ] + then + echo "Error running Sage on $1.sagetex.sage!" + exit 1 + else + echo "No problem that spkg-check can detect running Sage on $1.sagetex.sage." + fi +} + +latex -version +if [ $? -ne 0 ] +then + echo "LaTeX isn't installed (or isn't in \$PATH). Skipping test of SageTeX spkg." + exit 0 +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 +typeset example.tex + +typeset sagetex.dtx +checkdotsage sagetex +typeset sagetex.dtx +typeset sagetex.dtx + +# if we get here, we assume the .sage files are good, and exit successfully +exit 0 + diff --git a/build/pkgs/sagetex/spkg-install b/build/pkgs/sagetex/spkg-install deleted file mode 100644 index 3441b450b90..00000000000 --- a/build/pkgs/sagetex/spkg-install +++ /dev/null @@ -1,17 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src/ - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing SageTeX." - exit 1 -fi - -echo "Removing old SageTex version(s)" -rm -rf $SAGE_LOCAL/share/texmf/tex/generic/sagetex diff --git a/build/pkgs/sagetex/spkg-install.in b/build/pkgs/sagetex/spkg-install.in new file mode 100644 index 00000000000..55f0e9c76e5 --- /dev/null +++ b/build/pkgs/sagetex/spkg-install.in @@ -0,0 +1,6 @@ +cd src + +sdh_pip_install . + +echo "Removing old SageTex version(s)" +rm -rf $SAGE_LOCAL/share/texmf/tex/generic/sagetex diff --git a/build/pkgs/scandir/SPKG.rst b/build/pkgs/scandir/SPKG.rst new file mode 100644 index 00000000000..99ffec860a1 --- /dev/null +++ b/build/pkgs/scandir/SPKG.rst @@ -0,0 +1,14 @@ +scandir +======= + +Description +----------- + +scandir, a better directory iterator and faster os.walk() + +scandir() is a directory iteration function like os.listdir(), except +that instead of returning a list of bare filenames, it yields DirEntry +objects that include file type and stat information along with the name. +Using scandir() increases the speed of os.walk() by 2-20 times +(depending on the platform and file system) by avoiding unnecessary +calls to os.stat() in most cases. diff --git a/build/pkgs/scandir/SPKG.txt b/build/pkgs/scandir/SPKG.txt deleted file mode 100644 index 82ac2f24bb9..00000000000 --- a/build/pkgs/scandir/SPKG.txt +++ /dev/null @@ -1,13 +0,0 @@ -= scandir = - -== Description == - -scandir, a better directory iterator and faster os.walk() - -scandir() is a directory iteration function like os.listdir(), except -that instead of returning a list of bare filenames, it yields DirEntry -objects that include file type and stat information along with the -name. Using scandir() increases the speed of os.walk() by 2-20 times -(depending on the platform and file system) by avoiding unnecessary -calls to os.stat() in most cases. - diff --git a/build/pkgs/scandir/dependencies b/build/pkgs/scandir/dependencies index e9c4df60abc..15df0c4d6d8 100644 --- a/build/pkgs/scandir/dependencies +++ b/build/pkgs/scandir/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip six +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pysingular/spkg-install b/build/pkgs/scandir/spkg-install.in similarity index 100% rename from build/pkgs/pysingular/spkg-install rename to build/pkgs/scandir/spkg-install.in diff --git a/build/pkgs/scipoptsuite/SPKG.rst b/build/pkgs/scipoptsuite/SPKG.rst new file mode 100644 index 00000000000..febc62d889d --- /dev/null +++ b/build/pkgs/scipoptsuite/SPKG.rst @@ -0,0 +1,49 @@ +scipoptsuite +============ + +Description +----------- + +SCIP is currently one of the fastest non-commercial mixed integer +programming (MIP) solvers. It is also a framework for constraint integer +programming and branch-cut-and-price. It allows total control of the +solution process and the access of detailed information down to the guts +of the solver. + +License +------- + +ZIB Academic License + +The ZIB Academic License allows the use of software distributed under +this license without charge for research purposes as a member of a +non-commercial and academic institution, e.g., a university. The +software is available with its source code. + +http://scip.zib.de/academic.txt + + +SPKG Maintainers +---------------- + +- Martin Albrecht (original spkg) +- Matthias Koeppe (updates for new spkg style) + + +Upstream Contact +---------------- + +http://scip.zib.de/doc/html/AUTHORS.shtml + +Dependencies +------------ + +cmake + + +Special Update/Build Instructions +--------------------------------- + +We do not have permission to redistribute SCIP or SoPlex. Hence, you +must download it yourself from http://scip.zib.de and put the tarball +scipoptsuite-VERSION.tgz in $SAGE_ROOT/upstream diff --git a/build/pkgs/scipoptsuite/SPKG.txt b/build/pkgs/scipoptsuite/SPKG.txt deleted file mode 100644 index d37e355813f..00000000000 --- a/build/pkgs/scipoptsuite/SPKG.txt +++ /dev/null @@ -1,38 +0,0 @@ -= scipoptsuite = - -== Description == - -SCIP is currently one of the fastest non-commercial mixed integer programming -(MIP) solvers. It is also a framework for constraint integer programming and -branch-cut-and-price. It allows total control of the solution process and the -access of detailed information down to the guts of the solver. - -== License == - -ZIB Academic License - -The ZIB Academic License allows the use of software distributed under this -license without charge for research purposes as a member of a non-commercial and -academic institution, e.g., a university. The software is available with its -source code. - -http://scip.zib.de/academic.txt - -== SPKG Maintainers == - -* Martin Albrecht (original spkg) -* Matthias Koeppe (updates for new spkg style) - -== Upstream Contact == - -http://scip.zib.de/doc/html/AUTHORS.shtml - -== Dependencies == - -cmake - -== Special Update/Build Instructions == - -We do not have permission to redistribute SCIP or SoPlex. Hence, you -must download it yourself from http://scip.zib.de and put the tarball -scipoptsuite-VERSION.tgz in $SAGE_ROOT/upstream diff --git a/build/pkgs/scipoptsuite/spkg-check b/build/pkgs/scipoptsuite/spkg-check deleted file mode 100644 index 957fb3728fb..00000000000 --- a/build/pkgs/scipoptsuite/spkg-check +++ /dev/null @@ -1,16 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting" - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src/build/ - -echo "Now running the SCIP test suite..." -# Do the tests with the installed version of SCIP. -# (The uninstalled ones do not work because we did not adjust their library dependencies.) -$MAKE test | tee testsuite.log -if [ $? -ne 0 ]; then - echo >&2 "Error: The SCIP test suite failed with an error status." - exit 1 -fi diff --git a/build/pkgs/scipoptsuite/spkg-check.in b/build/pkgs/scipoptsuite/spkg-check.in new file mode 100644 index 00000000000..b475168e674 --- /dev/null +++ b/build/pkgs/scipoptsuite/spkg-check.in @@ -0,0 +1,5 @@ +cd src/build/ + +# Do the tests with the installed version of SCIP. +# (The uninstalled ones do not work because we did not adjust their library dependencies.) +$MAKE test | tee testsuite.log diff --git a/build/pkgs/scipoptsuite/spkg-install b/build/pkgs/scipoptsuite/spkg-install deleted file mode 100644 index f86edf368ec..00000000000 --- a/build/pkgs/scipoptsuite/spkg-install +++ /dev/null @@ -1,24 +0,0 @@ -if [ "${SAGE_LOCAL}" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting" - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -echo "Building scipoptsuite with cmake" - -cd src -mkdir build -cd build -cmake .. -DCMAKE_INSTALL_PREFIX="${SAGE_LOCAL}"/ -DCMAKE_VERBOSE_MAKEFILE=ON -DGMP_DIR="${SAGE_LOCAL}" -DZLIB_ROOT="${SAGE_LOCAL}" -DReadline_ROOT_DIR="${SAGE_LOCAL}" -DBLISS_DIR="${SAGE_LOCAL}" -DIPOPT=FALSE -make -make install - -$SAGE_LOCAL/bin/scip -s /dev/null -c quit - -if [ $? -ne 0 ]; then - echo "SCIP build completed but the scip executable does not work." - exit 1 -fi - - -echo "Finished building scipoptsuite" diff --git a/build/pkgs/scipoptsuite/spkg-install.in b/build/pkgs/scipoptsuite/spkg-install.in new file mode 100644 index 00000000000..cb3d4e1c4dc --- /dev/null +++ b/build/pkgs/scipoptsuite/spkg-install.in @@ -0,0 +1,18 @@ +echo "Building scipoptsuite with cmake" + +cd src +mkdir build +cd build +cmake .. -DCMAKE_INSTALL_PREFIX="${SAGE_LOCAL}"/ -DCMAKE_VERBOSE_MAKEFILE=ON -DGMP_DIR="${SAGE_LOCAL}" -DZLIB_ROOT="${SAGE_LOCAL}" -DReadline_ROOT_DIR="${SAGE_LOCAL}" -DBLISS_DIR="${SAGE_LOCAL}" -DIPOPT=FALSE +make +make install + +$SAGE_LOCAL/bin/scip -s /dev/null -c quit + +if [ $? -ne 0 ]; then + echo "SCIP build completed but the scip executable does not work." + exit 1 +fi + + +echo "Finished building scipoptsuite" diff --git a/build/pkgs/scipy/SPKG.rst b/build/pkgs/scipy/SPKG.rst new file mode 100644 index 00000000000..f037ff798a5 --- /dev/null +++ b/build/pkgs/scipy/SPKG.rst @@ -0,0 +1,41 @@ +scipy +===== + +Description +----------- + +SciPy (pronounced "Sigh Pie") is open-source software for mathematics, +science, and engineering. The SciPy library depends on NumPy, which +provides convenient and fast N-dimensional array manipulation. The SciPy +library is built to work with NumPy arrays, and provides many +user-friendly and efficient numerical routines such as routines for +numerical integration and optimization. Together, they run on all +popular operating systems, are quick to install, and are free of charge. +NumPy and SciPy are easy to use, but powerful enough to be depended upon +by some of the world's leading scientists and engineers. + +License +------- + +SciPy's license is free for both commercial and non-commercial use, +under the BSD terms. See http://www.scipy.org/License_Compatibility + + +Upstream Contact +---------------- + + http://www.scipy.org/ + +Dependencies +------------ + +- Python, which in Sage has numerous dependencies +- Numpy +- Fortran +- GNU patch + + +Special Update/Build Instructions +--------------------------------- + +- None. diff --git a/build/pkgs/scipy/SPKG.txt b/build/pkgs/scipy/SPKG.txt deleted file mode 100644 index 9754f5f9f70..00000000000 --- a/build/pkgs/scipy/SPKG.txt +++ /dev/null @@ -1,28 +0,0 @@ -= scipy = - -== Description == -SciPy (pronounced "Sigh Pie") is open-source software for mathematics, -science, and engineering. The SciPy library depends on NumPy, which provides -convenient and fast N-dimensional array manipulation. The SciPy library is -built to work with NumPy arrays, and provides many user-friendly and efficient -numerical routines such as routines for numerical integration and optimization. -Together, they run on all popular operating systems, are quick to install, and -are free of charge. NumPy and SciPy are easy to use, but powerful enough to be -depended upon by some of the world's leading scientists and engineers. - -== License == -SciPy's license is free for both commercial and non-commercial use, under the -BSD terms. See http://www.scipy.org/License_Compatibility - -== Upstream Contact == - http://www.scipy.org/ - -== Dependencies == - * Python, which in Sage has numerous dependencies - * Numpy - * Fortran - * GNU patch - -== Special Update/Build Instructions == - * None. - diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index 8843cd07074..fc9e08541d1 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,4 +1,5 @@ tarball=scipy-VERSION.tar.gz -sha1=7e4b5a8c9de029e4eab8735df51b1d8f1e55d346 -md5=e57011507865b0b702aff6077d412e03 -cksum=2206907772 +sha1=d921aa98f73ea5bc533536df7fa681df52d8b9e2 +md5=620fc39f371e04a76af5d0290f8d3753 +cksum=1914434501 +upstream_url=https://pypi.io/packages/source/s/scipy/scipy-VERSION.tar.gz diff --git a/build/pkgs/scipy/dependencies b/build/pkgs/scipy/dependencies index 51d2af0a0d7..23dcaee7e65 100644 --- a/build/pkgs/scipy/dependencies +++ b/build/pkgs/scipy/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) $(BLAS) gfortran numpy | pip +$(PYTHON) $(BLAS) gfortran numpy pybind11 | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index 26aaba0e866..4cda8f19edc 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -1.2.0 +1.5.2 diff --git a/build/pkgs/scipy/patches/4a8a35f2c27be9f209cfc6f347e7a57a4a62e693.patch b/build/pkgs/scipy/patches/4a8a35f2c27be9f209cfc6f347e7a57a4a62e693.patch deleted file mode 100644 index 17e04da5333..00000000000 --- a/build/pkgs/scipy/patches/4a8a35f2c27be9f209cfc6f347e7a57a4a62e693.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 4a8a35f2c27be9f209cfc6f347e7a57a4a62e693 Mon Sep 17 00:00:00 2001 -From: Eric Larson -Date: Wed, 19 Dec 2018 10:50:47 -0500 -Subject: [PATCH] FIX: Fix encoding lines - ---- - scipy/io/__init__.py | 2 +- - scipy/stats/_continuous_distns.py | 1 + - scipy/stats/_stats_mstats_common.py | 3 +++ - 3 files changed, 5 insertions(+), 1 deletion(-) - -diff --git a/scipy/io/__init__.py b/scipy/io/__init__.py -index 64a7aa55d39..056c742687b 100644 ---- a/scipy/io/__init__.py -+++ b/scipy/io/__init__.py -@@ -1,4 +1,4 @@ --# -*- encoding:utf-8 -*- -+# -*- coding: utf-8 -*- - """ - ================================== - Input and output (:mod:`scipy.io`) -diff --git a/scipy/stats/_continuous_distns.py b/scipy/stats/_continuous_distns.py -index 44c05bb7d67..b48d7e7af32 100644 ---- a/scipy/stats/_continuous_distns.py -+++ b/scipy/stats/_continuous_distns.py -@@ -1,3 +1,4 @@ -+# -*- coding: utf-8 -*- - # - # Author: Travis Oliphant 2002-2011 with contributions from - # SciPy Developers 2004-2011 -diff --git a/scipy/stats/_stats_mstats_common.py b/scipy/stats/_stats_mstats_common.py -index e86c8287b48..be478da1f2a 100644 ---- a/scipy/stats/_stats_mstats_common.py -+++ b/scipy/stats/_stats_mstats_common.py -@@ -1,3 +1,5 @@ -+# -*- coding: utf-8 -*- -+ - from collections import namedtuple - - import numpy as np -@@ -11,6 +13,7 @@ - 'rvalue', 'pvalue', - 'stderr')) - -+ - def linregress(x, y=None): - """ - Calculate a linear least-squares regression for two sets of measurements. diff --git a/build/pkgs/scipy/spkg-install b/build/pkgs/scipy/spkg-install deleted file mode 100644 index d63973ca17d..00000000000 --- a/build/pkgs/scipy/spkg-install +++ /dev/null @@ -1,49 +0,0 @@ -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -# These flags confuse numpy's distutils. In particular, -# if they are set empty bad things happen. -unset CFLAGS CXXFLAGS SHAREDFLAGS -echo "Note: CFLAGS, CXXFLAGS and SHAREDFLAGS are taken from distutils," -echo " so their current settings are ignored." - -if [ "$UNAME" = "Darwin" ]; then - unset ATLAS - unset BLAS - unset LAPACK - export LDFLAGS="-bundle -undefined dynamic_lookup $LDFLAGS" - export CPPFLAGS="-D__ACCELERATE__ $CPPFLAGS" -else - export {ATLAS,PTATLAS,OPENBLAS,MKL,MKLROOT}=None - export LDFLAGS="-shared $LDFLAGS" -fi - -# Make sure that the fortran objects are compiled with -fPIC -export FFLAGS="$FFLAGS -fPIC" -export FCFLAGS="$FCFLAGS -fPIC" -if [ "$UNAME" = "CYGWIN" -a "$SAGE_DEBUG" = "yes" ]; then - # Needed for just one or two modules when compiling in debug mode - # Otherwise the debug symbols create too many sections in the binary - export CPPFLAGS="$CPPFLAGS -Wa,-mbig-obj" -fi - - -# This avoids problems on some systems -- until we officially -# support umfpack (which we will likely do, since cvxopt I think includes it): -UMFPACK="None"; export UMFPACK -# See http://projects.scipy.org/pipermail/scipy-user/2006-July/008661.html -# (Currently SWIG gets invoked by scipy when building the umfpack interface, -# which is bad.) - -cd src/ - -# Install: -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo >&2 "Error installing scipy." - exit 1 -fi diff --git a/build/pkgs/scipy/spkg-install.in b/build/pkgs/scipy/spkg-install.in new file mode 100644 index 00000000000..29d9bad7dbc --- /dev/null +++ b/build/pkgs/scipy/spkg-install.in @@ -0,0 +1,38 @@ +# These flags confuse numpy's distutils. In particular, +# if they are set empty bad things happen. +unset CFLAGS CXXFLAGS SHAREDFLAGS +echo "Note: CFLAGS, CXXFLAGS and SHAREDFLAGS are taken from distutils," +echo " so their current settings are ignored." + +if [ "$UNAME" = "Darwin" ]; then + unset ATLAS + unset BLAS + unset LAPACK + export LDFLAGS="-bundle -undefined dynamic_lookup $LDFLAGS" + export CPPFLAGS="-D__ACCELERATE__ $CPPFLAGS" +else + export {ATLAS,PTATLAS,OPENBLAS,MKL,MKLROOT}=None + export LDFLAGS="-shared $LDFLAGS" +fi + +# Make sure that the fortran objects are compiled with -fPIC +export FFLAGS="$FFLAGS -fPIC" +export FCFLAGS="$FCFLAGS -fPIC" +if [ "$UNAME" = "CYGWIN" -a "$SAGE_DEBUG" = "yes" ]; then + # Needed for just one or two modules when compiling in debug mode + # Otherwise the debug symbols create too many sections in the binary + export CPPFLAGS="$CPPFLAGS -Wa,-mbig-obj" +fi + + +# This avoids problems on some systems -- until we officially +# support umfpack (which we will likely do, since cvxopt I think includes it): +UMFPACK="None"; export UMFPACK +# See http://projects.scipy.org/pipermail/scipy-user/2006-July/008661.html +# (Currently SWIG gets invoked by scipy when building the umfpack interface, +# which is bad.) + +cd src/ + +# Install: +sdh_pip_install . diff --git a/build/pkgs/scons/SPKG.txt b/build/pkgs/scons/SPKG.txt deleted file mode 100644 index 7b822f26f9c..00000000000 --- a/build/pkgs/scons/SPKG.txt +++ /dev/null @@ -1,30 +0,0 @@ -= SCons = - -== Description == - -SCons is an Open Source software construction tool—that is, a next-generation build tool. Think of SCons as an improved, cross-platform substitute for the classic Make utility with integrated functionality similar to autoconf/automake and compiler caches such as ccache. In short, SCons is an easier, more reliable and faster way to build software. - -Website: http://www.scons.org/ - -== License == - -X/MIT - -== Upstream Contact == - * SCons mailing lists - see http://www.scons.org/lists.php - -== Dependencies == - * Python - -== Changelog == - -=== scons-1.2.0 (Mike Hansen, June 19th, 2009) === - * Updated to 1.2.0. - -=== scons-0.97.0d20071212 (Michael Abshoff, March 24th, 2008) === - * update to latest 0.97 snapshot release - * remove "-no_archive" from ifort.py (#1618) - * make sure SAGE_LOCAL is defined (#633) - -=== scons-0.97 (Joel B. Mohler) === - * initial version diff --git a/build/pkgs/scons/checksums.ini b/build/pkgs/scons/checksums.ini deleted file mode 100644 index b562e0d8cfc..00000000000 --- a/build/pkgs/scons/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=scons-VERSION.tar.bz2 -sha1=f64373026891502f0ba328504d3126a58e9ec2de -md5=2d9988b88d18a6ed105da3a5838283d4 -cksum=4171645742 diff --git a/build/pkgs/scons/dependencies b/build/pkgs/scons/dependencies deleted file mode 100644 index 304d0c987a2..00000000000 --- a/build/pkgs/scons/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/scons/package-version.txt b/build/pkgs/scons/package-version.txt deleted file mode 100644 index 26aaba0e866..00000000000 --- a/build/pkgs/scons/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -1.2.0 diff --git a/build/pkgs/scons/spkg-install b/build/pkgs/scons/spkg-install deleted file mode 100644 index efaeb341c6b..00000000000 --- a/build/pkgs/scons/spkg-install +++ /dev/null @@ -1,17 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src/ - -rm -rf $SAGE_LOCAL/lib/scons-* - -# scons does not work with pip -sage-python23 setup.py --no-user-cfg install - -if [ $? -ne 0 ]; then - echo "Error installing scons" - exit 1 -fi diff --git a/build/pkgs/scons/type b/build/pkgs/scons/type deleted file mode 100644 index 9839eb20815..00000000000 --- a/build/pkgs/scons/type +++ /dev/null @@ -1 +0,0 @@ -experimental diff --git a/build/pkgs/send2trash/SPKG.rst b/build/pkgs/send2trash/SPKG.rst new file mode 100644 index 00000000000..83784265b8c --- /dev/null +++ b/build/pkgs/send2trash/SPKG.rst @@ -0,0 +1,19 @@ +Send2Trash +========== + +Description +----------- + +Send file to trash natively under Mac OS X, Windows and Linux. + +Send2Trash is a small package that sends files to the Trash (or Recycle +Bin) natively and on all platforms. On OS X, it uses native +FSMoveObjectToTrashSync Cocoa calls, on Windows, it uses native (and +ugly) SHFileOperation win32 calls. On other platforms, if PyGObject and +GIO are available, it will use this. Otherwise, it will fallback to its +own implementation of the trash specifications from freedesktop.org. + +ctypes is used to access native libraries, so no compilation is +necessary. + +Send2Trash supports Python 2.7 and up (Python 3 is supported). diff --git a/build/pkgs/send2trash/SPKG.txt b/build/pkgs/send2trash/SPKG.txt deleted file mode 100644 index d829cf1573b..00000000000 --- a/build/pkgs/send2trash/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= Send2Trash = - -== Description == - -Send file to trash natively under Mac OS X, Windows and Linux. - -Send2Trash is a small package that sends files to the Trash (or Recycle -Bin) natively and on all platforms. On OS X, it uses native -FSMoveObjectToTrashSync Cocoa calls, on Windows, it uses native (and -ugly) SHFileOperation win32 calls. On other platforms, if PyGObject and -GIO are available, it will use this. Otherwise, it will fallback to its -own implementation of the trash specifications from freedesktop.org. - -ctypes is used to access native libraries, so no compilation is -necessary. - -Send2Trash supports Python 2.7 and up (Python 3 is supported). diff --git a/build/pkgs/send2trash/dependencies b/build/pkgs/send2trash/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/send2trash/dependencies +++ b/build/pkgs/send2trash/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/python_openid/spkg-install b/build/pkgs/send2trash/spkg-install.in similarity index 100% rename from build/pkgs/python_openid/spkg-install rename to build/pkgs/send2trash/spkg-install.in diff --git a/build/pkgs/setuptools/SPKG.rst b/build/pkgs/setuptools/SPKG.rst new file mode 100644 index 00000000000..182ce736f0d --- /dev/null +++ b/build/pkgs/setuptools/SPKG.rst @@ -0,0 +1,41 @@ +setuptools +========== + +Description +----------- + +setuptools is a collection of enhancements to the Python distutils (for +Python 2.6 and up) that allow you to more easily build and distribute +Python packages, especially ones that have dependencies on other +packages. + +Website: http://pypi.python.org/pypi/setuptools/ + +License +------- + +PSF or ZPL. i.e Python Software Foundation License or Zope Public +License + + +Upstream Contact +---------------- + +- Phillip J. Eby (distutils-sig@python org) + +Dependencies +------------ + +- python + + +Build Instructions/Changes +-------------------------- + +The following patches are in the patches subdirectory. The patches are +applied during the build process. + +- pkg_resources.py.patch: silence warning about permissions. + +- easy_install_lock.patch: lock the easy_install.pth file to allow + simultaneous installation diff --git a/build/pkgs/setuptools/SPKG.txt b/build/pkgs/setuptools/SPKG.txt deleted file mode 100644 index 6f9783df002..00000000000 --- a/build/pkgs/setuptools/SPKG.txt +++ /dev/null @@ -1,61 +0,0 @@ -= setuptools = - -== Description == - -setuptools is a collection of enhancements to the Python distutils (for -Python 2.6 and up) that allow you to more easily build and distribute -Python packages, especially ones that have dependencies on other packages. - -Website: http://pypi.python.org/pypi/setuptools/ - -== License == - -PSF or ZPL. i.e Python Software Foundation License or Zope Public License - -== Upstream Contact == - * Phillip J. Eby (distutils-sig@python org) - -== Dependencies == - * python - -== Build Instructions/Changes == - -The following patches are in the patches subdirectory. -The patches are applied during the build process. - - * pkg_resources.py.patch: silence warning about permissions. - - * easy_install_lock.patch: lock the easy_install.pth file to allow - simultaneous installation - -= Changelog == - -=== setuptools-0.6.16.p0 (Jeroen Demeyer, 27 February 2012) === - * Trac #12599: make spkg-install executable. - -=== setuptools-0.6.16 (Francois Bissey, June 1, 2011) === - * Switch to the "distribute" fork of setuptools and update to 0.6.16, - * adopt a Gentoo patch to avoid warnings with python-2.7 (works with 2.6 too). - * Remove the two windows binaries and patch so setup.py doesn't - try to install them. - -=== setuptools-0.6c9 (Mike Hansen, February 19, 2008) === - * Update to 0.6c9 - -=== setuptools-0.6c8.p1 (William Stein, July 7, 2008) === - * Delete two windows binaries (!!) - src/setuptools/gui.exe - src/setuptools/cli.exe - -=== setuptools-0.6c8.p0 (Michael Abshoff, March 14th, 2008) === - * add hg repo - * add .hgignore - * clean up SPKG.txt - * make setuptools a default spkg - -=== setuptools-0.6c8 (Jaap Spies, March 12th, 2008) === - * upgrade to setuptools-0.6c8 - -=== setuptools-0.6.spkg (Jaap Spies) === - * initial release - diff --git a/build/pkgs/setuptools/checksums.ini b/build/pkgs/setuptools/checksums.ini index 845ef67e3c0..da1c43e62d5 100644 --- a/build/pkgs/setuptools/checksums.ini +++ b/build/pkgs/setuptools/checksums.ini @@ -1,4 +1,5 @@ tarball=setuptools-VERSION.zip -sha1=74b0dc738f72a21fe11f16af613166fd5694e436 -md5=1fbcbe45c7fb1d21041e676ba68d2dec -cksum=3373762786 +sha1=f76f68af6d2072882329870e8c022f1b958895cf +md5=6e9de90b242fdd60ef59f497424ce13a +cksum=1518553956 +upstream_url=https://pypi.io/packages/source/s/setuptools/setuptools-VERSION.zip diff --git a/build/pkgs/setuptools/dependencies b/build/pkgs/setuptools/dependencies index 63046883544..304d0c987a2 100644 --- a/build/pkgs/setuptools/dependencies +++ b/build/pkgs/setuptools/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) python3 +$(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/setuptools/package-version.txt b/build/pkgs/setuptools/package-version.txt index 9dd3e2460e8..d9694843ef5 100644 --- a/build/pkgs/setuptools/package-version.txt +++ b/build/pkgs/setuptools/package-version.txt @@ -1 +1 @@ -40.6.3 +47.1.1 diff --git a/build/pkgs/setuptools/spkg-install b/build/pkgs/setuptools/spkg-install deleted file mode 100644 index 800170674b0..00000000000 --- a/build/pkgs/setuptools/spkg-install +++ /dev/null @@ -1,23 +0,0 @@ -# distribute doesn't allow itself to be replaced by setuptools -# so we manually have to delete it -# (pip actually can uninstall setuptools but we may not have pip -# install yet) -rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/setuptools* -rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/distribute* - -export PYTHON_EGG_CACHE="$DOT_SAGE/.python-eggs" - -cd src - -if [ "$SAGE_PYTHON3" = "yes" ]; then - versions="3" -else - versions="2 3" -fi - -# Prevent setuptools from installing itself with easy_install -for vers in $versions; do - python${vers} setup.py --no-user-cfg install \ - --single-version-externally-managed --root="$SAGE_DESTDIR" || \ - sdh_die "Error building / installing setuptools for Python ${vers}" -done diff --git a/build/pkgs/setuptools/spkg-install.in b/build/pkgs/setuptools/spkg-install.in new file mode 100644 index 00000000000..efc63964f5c --- /dev/null +++ b/build/pkgs/setuptools/spkg-install.in @@ -0,0 +1,19 @@ +# distribute doesn't allow itself to be replaced by setuptools +# so we manually have to delete it +# (pip actually can uninstall setuptools but we may not have pip +# install yet) +rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/setuptools* +rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/distribute* + +export PYTHON_EGG_CACHE="$DOT_SAGE/.python-eggs" + +cd src + +versions=3 + +# Prevent setuptools from installing itself with easy_install +for vers in $versions; do + python${vers} setup.py --no-user-cfg install \ + --single-version-externally-managed --root="$SAGE_DESTDIR" || \ + sdh_die "Error building / installing setuptools for Python ${vers}" +done diff --git a/build/pkgs/setuptools_scm/SPKG.rst b/build/pkgs/setuptools_scm/SPKG.rst new file mode 100644 index 00000000000..4ed3f3da711 --- /dev/null +++ b/build/pkgs/setuptools_scm/SPKG.rst @@ -0,0 +1,7 @@ +setuptools_scm +============== + +Description +----------- + +the blessed package to manage your versions by scm tags diff --git a/build/pkgs/setuptools_scm/SPKG.txt b/build/pkgs/setuptools_scm/SPKG.txt deleted file mode 100644 index 23567beee69..00000000000 --- a/build/pkgs/setuptools_scm/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= setuptools_scm = - -== Description == - -the blessed package to manage your versions by scm tags diff --git a/build/pkgs/setuptools_scm/checksums.ini b/build/pkgs/setuptools_scm/checksums.ini index 319eec9fa78..4a07c989b19 100644 --- a/build/pkgs/setuptools_scm/checksums.ini +++ b/build/pkgs/setuptools_scm/checksums.ini @@ -1,4 +1,5 @@ tarball=setuptools_scm-VERSION.tar.gz -sha1=cffffd63429761edece3957321a50fbdb364f043 -md5=52a8dee23c9e5f7d7d18094563db516c -cksum=3085521574 +sha1=464fcfa8c35f0f1e6dcfe79c0d66cfe6f8e4b5ec +md5=e6c9fad17c90516d640868eb833d5150 +cksum=723592301 +upstream_url=https://pypi.io/packages/source/s/setuptools_scm/setuptools_scm-VERSION.tar.gz diff --git a/build/pkgs/setuptools_scm/package-version.txt b/build/pkgs/setuptools_scm/package-version.txt index fd2a01863fd..4d0dcda01c4 100644 --- a/build/pkgs/setuptools_scm/package-version.txt +++ b/build/pkgs/setuptools_scm/package-version.txt @@ -1 +1 @@ -3.1.0 +4.1.2 diff --git a/build/pkgs/pytz/spkg-install b/build/pkgs/setuptools_scm/spkg-install.in similarity index 100% rename from build/pkgs/pytz/spkg-install rename to build/pkgs/setuptools_scm/spkg-install.in diff --git a/build/pkgs/simplegeneric/SPKG.rst b/build/pkgs/simplegeneric/SPKG.rst new file mode 100644 index 00000000000..82b468c52ff --- /dev/null +++ b/build/pkgs/simplegeneric/SPKG.rst @@ -0,0 +1,28 @@ +simplegeneric +============= + +Description +----------- + +Simple generic functions (similar to Python's own len(), pickle.dump(), +etc.) + +The simplegeneric module lets you define simple single-dispatch generic +functions, akin to Python's built-in generic functions like len() iter() +and so on. However, instead of using specially-named methods, these +generic functions use simple lookup tables, akin to those used by e.g. +pickle.dump() and other generic functions found in the Python standard +library. + +As you can see from the above examples, generic functions are actually +quite common in Python already, but there is no standard way to create +simple ones. This library attempts to fill that gap, as generic +functions are an excellent alternative to the Visitor pattern, as well +as being a great substitute for most common uses of adaptation. + +This library tries to be the simplest possible implementation of generic +functions, and it therefore eschews the use of multiple or predicate +dispatch, as well as avoiding speedup techniques such as C dispatching +or code generation. But it has absolutely no dependencies, other than +Python 2.4, and the implementation is just a single Python module of +less than 100 lines. diff --git a/build/pkgs/simplegeneric/SPKG.txt b/build/pkgs/simplegeneric/SPKG.txt deleted file mode 100644 index 0fa7cf0a667..00000000000 --- a/build/pkgs/simplegeneric/SPKG.txt +++ /dev/null @@ -1,25 +0,0 @@ -= simplegeneric = - -== Description == - -Simple generic functions (similar to Python's own len(), pickle.dump(), etc.) - -The simplegeneric module lets you define simple single-dispatch generic -functions, akin to Python's built-in generic functions like len() -iter() and so on. However, instead of using specially-named methods, -these generic functions use simple lookup tables, akin to those used by -e.g. pickle.dump() and other generic functions found in the Python -standard library. - -As you can see from the above examples, generic functions are actually -quite common in Python already, but there is no standard way to create -simple ones. This library attempts to fill that gap, as generic -functions are an excellent alternative to the Visitor pattern, as well -as being a great substitute for most common uses of adaptation. - -This library tries to be the simplest possible implementation of -generic functions, and it therefore eschews the use of multiple or -predicate dispatch, as well as avoiding speedup techniques such as C -dispatching or code generation. But it has absolutely no dependencies, -other than Python 2.4, and the implementation is just a single Python -module of less than 100 lines. diff --git a/build/pkgs/simplegeneric/dependencies b/build/pkgs/simplegeneric/dependencies index 719cbd07335..7b38836acd6 100644 --- a/build/pkgs/simplegeneric/dependencies +++ b/build/pkgs/simplegeneric/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) # simplegeneric does not strictly require setuptools, but it does try # to use setuptools if it is available. However, there is a problem diff --git a/build/pkgs/requests/spkg-install b/build/pkgs/simplegeneric/spkg-install.in similarity index 100% rename from build/pkgs/requests/spkg-install rename to build/pkgs/simplegeneric/spkg-install.in diff --git a/build/pkgs/singledispatch/SPKG.txt b/build/pkgs/singledispatch/SPKG.txt deleted file mode 100644 index f056e32320c..00000000000 --- a/build/pkgs/singledispatch/SPKG.txt +++ /dev/null @@ -1,25 +0,0 @@ -= singledispatch = - -== Description == - -This library brings functools.singledispatch from Python 3.4 to -Python 2.6-3.3. - -== License == - -MIT License - -== Upstream Contact == - -Author: Łukasz Langa -Home page: http://docs.python.org/3/library/functools.html#functools.singledispatch - -== Dependencies == - -Python - -== Changelog == - -=== singledispatch-3.4.0.3 (Emmanuel Charpentier, December 1st, 2015) === - - * Trac #19638: initial release, enabling upgrade of rpy2. diff --git a/build/pkgs/singledispatch/checksums.ini b/build/pkgs/singledispatch/checksums.ini deleted file mode 100644 index 73faccd72b0..00000000000 --- a/build/pkgs/singledispatch/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=singledispatch-VERSION.tar.gz -sha1=f93241b06754a612af8bb7aa208c4d1805637022 -md5=af2fc6a3d6cc5a02d0bf54d909785fcb -cksum=2060266165 diff --git a/build/pkgs/singledispatch/dependencies b/build/pkgs/singledispatch/dependencies deleted file mode 100644 index b1ea6cd2264..00000000000 --- a/build/pkgs/singledispatch/dependencies +++ /dev/null @@ -1,4 +0,0 @@ -$(PYTHON) six | setuptools pip ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/singledispatch/package-version.txt b/build/pkgs/singledispatch/package-version.txt deleted file mode 100644 index d47ed3edce1..00000000000 --- a/build/pkgs/singledispatch/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -3.4.0.3.p0 diff --git a/build/pkgs/singledispatch/spkg-install b/build/pkgs/singledispatch/spkg-install deleted file mode 100644 index 4d42d811782..00000000000 --- a/build/pkgs/singledispatch/spkg-install +++ /dev/null @@ -1,14 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing singledispatch ... exiting" - exit 1 -fi diff --git a/build/pkgs/singledispatch/type b/build/pkgs/singledispatch/type deleted file mode 100644 index a6a7b9cd726..00000000000 --- a/build/pkgs/singledispatch/type +++ /dev/null @@ -1 +0,0 @@ -standard diff --git a/build/pkgs/singular/SPKG.rst b/build/pkgs/singular/SPKG.rst new file mode 100644 index 00000000000..af9574e0afd --- /dev/null +++ b/build/pkgs/singular/SPKG.rst @@ -0,0 +1,45 @@ +Singular +======== + +Description +----------- + +Singular is a computer algebra system for polynomial computations, with +special emphasis on commutative and non-commutative algebra, algebraic +geometry, and singularity theory. + +License +------- + +GPLv2 or GPLv3 + + +Upstream Contact +---------------- + +libsingular-devel@mathematik.uni-kl.de + +http://www.singular.uni-kl.de/ + +Dependencies +------------ + +- GNU patch +- readline +- GMP/MPIR +- MPFR +- NTL +- FLINT + + +Special Update/Build Instructions +--------------------------------- + +See spkg-src to create the source tarball. + +Other notes: + +- If the environment variable SAGE_DEBUG is set to "yes", then + omalloc will be replaced by xalloc. The resulting Singular executable + and libsingular library will be slower than with omalloc, but allow + for easier debugging of memory corruptions. diff --git a/build/pkgs/singular/SPKG.txt b/build/pkgs/singular/SPKG.txt deleted file mode 100644 index 6dc588577ba..00000000000 --- a/build/pkgs/singular/SPKG.txt +++ /dev/null @@ -1,36 +0,0 @@ -= Singular = - -== Description == - -Singular is a computer algebra system for polynomial computations, -with special emphasis on commutative and non-commutative algebra, -algebraic geometry, and singularity theory. - -== License == - -GPLv2 or GPLv3 - -== Upstream Contact == - -libsingular-devel@mathematik.uni-kl.de - -http://www.singular.uni-kl.de/ - -== Dependencies == - -* GNU patch -* readline -* GMP/MPIR -* MPFR -* NTL -* FLINT - -== Special Update/Build Instructions == - -See spkg-src to create the source tarball. - -Other notes: - * If the environment variable SAGE_DEBUG is set to "yes", then - omalloc will be replaced by xalloc. The resulting Singular executable - and libsingular library will be slower than with omalloc, but allow - for easier debugging of memory corruptions. diff --git a/build/pkgs/singular/patches/configure-no-ntl-header-check.patch b/build/pkgs/singular/patches/configure-no-ntl-header-check.patch new file mode 100644 index 00000000000..3109e57f4b2 --- /dev/null +++ b/build/pkgs/singular/patches/configure-no-ntl-header-check.patch @@ -0,0 +1,78 @@ +diff --git a/configure b/configure +index 6bc79d9935..f39dcdb95f 100755 +--- a/configure ++++ b/configure +@@ -20713,7 +20713,7 @@ fi + + for NTL_HOME in ${NTL_HOME_PATH} + do +-if test -r "$NTL_HOME/include/NTL/ZZ.h"; then ++## if test -r "$NTL_HOME/include/NTL/ZZ.h"; then + + if test "x$NTL_HOME" != "x/usr"; then + NTL_CPPFLAGS="-I${NTL_HOME}/include" +@@ -20784,9 +20784,9 @@ else + fi + rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +-else +- ntl_found="no" +-fi ++## else ++## ntl_found="no" ++## fi + done + + if test "x$ntl_found" = "xyes" ; then +diff --git a/factory/configure b/factory/configure +index db6423d..c0a2260 100755 +--- a/factory/configure ++++ b/factory/configure +@@ -22277,7 +22277,7 @@ fi + + for NTL_HOME in ${NTL_HOME_PATH} + do +-if test -r "$NTL_HOME/include/NTL/ZZ.h"; then ++## if test -r "$NTL_HOME/include/NTL/ZZ.h"; then + + if test "x$NTL_HOME" != "x/usr"; then + NTL_CPPFLAGS="-I${NTL_HOME}/include" +@@ -22348,9 +22348,9 @@ else + fi + rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +-else +- ntl_found="no" +-fi ++## else ++## ntl_found="no" ++## fi + done + + if test "x$ntl_found" = "xyes" ; then +diff --git a/libpolys/configure b/libpolys/configure +index 41b0928..9a4b9f5 100755 +--- a/libpolys/configure ++++ b/libpolys/configure +@@ -20660,7 +20660,7 @@ fi + + for NTL_HOME in ${NTL_HOME_PATH} + do +-if test -r "$NTL_HOME/include/NTL/ZZ.h"; then ++## if test -r "$NTL_HOME/include/NTL/ZZ.h"; then + + if test "x$NTL_HOME" != "x/usr"; then + NTL_CPPFLAGS="-I${NTL_HOME}/include" +@@ -20731,9 +20731,9 @@ else + fi + rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +-else +- ntl_found="no" +-fi ++## else ++## ntl_found="no" ++## fi + done + + if test "x$ntl_found" = "xyes" ; then diff --git a/build/pkgs/singular/patches/fix-building-with-nodebug.patch b/build/pkgs/singular/patches/fix-building-with-nodebug.patch new file mode 100644 index 00000000000..46b8ab83cec --- /dev/null +++ b/build/pkgs/singular/patches/fix-building-with-nodebug.patch @@ -0,0 +1,31 @@ +From 80a9ffc773542e3329935e5377f6906628be16e6 Mon Sep 17 00:00:00 2001 +From: Yue Ren +Date: Thu, 15 Nov 2018 10:48:24 -0500 +Subject: [PATCH] fix: building with NDEBUG=1, trac ticket 840 + +--- + Singular/dyn_modules/gfanlib/groebnerCone.h | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/Singular/dyn_modules/gfanlib/groebnerCone.h b/Singular/dyn_modules/gfanlib/groebnerCone.h +index cb067250a0..8a212a7b7f 100644 +--- a/Singular/dyn_modules/gfanlib/groebnerCone.h ++++ b/Singular/dyn_modules/gfanlib/groebnerCone.h +@@ -99,12 +99,16 @@ class groebnerCone + */ + groebnerCones tropicalNeighbours() const; + ++ /** ++ * Return 1 if w points is in the dual of the polyhedral cone, 0 otherwise ++ */ ++ bool pointsOutwards(const gfan::ZVector w) const; ++ + /** + * Debug tools. + */ + #ifndef NDEBUG + bool checkFlipConeInput(const gfan::ZVector interiorPoint, const gfan::ZVector facetNormal) const; +- bool pointsOutwards(const gfan::ZVector) const; + #endif + }; + diff --git a/build/pkgs/singular/patches/ntl-check-m4-no-ntl-header-check.patch b/build/pkgs/singular/patches/ntl-check-m4-no-ntl-header-check.patch new file mode 100644 index 00000000000..26916f9c867 --- /dev/null +++ b/build/pkgs/singular/patches/ntl-check-m4-no-ntl-header-check.patch @@ -0,0 +1,26 @@ +diff --git a/m4/ntl-check.m4 b/m4/ntl-check.m4 +index 50af64be6b..ae81557a77 100644 +--- a/m4/ntl-check.m4 ++++ b/m4/ntl-check.m4 +@@ -48,7 +48,7 @@ fi + + for NTL_HOME in ${NTL_HOME_PATH} + do +-if test -r "$NTL_HOME/include/NTL/ZZ.h"; then ++## if test -r "$NTL_HOME/include/NTL/ZZ.h"; then + + if test "x$NTL_HOME" != "x/usr"; then + NTL_CPPFLAGS="-I${NTL_HOME}/include" +@@ -92,9 +92,9 @@ if test -r "$NTL_HOME/include/NTL/ZZ.h"; then + unset NTL_CPPFLAGS + unset NTL_LIBS + ]) +-else +- ntl_found="no" +-fi ++## else ++## ntl_found="no" ++## fi + done + + if test "x$ntl_found" = "xyes" ; then diff --git a/build/pkgs/singular/spkg-check b/build/pkgs/singular/spkg-check.in similarity index 100% rename from build/pkgs/singular/spkg-check rename to build/pkgs/singular/spkg-check.in diff --git a/build/pkgs/singular/spkg-install b/build/pkgs/singular/spkg-install.in similarity index 100% rename from build/pkgs/singular/spkg-install rename to build/pkgs/singular/spkg-install.in diff --git a/build/pkgs/singular_jupyter/SPKG.rst b/build/pkgs/singular_jupyter/SPKG.rst new file mode 100644 index 00000000000..282bbc5f621 --- /dev/null +++ b/build/pkgs/singular_jupyter/SPKG.rst @@ -0,0 +1,21 @@ + +jupyter-kernel-singular +======================= + +Description +----------- + +A Jupyter kernel for singular + +This is a beta version of a jupyter kernel for Singular. + +License +------- + +GPL version 2 or later + + +Upstream Contact +---------------- + +- https://github.com/sebasguts/jupyter_kernel_singular diff --git a/build/pkgs/singular_jupyter/SPKG.txt b/build/pkgs/singular_jupyter/SPKG.txt deleted file mode 100644 index ced40f28a9e..00000000000 --- a/build/pkgs/singular_jupyter/SPKG.txt +++ /dev/null @@ -1,15 +0,0 @@ -= jupyter-kernel-singular = - -== Description == - -A Jupyter kernel for singular - -This is a beta version of a jupyter kernel for Singular. - -== License == - -GPL version 2 or later - -== Upstream Contact == - -* https://github.com/sebasguts/jupyter_kernel_singular diff --git a/build/pkgs/singular_jupyter/dependencies b/build/pkgs/singular_jupyter/dependencies index 1113df615a6..14dc06f7076 100644 --- a/build/pkgs/singular_jupyter/dependencies +++ b/build/pkgs/singular_jupyter/dependencies @@ -1 +1 @@ -$(PYTHON) jupyter_client | pip pysingular ipython ipywidgets +$(PYTHON) jupyter_client | $(PYTHON_TOOLCHAIN) pysingular ipython ipywidgets diff --git a/build/pkgs/rst2ipynb/spkg-install b/build/pkgs/singular_jupyter/spkg-install.in similarity index 100% rename from build/pkgs/rst2ipynb/spkg-install rename to build/pkgs/singular_jupyter/spkg-install.in diff --git a/build/pkgs/sip/SPKG.rst b/build/pkgs/sip/SPKG.rst new file mode 100644 index 00000000000..d84fbc5cbf6 --- /dev/null +++ b/build/pkgs/sip/SPKG.rst @@ -0,0 +1,24 @@ +SIP +=== + +Description +----------- + +Python extension module generator for C and C++ libraries + + +Upstream contact +---------------- + +- https://www.riverbankcomputing.com/software/sip/ +- https://pypi.python.org/pypi/SIP + +License +------- + +SIP is released under the GPL v2, GPL v3 licenses, and under a +license +similar to the BSD license. + +SIP is copyright (c) Riverbank Computing Limited. Its homepage is +https://www.riverbankcomputing.com/software/sip/. diff --git a/build/pkgs/sip/SPKG.txt b/build/pkgs/sip/SPKG.txt deleted file mode 100644 index d253236fc9b..00000000000 --- a/build/pkgs/sip/SPKG.txt +++ /dev/null @@ -1,18 +0,0 @@ -= SIP = - -== Description == - -Python extension module generator for C and C++ libraries - -== Upstream contact == - - https://www.riverbankcomputing.com/software/sip/ - https://pypi.python.org/pypi/SIP - -== License == - - SIP is released under the GPL v2, GPL v3 licenses, and under a license - similar to the BSD license. - - SIP is copyright (c) Riverbank Computing Limited. Its homepage is - https://www.riverbankcomputing.com/software/sip/. diff --git a/build/pkgs/sip/spkg-install b/build/pkgs/sip/spkg-install deleted file mode 100644 index 9b705de13a0..00000000000 --- a/build/pkgs/sip/spkg-install +++ /dev/null @@ -1,28 +0,0 @@ -if [ -z "${SAGE_LOCAL}" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src/ - -sage-python23 configure.py -if [ $? -ne 0 ] -then - echo "Configuration of sip failed" - exit 1 -fi - -${MAKE} -if [ $? -ne 0 ] -then - echo "Build of sip failed" -exit 1 -fi - -${MAKE} install -if [ $? -ne 0 ] -then - echo "Installation of sip failed" - exit 1 -fi diff --git a/build/pkgs/sip/spkg-install.in b/build/pkgs/sip/spkg-install.in new file mode 100644 index 00000000000..f4f905cfb8f --- /dev/null +++ b/build/pkgs/sip/spkg-install.in @@ -0,0 +1,22 @@ +cd src + +sage-python23 configure.py +if [ $? -ne 0 ] +then + echo "Configuration of sip failed" + exit 1 +fi + +${MAKE} +if [ $? -ne 0 ] +then + echo "Build of sip failed" +exit 1 +fi + +${MAKE} install +if [ $? -ne 0 ] +then + echo "Installation of sip failed" + exit 1 +fi diff --git a/build/pkgs/sirocco/SPKG.rst b/build/pkgs/sirocco/SPKG.rst new file mode 100644 index 00000000000..3d88325f106 --- /dev/null +++ b/build/pkgs/sirocco/SPKG.rst @@ -0,0 +1,30 @@ +SIROCCO +======= + +Description +----------- + +sirocco is a library to compute topologically certified root +continuation of bivariate polynomials. + +License +------- + +GPLv3+ + + +SPKG Maintainers +---------------- + +- Miguel Marco + + +Upstream Contact +---------------- + +Miguel Marco (mmarco@unizar.es) + +Dependencies +------------ + +- gcc diff --git a/build/pkgs/sirocco/SPKG.txt b/build/pkgs/sirocco/SPKG.txt deleted file mode 100644 index 7110501049d..00000000000 --- a/build/pkgs/sirocco/SPKG.txt +++ /dev/null @@ -1,21 +0,0 @@ -= SIROCCO = - -== Description == - -sirocco is a library to compute topologically certified root continuation of bivariate polynomials. - -== License == - -GPLv3+ - -== SPKG Maintainers == - -* Miguel Marco - -== Upstream Contact == - -Miguel Marco (mmarco@unizar.es) - -== Dependencies == - -* gcc diff --git a/build/pkgs/sirocco/checksums.ini b/build/pkgs/sirocco/checksums.ini index ae6a5795b55..672a9235128 100644 --- a/build/pkgs/sirocco/checksums.ini +++ b/build/pkgs/sirocco/checksums.ini @@ -1,4 +1,5 @@ tarball=libsirocco-VERSION.tar.gz -sha1=8f36d14574426b4914c77132d03d822788f11603 -md5=e3e775144ae2bf66eed0066e98e167e9 -cksum=1779789142 +sha1=96e16ce48d6d320895b8d1ee8e07b738765f5e8d +md5=44c67dccf82fe3e2c61b5b7a51543718 +cksum=596947378 +upstream_url=https://github.com/miguelmarco/SIROCCO2/releases/download/VERSION/libsirocco-VERSION.tar.gz diff --git a/build/pkgs/sirocco/package-version.txt b/build/pkgs/sirocco/package-version.txt index c9eae59d88c..e9307ca5751 100644 --- a/build/pkgs/sirocco/package-version.txt +++ b/build/pkgs/sirocco/package-version.txt @@ -1 +1 @@ -2.0.p0 +2.0.2 diff --git a/build/pkgs/sirocco/spkg-install b/build/pkgs/sirocco/spkg-install.in similarity index 100% rename from build/pkgs/sirocco/spkg-install rename to build/pkgs/sirocco/spkg-install.in diff --git a/build/pkgs/six/SPKG.rst b/build/pkgs/six/SPKG.rst new file mode 100644 index 00000000000..8176852b344 --- /dev/null +++ b/build/pkgs/six/SPKG.rst @@ -0,0 +1,24 @@ +six +=== + +Description +----------- + +Python 2 and 3 compatibility utilities + +License +------- + +MIT License + + +Upstream Contact +---------------- + +- Author: Benjamin Peterson +- Home page: http://pypi.python.org/pypi/six/ + +Dependencies +------------ + +Python diff --git a/build/pkgs/six/SPKG.txt b/build/pkgs/six/SPKG.txt deleted file mode 100644 index 97f82a992b2..00000000000 --- a/build/pkgs/six/SPKG.txt +++ /dev/null @@ -1,28 +0,0 @@ -= six = - -== Description == - -Python 2 and 3 compatibility utilities - -== License == - -MIT License - -== Upstream Contact == - -Author: Benjamin Peterson -Home page: http://pypi.python.org/pypi/six/ - -== Dependencies == - -Python - -== Changelog == - -=== six-1.4.1 (John H. Palmieri, 20 December 2013) === - - * Trac #14993: initial release. - -=== six-1.10.0 (Emmanuel Charpentier, December 1st, 2015) === - -* Trac #19638 : upgrade to allow upgrading rpy2. diff --git a/build/pkgs/six/dependencies b/build/pkgs/six/dependencies index 2573414ce8a..15df0c4d6d8 100644 --- a/build/pkgs/six/dependencies +++ b/build/pkgs/six/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/six/spkg-install b/build/pkgs/six/spkg-install deleted file mode 100644 index b056267213c..00000000000 --- a/build/pkgs/six/spkg-install +++ /dev/null @@ -1,14 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo "Error installing six ... exiting" - exit 1 -fi diff --git a/build/pkgs/six/spkg-install.in b/build/pkgs/six/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/six/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/slackware-bootstrap.txt b/build/pkgs/slackware-bootstrap.txt new file mode 100644 index 00000000000..4bd0e6e12b5 --- /dev/null +++ b/build/pkgs/slackware-bootstrap.txt @@ -0,0 +1,4 @@ +# Additional packages needed for ./bootstrap +autoconf +automake +libtool diff --git a/build/pkgs/slackware.txt b/build/pkgs/slackware.txt new file mode 100644 index 00000000000..33127c08549 --- /dev/null +++ b/build/pkgs/slackware.txt @@ -0,0 +1,17 @@ +binutils +make +guile gc libffi # dependencies of make +"gcc-[0-9]" # So that slackpkg pattern matching does not pull in all gcc-* packages +gcc-g++ +libmpc glibc kernel-headers # dependencies of gcc +perl +m4 +bc +python-2.7 +flex +# for https upstream_url downloads +ca-certificates +pkg-config +gettext-tools +libcroco # gettext dependency (msgfmt) +libxml2 diff --git a/build/pkgs/snowballstemmer/SPKG.rst b/build/pkgs/snowballstemmer/SPKG.rst new file mode 100644 index 00000000000..e1da7151279 --- /dev/null +++ b/build/pkgs/snowballstemmer/SPKG.rst @@ -0,0 +1,29 @@ +snowballstemmer +=============== + +Description +----------- + +This package provides 16 stemmer algorithms (15 + Poerter English +stemmer) generated from Snowball algorithms. + +It includes following language algorithms: + +- Danish +- Dutch +- English (Standard, Porter) +- Finnish +- French +- German +- Hungarian +- Italian +- Norwegian +- Portuguese +- Romanian +- Russian +- Spanish +- Swedish +- Turkish + +This is a pure Python stemming library. If PyStemmer is available, this +module uses it to accelerate. diff --git a/build/pkgs/snowballstemmer/SPKG.txt b/build/pkgs/snowballstemmer/SPKG.txt deleted file mode 100644 index 8a649d28043..00000000000 --- a/build/pkgs/snowballstemmer/SPKG.txt +++ /dev/null @@ -1,25 +0,0 @@ -= snowballstemmer = - -== Description == - -This package provides 16 stemmer algorithms (15 + Poerter English stemmer) generated from Snowball algorithms. - -It includes following language algorithms: - - Danish - Dutch - English (Standard, Porter) - Finnish - French - German - Hungarian - Italian - Norwegian - Portuguese - Romanian - Russian - Spanish - Swedish - Turkish - -This is a pure Python stemming library. If PyStemmer is available, this module uses it to accelerate. diff --git a/build/pkgs/snowballstemmer/dependencies b/build/pkgs/snowballstemmer/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/snowballstemmer/dependencies +++ b/build/pkgs/snowballstemmer/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/scandir/spkg-install b/build/pkgs/snowballstemmer/spkg-install.in similarity index 100% rename from build/pkgs/scandir/spkg-install rename to build/pkgs/snowballstemmer/spkg-install.in diff --git a/build/pkgs/speaklater/SPKG.rst b/build/pkgs/speaklater/SPKG.rst new file mode 100644 index 00000000000..1427f283d7d --- /dev/null +++ b/build/pkgs/speaklater/SPKG.rst @@ -0,0 +1,14 @@ +speaklater +========== + +Description +----------- + +Implements a lazy string for python useful for use with gettext + +A module that provides lazy strings for translations. Basically you get +an object that appears to be a string but changes the value every time +the value is evaluated based on a callable you provide. + +For example you can have a global lazy_gettext function that returns a +lazy string with the value of the current set language. diff --git a/build/pkgs/speaklater/SPKG.txt b/build/pkgs/speaklater/SPKG.txt deleted file mode 100644 index 3143efa59f3..00000000000 --- a/build/pkgs/speaklater/SPKG.txt +++ /dev/null @@ -1,12 +0,0 @@ -= speaklater = - -== Description == - -Implements a lazy string for python useful for use with gettext - -A module that provides lazy strings for translations. Basically you get an -object that appears to be a string but changes the value every time the value -is evaluated based on a callable you provide. - -For example you can have a global lazy_gettext function that returns a lazy -string with the value of the current set language. diff --git a/build/pkgs/speaklater/dependencies b/build/pkgs/speaklater/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/speaklater/dependencies +++ b/build/pkgs/speaklater/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/send2trash/spkg-install b/build/pkgs/speaklater/spkg-install.in similarity index 100% rename from build/pkgs/send2trash/spkg-install rename to build/pkgs/speaklater/spkg-install.in diff --git a/build/pkgs/speaklater/type b/build/pkgs/speaklater/type index a6a7b9cd726..134d9bc32d5 100644 --- a/build/pkgs/speaklater/type +++ b/build/pkgs/speaklater/type @@ -1 +1 @@ -standard +optional diff --git a/build/pkgs/sphinx/SPKG.rst b/build/pkgs/sphinx/SPKG.rst new file mode 100644 index 00000000000..43f741e9441 --- /dev/null +++ b/build/pkgs/sphinx/SPKG.rst @@ -0,0 +1,47 @@ +Sphinx +====== + +Description +----------- + +Sphinx is a tool that makes it easy to create intelligent and beautiful +documentation for Python projects (or other documents consisting of +multiple reStructuredText sources), written by Georg Brandl. It was +originally created to translate the new Python documentation, but has +now been cleaned up in the hope that it will be useful to many other +projects. + +License +------- + +Modified BSD; see e.g. its egg-info file for other options + + +Upstream Contact +---------------- + +- Author: Georg Brandl +- Home Page: http://sphinx.pocoo.org, + see also http://pypi.python.org/pypi/Sphinx + +Dependencies +------------ + +- six >= 1.4 +- Jinja2 >= 2.3 +- Pygments >= 2.0 +- docutils >= 0.11 +- snowballstemmer >= 1.1 +- babel >= 1.3 +- setuptools / distribute +- Python +- GNU patch (shipped with Sage) + + +Special Update/Build Instructions +--------------------------------- + +- The script create_grammar_pickle.py creates the file + Grammar2.7.pickle in site-packages/Sphinx-.../sphinx/pycode/. This + helps to avoid race conditions when building the documentation in + parallel. diff --git a/build/pkgs/sphinx/SPKG.txt b/build/pkgs/sphinx/SPKG.txt deleted file mode 100644 index fce17ba0963..00000000000 --- a/build/pkgs/sphinx/SPKG.txt +++ /dev/null @@ -1,38 +0,0 @@ -= Sphinx = - -== Description == - -Sphinx is a tool that makes it easy to create intelligent and -beautiful documentation for Python projects (or other documents -consisting of multiple reStructuredText sources), written by Georg -Brandl. It was originally created to translate the new Python -documentation, but has now been cleaned up in the hope that it will be -useful to many other projects. - -== License == - -Modified BSD; see e.g. its egg-info file for other options - -== Upstream Contact == - -Author: Georg Brandl -Home Page: http://sphinx.pocoo.org, - see also http://pypi.python.org/pypi/Sphinx - -== Dependencies == - * six >= 1.4 - * Jinja2 >= 2.3 - * Pygments >= 2.0 - * docutils >= 0.11 - * snowballstemmer >= 1.1 - * babel >= 1.3 - * setuptools / distribute - * Python - * GNU patch (shipped with Sage) - -== Special Update/Build Instructions == - - * The script create_grammar_pickle.py creates the file - Grammar2.7.pickle in site-packages/Sphinx-.../sphinx/pycode/. This - helps to avoid race conditions when building the documentation in - parallel. diff --git a/build/pkgs/sphinx/checksums.ini b/build/pkgs/sphinx/checksums.ini index ef7a4b12849..ce072d1bb1e 100644 --- a/build/pkgs/sphinx/checksums.ini +++ b/build/pkgs/sphinx/checksums.ini @@ -1,4 +1,5 @@ tarball=Sphinx-VERSION.tar.gz -sha1=93e715cb5d16d981698caa2d54b3b58596798f5d -md5=554f7a4e752f48b2601e5ef5ab463346 -cksum=2134440192 +sha1=9043e0f324d62a5c47a0773f562d423e66163f63 +md5=6e01aa4ab0f1dcbc8d65cc25c736e3ea +cksum=4106849897 +upstream_url=https://pypi.io/packages/source/s/sphinx/Sphinx-VERSION.tar.gz diff --git a/build/pkgs/sphinx/dependencies b/build/pkgs/sphinx/dependencies index 3e3e5e8656f..af979e75c97 100644 --- a/build/pkgs/sphinx/dependencies +++ b/build/pkgs/sphinx/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip docutils jinja2 pygments six snowballstemmer imagesize babel alabaster requests typing sphinxcontrib_websupport packaging +$(PYTHON) | $(PYTHON_TOOLCHAIN) docutils jinja2 pygments six snowballstemmer imagesize babel alabaster requests sphinxcontrib_websupport sphinxcontrib_applehelp sphinxcontrib_devhelp sphinxcontrib_htmlhelp sphinxcontrib_jsmath sphinxcontrib_qthelp sphinxcontrib_serializinghtml packaging ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sphinx/package-version.txt b/build/pkgs/sphinx/package-version.txt index 991e538f58c..0be0b439969 100644 --- a/build/pkgs/sphinx/package-version.txt +++ b/build/pkgs/sphinx/package-version.txt @@ -1 +1 @@ -1.8.5.p0 +3.1.2.p0 diff --git a/build/pkgs/setuptools_scm/spkg-install b/build/pkgs/sphinx/spkg-install.in similarity index 100% rename from build/pkgs/setuptools_scm/spkg-install rename to build/pkgs/sphinx/spkg-install.in diff --git a/build/pkgs/sphinxcontrib_applehelp/SPKG.txt b/build/pkgs/sphinxcontrib_applehelp/SPKG.txt new file mode 100644 index 00000000000..ed59bda33d2 --- /dev/null +++ b/build/pkgs/sphinxcontrib_applehelp/SPKG.txt @@ -0,0 +1,9 @@ += sphinxcontrib-applehelp = + +== Description == + +Sphinx extension which outputs Apple help book + +== License == + +BSD diff --git a/build/pkgs/sphinxcontrib_applehelp/checksums.ini b/build/pkgs/sphinxcontrib_applehelp/checksums.ini new file mode 100644 index 00000000000..ed55d035f14 --- /dev/null +++ b/build/pkgs/sphinxcontrib_applehelp/checksums.ini @@ -0,0 +1,5 @@ +tarball=sphinxcontrib-applehelp-VERSION.tar.gz +sha1=adfd47917f82a86f30bdf356497baf68b47380bc +md5=3f2de7681e12dde031acee0497c3cc2b +cksum=1142149867 +upstream_url=https://pypi.io/packages/source/s/sphinxcontrib-applehelp/sphinxcontrib-applehelp-VERSION.tar.gz diff --git a/build/pkgs/sphinxcontrib_applehelp/dependencies b/build/pkgs/sphinxcontrib_applehelp/dependencies new file mode 100644 index 00000000000..15df0c4d6d8 --- /dev/null +++ b/build/pkgs/sphinxcontrib_applehelp/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/sphinxcontrib_applehelp/package-version.txt b/build/pkgs/sphinxcontrib_applehelp/package-version.txt new file mode 100644 index 00000000000..6d7de6e6abe --- /dev/null +++ b/build/pkgs/sphinxcontrib_applehelp/package-version.txt @@ -0,0 +1 @@ +1.0.2 diff --git a/build/pkgs/simplegeneric/spkg-install b/build/pkgs/sphinxcontrib_applehelp/spkg-install.in similarity index 100% rename from build/pkgs/simplegeneric/spkg-install rename to build/pkgs/sphinxcontrib_applehelp/spkg-install.in diff --git a/build/pkgs/flask_babel/type b/build/pkgs/sphinxcontrib_applehelp/type similarity index 100% rename from build/pkgs/flask_babel/type rename to build/pkgs/sphinxcontrib_applehelp/type diff --git a/build/pkgs/sphinxcontrib_devhelp/SPKG.txt b/build/pkgs/sphinxcontrib_devhelp/SPKG.txt new file mode 100644 index 00000000000..50e27ba320e --- /dev/null +++ b/build/pkgs/sphinxcontrib_devhelp/SPKG.txt @@ -0,0 +1,9 @@ += sphinxcontrib-devhelp = + +== Description == + +Sphinx extension which outputs Devhelp documents + +== License == + +BSD diff --git a/build/pkgs/sphinxcontrib_devhelp/checksums.ini b/build/pkgs/sphinxcontrib_devhelp/checksums.ini new file mode 100644 index 00000000000..ec2f9b15bbe --- /dev/null +++ b/build/pkgs/sphinxcontrib_devhelp/checksums.ini @@ -0,0 +1,5 @@ +tarball=sphinxcontrib-devhelp-VERSION.tar.gz +sha1=3782815be9e11190fe7c7d697e73369432c56fd6 +md5=94069c5cdb5079c445f5477fa6107016 +cksum=4072264365 +upstream_url=https://pypi.io/packages/source/s/sphinxcontrib-devhelp/sphinxcontrib-devhelp-VERSION.tar.gz diff --git a/build/pkgs/sphinxcontrib_devhelp/dependencies b/build/pkgs/sphinxcontrib_devhelp/dependencies new file mode 100644 index 00000000000..15df0c4d6d8 --- /dev/null +++ b/build/pkgs/sphinxcontrib_devhelp/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/sphinxcontrib_devhelp/package-version.txt b/build/pkgs/sphinxcontrib_devhelp/package-version.txt new file mode 100644 index 00000000000..6d7de6e6abe --- /dev/null +++ b/build/pkgs/sphinxcontrib_devhelp/package-version.txt @@ -0,0 +1 @@ +1.0.2 diff --git a/build/pkgs/singular_jupyter/spkg-install b/build/pkgs/sphinxcontrib_devhelp/spkg-install.in similarity index 100% rename from build/pkgs/singular_jupyter/spkg-install rename to build/pkgs/sphinxcontrib_devhelp/spkg-install.in diff --git a/build/pkgs/flask_oldsessions/type b/build/pkgs/sphinxcontrib_devhelp/type similarity index 100% rename from build/pkgs/flask_oldsessions/type rename to build/pkgs/sphinxcontrib_devhelp/type diff --git a/build/pkgs/sphinxcontrib_htmlhelp/SPKG.txt b/build/pkgs/sphinxcontrib_htmlhelp/SPKG.txt new file mode 100644 index 00000000000..ef225d7bf13 --- /dev/null +++ b/build/pkgs/sphinxcontrib_htmlhelp/SPKG.txt @@ -0,0 +1,9 @@ += sphinxcontrib-htmlhelp = + +== Description == + +Sphinx extension which outputs HTML help book + +== License == + +BSD diff --git a/build/pkgs/sphinxcontrib_htmlhelp/checksums.ini b/build/pkgs/sphinxcontrib_htmlhelp/checksums.ini new file mode 100644 index 00000000000..818e991207c --- /dev/null +++ b/build/pkgs/sphinxcontrib_htmlhelp/checksums.ini @@ -0,0 +1,5 @@ +tarball=sphinxcontrib-htmlhelp-VERSION.tar.gz +sha1=fc695250bc781777203b3a97a7aa7b0eab8a2c51 +md5=f1db7db2a467f08f6292ab0d76e38584 +cksum=1195324208 +upstream_url=https://pypi.io/packages/source/s/sphinxcontrib-htmlhelp/sphinxcontrib-htmlhelp-VERSION.tar.gz diff --git a/build/pkgs/sphinxcontrib_htmlhelp/dependencies b/build/pkgs/sphinxcontrib_htmlhelp/dependencies new file mode 100644 index 00000000000..15df0c4d6d8 --- /dev/null +++ b/build/pkgs/sphinxcontrib_htmlhelp/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/sphinxcontrib_htmlhelp/package-version.txt b/build/pkgs/sphinxcontrib_htmlhelp/package-version.txt new file mode 100644 index 00000000000..21e8796a09d --- /dev/null +++ b/build/pkgs/sphinxcontrib_htmlhelp/package-version.txt @@ -0,0 +1 @@ +1.0.3 diff --git a/build/pkgs/snowballstemmer/spkg-install b/build/pkgs/sphinxcontrib_htmlhelp/spkg-install.in similarity index 100% rename from build/pkgs/snowballstemmer/spkg-install rename to build/pkgs/sphinxcontrib_htmlhelp/spkg-install.in diff --git a/build/pkgs/flask_openid/type b/build/pkgs/sphinxcontrib_htmlhelp/type similarity index 100% rename from build/pkgs/flask_openid/type rename to build/pkgs/sphinxcontrib_htmlhelp/type diff --git a/build/pkgs/sphinxcontrib_jsmath/SPKG.txt b/build/pkgs/sphinxcontrib_jsmath/SPKG.txt new file mode 100644 index 00000000000..243e8ad9955 --- /dev/null +++ b/build/pkgs/sphinxcontrib_jsmath/SPKG.txt @@ -0,0 +1,9 @@ += sphinxcontrib-jsmath = + +== Description == + +Sphinx extension which renders display math in HTML via JavaScript + +== License == + +BSD diff --git a/build/pkgs/sphinxcontrib_jsmath/checksums.ini b/build/pkgs/sphinxcontrib_jsmath/checksums.ini new file mode 100644 index 00000000000..f2fdba79204 --- /dev/null +++ b/build/pkgs/sphinxcontrib_jsmath/checksums.ini @@ -0,0 +1,5 @@ +tarball=sphinxcontrib-jsmath-VERSION.tar.gz +sha1=70348505f159dca801522d6df68230e3c5e749c7 +md5=e45179f0a3608b6766862e0f34c23b62 +cksum=3778623264 +upstream_url=https://pypi.io/packages/source/s/sphinxcontrib-jsmath/sphinxcontrib-jsmath-VERSION.tar.gz diff --git a/build/pkgs/sphinxcontrib_jsmath/dependencies b/build/pkgs/sphinxcontrib_jsmath/dependencies new file mode 100644 index 00000000000..15df0c4d6d8 --- /dev/null +++ b/build/pkgs/sphinxcontrib_jsmath/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/sphinxcontrib_jsmath/package-version.txt b/build/pkgs/sphinxcontrib_jsmath/package-version.txt new file mode 100644 index 00000000000..7dea76edb3d --- /dev/null +++ b/build/pkgs/sphinxcontrib_jsmath/package-version.txt @@ -0,0 +1 @@ +1.0.1 diff --git a/build/pkgs/speaklater/spkg-install b/build/pkgs/sphinxcontrib_jsmath/spkg-install.in similarity index 100% rename from build/pkgs/speaklater/spkg-install rename to build/pkgs/sphinxcontrib_jsmath/spkg-install.in diff --git a/build/pkgs/flask_silk/type b/build/pkgs/sphinxcontrib_jsmath/type similarity index 100% rename from build/pkgs/flask_silk/type rename to build/pkgs/sphinxcontrib_jsmath/type diff --git a/build/pkgs/sphinxcontrib_qthelp/SPKG.txt b/build/pkgs/sphinxcontrib_qthelp/SPKG.txt new file mode 100644 index 00000000000..8c27c1c3003 --- /dev/null +++ b/build/pkgs/sphinxcontrib_qthelp/SPKG.txt @@ -0,0 +1,9 @@ += sphinxcontrib-qthelp = + +== Description == + +Sphinx extension which outputs QtHelp documents + +== License == + +BSD diff --git a/build/pkgs/sphinxcontrib_qthelp/checksums.ini b/build/pkgs/sphinxcontrib_qthelp/checksums.ini new file mode 100644 index 00000000000..e1ff365400e --- /dev/null +++ b/build/pkgs/sphinxcontrib_qthelp/checksums.ini @@ -0,0 +1,5 @@ +tarball=sphinxcontrib-qthelp-VERSION.tar.gz +sha1=3f0d3b111e2a57ae24afc51c518ebc2e9e377cd5 +md5=93216721f3e154cce12d1e9c3307b415 +cksum=3260884791 +upstream_url=https://pypi.io/packages/source/s/sphinxcontrib-qthelp/sphinxcontrib-qthelp-VERSION.tar.gz diff --git a/build/pkgs/sphinxcontrib_qthelp/dependencies b/build/pkgs/sphinxcontrib_qthelp/dependencies new file mode 100644 index 00000000000..15df0c4d6d8 --- /dev/null +++ b/build/pkgs/sphinxcontrib_qthelp/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/sphinxcontrib_qthelp/package-version.txt b/build/pkgs/sphinxcontrib_qthelp/package-version.txt new file mode 100644 index 00000000000..21e8796a09d --- /dev/null +++ b/build/pkgs/sphinxcontrib_qthelp/package-version.txt @@ -0,0 +1 @@ +1.0.3 diff --git a/build/pkgs/sphinx/spkg-install b/build/pkgs/sphinxcontrib_qthelp/spkg-install.in similarity index 100% rename from build/pkgs/sphinx/spkg-install rename to build/pkgs/sphinxcontrib_qthelp/spkg-install.in diff --git a/build/pkgs/functools32/type b/build/pkgs/sphinxcontrib_qthelp/type similarity index 100% rename from build/pkgs/functools32/type rename to build/pkgs/sphinxcontrib_qthelp/type diff --git a/build/pkgs/sphinxcontrib_serializinghtml/SPKG.txt b/build/pkgs/sphinxcontrib_serializinghtml/SPKG.txt new file mode 100644 index 00000000000..bb3c470abf7 --- /dev/null +++ b/build/pkgs/sphinxcontrib_serializinghtml/SPKG.txt @@ -0,0 +1,9 @@ += sphinxcontrib-serializinghtml = + +== Description == + +Sphinx extension which outputs serialized HTML files + +== License == + +BSD diff --git a/build/pkgs/sphinxcontrib_serializinghtml/checksums.ini b/build/pkgs/sphinxcontrib_serializinghtml/checksums.ini new file mode 100644 index 00000000000..ea8ac106822 --- /dev/null +++ b/build/pkgs/sphinxcontrib_serializinghtml/checksums.ini @@ -0,0 +1,5 @@ +tarball=sphinxcontrib-serializinghtml-VERSION.tar.gz +sha1=7d782d9f8fef0a5663fc7732d72847e711f0f18b +md5=518ff437dcb05a74ed32ba19c892ce05 +cksum=3002072803 +upstream_url=https://pypi.io/packages/source/s/sphinxcontrib-serializinghtml/sphinxcontrib-serializinghtml-VERSION.tar.gz diff --git a/build/pkgs/sphinxcontrib_serializinghtml/dependencies b/build/pkgs/sphinxcontrib_serializinghtml/dependencies new file mode 100644 index 00000000000..15df0c4d6d8 --- /dev/null +++ b/build/pkgs/sphinxcontrib_serializinghtml/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/sphinxcontrib_serializinghtml/package-version.txt b/build/pkgs/sphinxcontrib_serializinghtml/package-version.txt new file mode 100644 index 00000000000..65087b4f5ec --- /dev/null +++ b/build/pkgs/sphinxcontrib_serializinghtml/package-version.txt @@ -0,0 +1 @@ +1.1.4 diff --git a/build/pkgs/sphinxcontrib_websupport/spkg-install b/build/pkgs/sphinxcontrib_serializinghtml/spkg-install.in similarity index 100% rename from build/pkgs/sphinxcontrib_websupport/spkg-install rename to build/pkgs/sphinxcontrib_serializinghtml/spkg-install.in diff --git a/build/pkgs/future/type b/build/pkgs/sphinxcontrib_serializinghtml/type similarity index 100% rename from build/pkgs/future/type rename to build/pkgs/sphinxcontrib_serializinghtml/type diff --git a/build/pkgs/sphinxcontrib_websupport/SPKG.rst b/build/pkgs/sphinxcontrib_websupport/SPKG.rst new file mode 100644 index 00000000000..de194491c4d --- /dev/null +++ b/build/pkgs/sphinxcontrib_websupport/SPKG.rst @@ -0,0 +1,16 @@ + +sphinxcontrib-websupport +======================== + +Description +----------- + +Sphinx API for Web Apps + +sphinxcontrib-websupport provides a Python API to easily integrate +Sphinx documentation into your Web application. + +License +------- + +BSD diff --git a/build/pkgs/sphinxcontrib_websupport/SPKG.txt b/build/pkgs/sphinxcontrib_websupport/SPKG.txt deleted file mode 100644 index cda85a6edf6..00000000000 --- a/build/pkgs/sphinxcontrib_websupport/SPKG.txt +++ /dev/null @@ -1,12 +0,0 @@ -= sphinxcontrib-websupport = - -== Description == - -Sphinx API for Web Apps - -sphinxcontrib-webuspport provides a Python API to easily integrate Sphinx -documentation into your Web application. - -== License == - -BSD diff --git a/build/pkgs/sphinxcontrib_websupport/checksums.ini b/build/pkgs/sphinxcontrib_websupport/checksums.ini index 1f0d8d40396..5fd49b7e2ac 100644 --- a/build/pkgs/sphinxcontrib_websupport/checksums.ini +++ b/build/pkgs/sphinxcontrib_websupport/checksums.ini @@ -1,4 +1,5 @@ tarball=sphinxcontrib-websupport-VERSION.tar.gz -sha1=03216c11167b75d2c1b08e34041ad2019c3e7dc9 -md5=ca6435e7b4eb9408df4f54972361e9d3 -cksum=1372914473 +sha1=313f4b764872d36f890e76579985e6aaa732f4ca +md5=4fe4d07afe1556c65182d0437228d7bb +cksum=1830011133 +upstream_url=https://pypi.io/packages/source/s/sphinxcontrib-websupport/sphinxcontrib-websupport-VERSION.tar.gz diff --git a/build/pkgs/sphinxcontrib_websupport/dependencies b/build/pkgs/sphinxcontrib_websupport/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/sphinxcontrib_websupport/dependencies +++ b/build/pkgs/sphinxcontrib_websupport/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sphinxcontrib_websupport/package-version.txt b/build/pkgs/sphinxcontrib_websupport/package-version.txt index 9084fa2f716..6085e946503 100644 --- a/build/pkgs/sphinxcontrib_websupport/package-version.txt +++ b/build/pkgs/sphinxcontrib_websupport/package-version.txt @@ -1 +1 @@ -1.1.0 +1.2.1 diff --git a/build/pkgs/terminado/spkg-install b/build/pkgs/sphinxcontrib_websupport/spkg-install.in similarity index 100% rename from build/pkgs/terminado/spkg-install rename to build/pkgs/sphinxcontrib_websupport/spkg-install.in diff --git a/build/pkgs/sqlalchemy/requirements.txt b/build/pkgs/sqlalchemy/requirements.txt new file mode 100644 index 00000000000..39fb2befb58 --- /dev/null +++ b/build/pkgs/sqlalchemy/requirements.txt @@ -0,0 +1 @@ +sqlalchemy diff --git a/build/pkgs/sqlalchemy/type b/build/pkgs/sqlalchemy/type index a1b589e38a3..134d9bc32d5 100644 --- a/build/pkgs/sqlalchemy/type +++ b/build/pkgs/sqlalchemy/type @@ -1 +1 @@ -pip +optional diff --git a/build/pkgs/sqlite/SPKG.rst b/build/pkgs/sqlite/SPKG.rst new file mode 100644 index 00000000000..f4b5d69f9a7 --- /dev/null +++ b/build/pkgs/sqlite/SPKG.rst @@ -0,0 +1,30 @@ +SQLite +====== + +Description +----------- + +SQLite is a software library that implements a self-contained, +serverless, zero-configuration, transactional SQL database engine. + +License +------- + +Public Domain + + +Upstream contact +---------------- + +- http://www.sqlite.org + +Dependencies +------------ + +- readline + + +Special Update/Build Instructions +--------------------------------- + +- Use the autoconf version of sqlite. diff --git a/build/pkgs/sqlite/SPKG.txt b/build/pkgs/sqlite/SPKG.txt deleted file mode 100644 index 23698c87b59..00000000000 --- a/build/pkgs/sqlite/SPKG.txt +++ /dev/null @@ -1,22 +0,0 @@ -= SQLite = - -== Description == - -SQLite is a software library that implements a self-contained, -serverless, zero-configuration, transactional SQL database engine. - -== License == - -Public Domain - -== Upstream contact == - - * http://www.sqlite.org - -== Dependencies == - -* readline - -== Special Update/Build Instructions == - -* Use the autoconf version of sqlite. diff --git a/build/pkgs/sqlite/distros/cygwin.txt b/build/pkgs/sqlite/distros/cygwin.txt new file mode 100644 index 00000000000..71eb206df07 --- /dev/null +++ b/build/pkgs/sqlite/distros/cygwin.txt @@ -0,0 +1 @@ +libsqlite3-devel diff --git a/build/pkgs/sqlite/distros/debian.txt b/build/pkgs/sqlite/distros/debian.txt index feb92603e94..7fab23657d6 100644 --- a/build/pkgs/sqlite/distros/debian.txt +++ b/build/pkgs/sqlite/distros/debian.txt @@ -1 +1,2 @@ libsqlite3-dev +sqlite3 diff --git a/build/pkgs/sqlite/distros/fedora.txt b/build/pkgs/sqlite/distros/fedora.txt new file mode 100644 index 00000000000..7eae3155c73 --- /dev/null +++ b/build/pkgs/sqlite/distros/fedora.txt @@ -0,0 +1 @@ +sqlite sqlite-devel diff --git a/build/pkgs/sqlite/distros/gentoo.txt b/build/pkgs/sqlite/distros/gentoo.txt new file mode 100644 index 00000000000..192f54b91b9 --- /dev/null +++ b/build/pkgs/sqlite/distros/gentoo.txt @@ -0,0 +1 @@ +dev-db/sqlite diff --git a/build/pkgs/sqlite/distros/homebrew.txt b/build/pkgs/sqlite/distros/homebrew.txt new file mode 100644 index 00000000000..532c6c608dd --- /dev/null +++ b/build/pkgs/sqlite/distros/homebrew.txt @@ -0,0 +1 @@ +sqlite diff --git a/build/pkgs/sqlite/distros/slackware.txt b/build/pkgs/sqlite/distros/slackware.txt new file mode 100644 index 00000000000..5a9494c03ec --- /dev/null +++ b/build/pkgs/sqlite/distros/slackware.txt @@ -0,0 +1,2 @@ +sqlite +icu4c diff --git a/build/pkgs/sqlite/spkg-install b/build/pkgs/sqlite/spkg-install.in similarity index 100% rename from build/pkgs/sqlite/spkg-install rename to build/pkgs/sqlite/spkg-install.in diff --git a/build/pkgs/subprocess32/SPKG.txt b/build/pkgs/subprocess32/SPKG.txt deleted file mode 100644 index 0bacc96c8a1..00000000000 --- a/build/pkgs/subprocess32/SPKG.txt +++ /dev/null @@ -1,5 +0,0 @@ -= subprocess32 = - -== Description == - -A backport of the subprocess module from Python 3 for use on 2.x diff --git a/build/pkgs/subprocess32/checksums.ini b/build/pkgs/subprocess32/checksums.ini deleted file mode 100644 index 1833f20506d..00000000000 --- a/build/pkgs/subprocess32/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=subprocess32-VERSION.tar.gz -sha1=bfc293afad55733195927a236fd57c099db231b3 -md5=afa0510115f483d668e25aa30502d9bc -cksum=3131639977 diff --git a/build/pkgs/subprocess32/dependencies b/build/pkgs/subprocess32/dependencies deleted file mode 100644 index 3750e1891b6..00000000000 --- a/build/pkgs/subprocess32/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | pip python3 - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/subprocess32/fix_config.py b/build/pkgs/subprocess32/fix_config.py deleted file mode 100644 index 0ed5933e23a..00000000000 --- a/build/pkgs/subprocess32/fix_config.py +++ /dev/null @@ -1,47 +0,0 @@ -# Fix build of subprocess32 -# -# subprocess32 is a backport from Python 3 to Python 2. But only the C -# sources were backported, not the configure script. This way, macros -# like HAVE_DIRFD which come from the Python 3 configure script are -# always undefined. This causes breakage on certain platforms. -# See upstream bug -# https://github.com/google/python-subprocess32/issues/40 -# -# In Sage, we fix this by using the actual pyconfig.h file from our -# Python 3 installation. -# -# This Python script should be run with the Python version where -# subprocess32 will eventually be installed. - - -from sysconfig import get_path -from subprocess import check_output - - -# Path to the Python 3 includes -cmd = "from sysconfig import get_path; print(get_path('include'), end='')" -py3incdir = check_output(["python3", "-c", cmd]) - -# Path to the includes of the Python installation where subprocess32 -# will be installed -incdir = get_path("include") - - -# Create a fake "Python.h" file which includes "pyconfig.h" from -# Python 3 and then includes the real Python.h header -header = ''' -/* Include pyconfig.h from Python 3 */ -#include "{}/pyconfig.h" - -/* Make sure that the Python 2 version of pyconfig.h can also be included */ -#undef Py_PYCONFIG_H - -/* Include the real Python.h file */ -#include "{}/Python.h" -'''.format(py3incdir, incdir) - - -print("NOTE: Using Python 3 configuration to build subprocess32 for Python 2") - -with open("Python.h", "w") as f: - f.write(header) diff --git a/build/pkgs/subprocess32/package-version.txt b/build/pkgs/subprocess32/package-version.txt deleted file mode 100644 index 444877d48fb..00000000000 --- a/build/pkgs/subprocess32/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -3.5.3 diff --git a/build/pkgs/subprocess32/spkg-install b/build/pkgs/subprocess32/spkg-install deleted file mode 100644 index d1734178f3a..00000000000 --- a/build/pkgs/subprocess32/spkg-install +++ /dev/null @@ -1,11 +0,0 @@ -if [ "$SAGE_PYTHON3" = yes ]; then - echo "Skipping subprocess32 since it is not necessary on Python 3" - exit 0 -fi - -cd src - -# See fix_config.py for an explanation -python2 ../fix_config.py || exit $? - -sdh_pip_install . diff --git a/build/pkgs/subprocess32/type b/build/pkgs/subprocess32/type deleted file mode 100644 index a6a7b9cd726..00000000000 --- a/build/pkgs/subprocess32/type +++ /dev/null @@ -1 +0,0 @@ -standard diff --git a/build/pkgs/suitesparse/SPKG.rst b/build/pkgs/suitesparse/SPKG.rst new file mode 100644 index 00000000000..773d8aebfb4 --- /dev/null +++ b/build/pkgs/suitesparse/SPKG.rst @@ -0,0 +1,1089 @@ +SuiteSparse +=========== + +SuiteSparse is a collection of software to deal with sparse matrix. It is +hosted at http://faculty.cse.tamu.edu/davis/suitesparse.html + +This spkg does a minimal install of suitesparse disabling the following + +- metis +- GraphBLAS (need cmake) +- Mongoose (need cmake) + +An external metis package can be used but we just disable its use. + +Patches: + +- The first patch disable the building of package using cmake. +- The second patch make sure we use sage's blas/lapack on OS X. By + default + suitesparse discard any configurations to use the accelerate framework. + +The building of metis is diabled by passing MY_METIS_LIB=none to make +(any value would have done) We also configure cholmod so it doesn't +require metis by passing CHOLMOD_CONFIG=-DNPARTITION to make. + +Other configurations are self explanatory. + +License: because SuiteSparse is a collection, it comes with a variety of +licenses. Find below a copy of the "LICENSES.txt" shipped with +SuiteSparse. + +AMD/Doc/License.txt +------------------- + + AMD, Copyright (c), 1996-2015, Timothy A. Davis, + Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. + + Availability: + + http://www.suitesparse.com + + AMD License: BSD 3-clause: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the organizations to which the authors are + affiliated, nor the names of its contributors may be used to endorse + or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR + ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH + DAMAGE. + +BTF/Doc/License.txt +------------------- + + BTF, Copyright (C) 2004-2013, University of Florida + by Timothy A. Davis and Ekanathan Palamadai. + BTF is also available under other licenses; contact authors for + details. + http://www.suitesparse.com + + BTF is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + BTF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + +CAMD/Doc/License.txt +------------------------ + + CAMD, Copyright (c) by Timothy A. Davis, + Yanqing Chen, + Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. + CAMD is available under alternate licenses, contact T. Davis for + details. + + CAMD License: BSD 3-clause + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the organizations to which the authors are + affiliated, nor the names of its contributors may be used to endorse + or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR + ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH + DAMAGE. + + Availability: + + http://www.suitesparse.com + +CCOLAMD/Doc/License.txt +----------------------- + + CCOLAMD: constrained column approximate minimum degree ordering + Copyright (C) 2005-2016, Univ. of Florida. Authors: Timothy A. Davis, + Sivasankaran Rajamanickam, and Stefan Larimore. Closely based on + COLAMD by + Davis, Stefan Larimore, in collaboration with Esmond Ng, and John + Gilbert. + http://www.suitesparse.com + + CCOLAMD license: BSD 3-clause: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the organizations to which the authors are + affiliated, nor the names of its contributors may be used to endorse + or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR + ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH + DAMAGE. + +CHOLMOD/Doc/License.txt +----------------------- + + ==Check/License.txt== + + CHOLMOD/Check Module. Copyright (C) 2005-2006, Timothy A. Davis + CHOLMOD is + also available under other licenses; contact authors for details. + http://www.suitesparse.com + + Note that this license is for the CHOLMOD/Check module only. + All CHOLMOD modules are licensed separately. + + This Module is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This Module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + + ==Cholesky/License.txt== + + CHOLMOD/Cholesky module, Copyright (C) 2005-2006, Timothy A. Davis. + CHOLMOD is also available under other licenses; contact authors for + details. http://www.suitesparse.com + + Note that this license is for the CHOLMOD/Cholesky module only. + All CHOLMOD modules are licensed separately. + + This Module is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This Module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + + ==Core/License.txt== + + CHOLMOD/Core Module. Copyright (C) 2005-2006, Univ. of Florida. + Author: + Timothy A. Davis. CHOLMOD is also available under other licenses; + contact + authors for details. http://www.suitesparse.com + + Note that this license is for the CHOLMOD/Core module only. + All CHOLMOD modules are licensed separately. + + This Module is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This Module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + + ==Demo/License.txt== + + CHOLMOD/Demo Module. Copyright (C) 2005-2006, Timothy A. Davis. + CHOLMOD + is also available under other licenses; contact authors for details. + http://www.suitesparse.com + + Note that this license is for the CHOLMOD/Demo module only. + All CHOLMOD modules are licensed separately. + + This Module is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This Module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, + USA. + + ==Include/License.txt== + + CHOLMOD/Include/\* files. Copyright (C) 2005-2006, either Univ. of + Florida + or T. Davis, depending on the file. + + Each file is licensed separately, according to the Module for which + it + contains definitions and prototypes: + + Include/cholmod.h LGPL + Include/cholmod_blas.h LGPL + Include/cholmod_camd.h part of Partition module + Include/cholmod_check.h part of Check module + Include/cholmod_cholesky.h part of Cholesky module + Include/cholmod_complexity.h LGPL + Include/cholmod_config.h LGPL + Include/cholmod_core.h part of Core module + Include/cholmod_function.h no license; freely usable, no restrictions + Include/cholmod_gpu.h part of GPU module + Include/cholmod_gpu_kernels.h part of GPU module + Include/cholmod_internal.h LGPL + Include/cholmod_io64.h LGPL + Include/cholmod_matrixops.h part of MatrixOps module + Include/cholmod_modify.h part of Modify module + Include/cholmod_partition.h part of Partition module + Include/cholmod_supernodal.h part of Supernodal module + Include/cholmod_template.h LGPL + + ==MATLAB/License.txt== + + CHOLMOD/MATLAB Module. Copyright (C) 2005-2006, Timothy A. Davis. + CHOLMOD + is also available under other licenses; contact authors for details. + MATLAB(tm) is a Registered Trademark of The MathWorks, Inc. + http://www.suitesparse.com + + Note that this license is for the CHOLMOD/MATLAB module only. + All CHOLMOD modules are licensed separately. + + This Module is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This Module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, + USA. + + ==MatrixOps/License.txt== + + CHOLMOD/MatrixOps Module. Copyright (C) 2005-2006, Timothy A. Davis. + CHOLMOD is also available under other licenses; contact authors for + details. http://www.suitesparse.com + + Note that this license is for the CHOLMOD/MatrixOps module only. + All CHOLMOD modules are licensed separately. + + This Module is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This Module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, + USA. + + ==Modify/License.txt== + + CHOLMOD/Modify Module. Copyright (C) 2005-2006, Timothy A. Davis and + William W. Hager. CHOLMOD is also available under other licenses; + contact + authors for details. http://www.suitesparse.com + + Note that this license is for the CHOLMOD/Modify module only. + All CHOLMOD modules are licensed separately. + + This Module is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This Module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, + USA. + + + ==Partition/License.txt== + + + CHOLMOD/Partition Module. + Copyright (C) 2005-2006, Univ. of Florida. Author: Timothy A. Davis + CHOLMOD is also available under other licenses; contact authors for + details. + http://www.suitesparse.com + + Note that this license is for the CHOLMOD/Partition module only. + All CHOLMOD modules are licensed separately. + + + This Module is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This Module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + + + ==Supernodal/License.txt== + + + CHOLMOD/Supernodal Module. + Copyright (C) 2005-2006, Timothy A. Davis + CHOLMOD is also available under other licenses; contact authors for + details. + http://www.suitesparse.com + + Note that this license is for the CHOLMOD/Supernodal module only. + All CHOLMOD modules are licensed separately. + + + This Module is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This Module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, + USA. + + + ==Tcov/License.txt== + + + CHOLMOD/Tcov Module. Copyright (C) 2005-2006, Timothy A. Davis + CHOLMOD is also available under other licenses; contact authors for + details. + http://www.suitesparse.com + + Note that this license is for the CHOLMOD/Tcov module only. + All CHOLMOD modules are licensed separately. + + + This Module is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This Module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, + USA. + + ==Valgrind/License.txt== + + CHOLMOD/Valgrind Module. Copyright (C) 2005-2006, Timothy A. Davis. + CHOLMOD is also available under other licenses; contact authors for + details. + http://www.suitesparse.com + + Note that this license is for the CHOLMOD/Valgrind module only. + All CHOLMOD modules are licensed separately. + + This Module is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This Module is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, + USA. + +COLAMD/Doc/License.txt +---------------------- + + COLAMD, Copyright 1998-2016, Timothy A. Davis. + http://www.suitesparse.com + http://www.suitesparse.com + + COLAMD License: BSD 3-clause + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the organizations to which the authors are + affiliated, nor the names of its contributors may be used to endorse + or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR + ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH + DAMAGE. + +CSparse/Doc/License.txt +----------------------- + + CSparse: a Concise Sparse matrix package. + Copyright (c) 2006, Timothy A. Davis. + http://www.suitesparse.com + + CSparse is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + CSparse is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + +CXSparse/Doc/License.txt +------------------------ + + CXSparse: a Concise Sparse matrix package - Extended. + Copyright (c) 2006, Timothy A. Davis. + http://www.suitesparse.com + + CXSparse is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + CXSparse is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + +CXSparse_newfiles/Doc/License.txt +--------------------------------- + + CXSparse: a Concise Sparse matrix package - Extended. + Copyright (c) 2006, Timothy A. Davis. + http://www.suitesparse.com + + CXSparse is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + CXSparse is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + +GPUQREngine/Doc/License.txt +--------------------------- + + GPUQREngine Copyright (c) 2013, Timothy A. Davis, Sencer Nuri + Yeralan, + and Sanjay Ranka. + http://www.suitesparse.com + + GPUQREngine 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. + + GPUQREngine is distributed in the hope that it will be useful, but + WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License + along with + this Module; if not, write to the Free Software Foundation, Inc., 51 + Franklin + Street, Fifth Floor, Boston, MA 02110-1301, USA. + +KLU/Doc/License.txt +------------------- + + KLU, Copyright (C) 2004-2013, University of Florida + by Timothy A. Davis and Ekanathan Palamadai. + KLU is also available under other licenses; contact authors for + details. + http://www.suitesparse.com + + KLU is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + KLU is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + +LDL/Doc/License.txt +------------------- + + LDL Copyright (c) 2005-2013 by Timothy A. Davis. + LDL is also available under other licenses; contact the author for + details. + http://www.suitesparse.com + + LDL is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + LDL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + +MATLAB_Tools/Doc/License.txt +---------------------------- + + The MATLAB_Tools collection of packages is + Copyright (c), Timothy A. Davis, All Rights Reserved, + with the exception of the spqr_rank package, which is + Copyright (c), Timothy A. Davis and Les Foster, All Rights Reserved, + + All packages are available under alternative licenses. + Contact the authors for details. + + MATLAB_Tools License, with the exception of SSMULT and + SuiteSparseCollection: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the organizations to which the authors are + affiliated, nor the names of its contributors may be used to endorse + or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR + ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH + DAMAGE. + + SuiteSparseCollection License: + + SuiteSparseCollection 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. + + SuiteSparseCollection is distributed in the hope that it will be + useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this package; if not, write to the Free Software + Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + SSMULT License: + + SSMULT, Copyright (c) 2007-2011, Timothy A. Davis, + http://www.suitesparse.com. + + SSMULT 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. + + SSMULT is distributed in the hope that it will be useful, but WITHOUT + ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License + along + with this package; if not, write to the Free Software Foundation, + Inc., 51 + Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +RBio/Doc/License.txt +-------------------- + + RBio toolbox. Copyright (C) 2006-2009, Timothy A. Davis + RBio is also available under other licenses; contact authors for + details. + http://www.suitesparse.com + + RBio 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. + + RBio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this Module; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + +SPQR/Doc/License.txt +-------------------- + + SPQR, Copyright 2008-2016 by Timothy A. Davis. + All Rights Reserved. + SPQR is available under alternate licenses, contact T. Davis for + details. + + SPQR License: + + Your use or distribution of SPQR or any modified version of + SPQR implies that you agree to this License. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + + Permission is hereby granted to use or copy this program under the + terms of the GNU GPL, provided that the Copyright, this License, + and the Availability of the original version is retained on all + copies. + User documentation of any code that uses this code or any modified + version of this code must cite the Copyright, this License, the + Availability note, and "Used by permission." Permission to modify + the code and to distribute modified code is granted, provided the + Copyright, this License, and the Availability note are retained, + and a notice that the code was modified is included. + + Availability: + + http://www.suitesparse.com + +SuiteSparse_GPURuntime/Doc/License.txt +-------------------------------------- + + SuiteSparse_GPURuntime Copyright (c) 2013-2016, Timothy A. Davis, + Sencer Nuri Yeralan, and Sanjay Ranka. http://www.suitesparse.com + + SuiteSparse_GPURuntime 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. + + SuiteSparse_GPURuntime is distributed in the hope that it will be + useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more + details. + + You should have received a copy of the GNU General Public License + along with + this Module; if not, write to the Free Software Foundation, Inc., 51 + Franklin + Street, Fifth Floor, Boston, MA 02110-1301, USA. + +ssget/Doc/License.txt +--------------------- + + Copyright (c), 2009-2016, Timothy A. Davis, All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the organizations to which the authors are + affiliated, nor the names of its contributors may be used to endorse + or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR + ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH + DAMAGE. + +UMFPACK/Doc/License.txt +----------------------- + + UMFPACK, Copyright 1995-2009 by Timothy A. Davis. + All Rights Reserved. + UMFPACK is available under alternate licenses, contact T. Davis for + details. + + UMFPACK License: + + Your use or distribution of UMFPACK or any modified version of + UMFPACK implies that you agree to this License. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + + Permission is hereby granted to use or copy this program under the + terms of the GNU GPL, provided that the Copyright, this License, + and the Availability of the original version is retained on all + copies. + User documentation of any code that uses this code or any modified + version of this code must cite the Copyright, this License, the + Availability note, and "Used by permission." Permission to modify + the code and to distribute modified code is granted, provided the + Copyright, this License, and the Availability note are retained, + and a notice that the code was modified is included. + + Availability: + + http://www.suitesparse.com + +CSparse/MATLAB/ssget/Doc/License.txt +------------------------------------ + + Copyright (c), 2009-2016, Timothy A. Davis, All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the organizations to which the authors are + affiliated, nor the names of its contributors may be used to endorse + or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR + ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH + DAMAGE. + +CXSparse/MATLAB/ssget/Doc/License.txt +------------------------------------- + + Copyright (c), 2009-2016, Timothy A. Davis, All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the organizations to which the authors are + affiliated, nor the names of its contributors may be used to endorse + or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR + ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + OF SUCH + DAMAGE. + +GraphBLAS/Doc/License.txt +------------------------- + + SuiteSparse:GraphBLAS, Copyright 2017, Timothy A. Davis + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use SuiteSparse:GraphBLAS except in compliance with the + License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + See the License for the specific language governing permissions and + limitations under the License. + + +Mongoose License +---------------- + + Mongoose, Copyright 2018, Timothy A. Davis, Scott P. Kolodziej, + William W. Hager, S. Nuri Yeralan + Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3, 29 June + 2007 diff --git a/build/pkgs/suitesparse/SPKG.txt b/build/pkgs/suitesparse/SPKG.txt deleted file mode 100644 index ff1fa1ca2f2..00000000000 --- a/build/pkgs/suitesparse/SPKG.txt +++ /dev/null @@ -1,967 +0,0 @@ -SuiteSpare is a collection of software to deal with sparse matrix. -It is hosted at http://faculty.cse.tamu.edu/davis/suitesparse.html - -This spkg does a minimal install of suitesparse disabling the following -* metis -* GraphBLAS (need cmake) -* Mongoose (need cmake) - -An external metis package can be used but we just disable its use. - -Patches: -* The first patch disable the building of package using cmake. -* The second patch make sure we use sage's blas/lapack on OS X. By default -suitesparse discard any configurations to use the accelerate framework. - -The building of metis is diabled by passing -MY_METIS_LIB=none to make (any value would have done) -We also configure cholmod so it doesn't require metis by -passing -CHOLMOD_CONFIG=-DNPARTITION to make. - -Other configurations are self explanatory. - -License: because SuiteSparse is a collection, it comes with a variety -of licenses. Find below a copy of the "LICENSES.txt" shipped with -SuiteSparse. - -==> AMD/Doc/License.txt <== - - AMD, Copyright (c), 1996-2015, Timothy A. Davis, - Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. - - Availability: - - http://www.suitesparse.com - - ------------------------------------------------------------------------------- - AMD License: BSD 3-clause: - ------------------------------------------------------------------------------- - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the organizations to which the authors are - affiliated, nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - DAMAGE. - -==> BTF/Doc/License.txt <== - BTF, Copyright (C) 2004-2013, University of Florida - by Timothy A. Davis and Ekanathan Palamadai. - BTF is also available under other licenses; contact authors for details. - http://www.suitesparse.com - - -------------------------------------------------------------------------------- - - BTF is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - BTF is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -==> CAMD/Doc/License.txt <== - - CAMD, Copyright (c) by Timothy A. Davis, - Yanqing Chen, - Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. - CAMD is available under alternate licenses, contact T. Davis for details. - - CAMD License: BSD 3-clause - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the organizations to which the authors are - affiliated, nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - DAMAGE. - - Availability: - - http://www.suitesparse.com - -==> CCOLAMD/Doc/License.txt <== - - CCOLAMD: constrained column approximate minimum degree ordering - Copyright (C) 2005-2016, Univ. of Florida. Authors: Timothy A. Davis, - Sivasankaran Rajamanickam, and Stefan Larimore. Closely based on COLAMD by - Davis, Stefan Larimore, in collaboration with Esmond Ng, and John Gilbert. - http://www.suitesparse.com - - -------------------------------------------------------------------------------- - - CCOLAMD license: BSD 3-clause: - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the organizations to which the authors are - affiliated, nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - DAMAGE. - - -------------------------------------------------------------------------------- - -==> CHOLMOD/Doc/License.txt <== - -------------------------------------------------------------------------------- - ==> Check/License.txt <== - -------------------------------------------------------------------------------- - - CHOLMOD/Check Module. Copyright (C) 2005-2006, Timothy A. Davis CHOLMOD is - also available under other licenses; contact authors for details. - http://www.suitesparse.com - - Note that this license is for the CHOLMOD/Check module only. - All CHOLMOD modules are licensed separately. - - ---------------------------------------------------------------------------- - - This Module is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This Module is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - -------------------------------------------------------------------------------- - ==> Cholesky/License.txt <== - -------------------------------------------------------------------------------- - - CHOLMOD/Cholesky module, Copyright (C) 2005-2006, Timothy A. Davis. - CHOLMOD is also available under other licenses; contact authors for - details. http://www.suitesparse.com - - Note that this license is for the CHOLMOD/Cholesky module only. - All CHOLMOD modules are licensed separately. - - ---------------------------------------------------------------------------- - - - This Module is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This Module is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - -------------------------------------------------------------------------------- - ==> Core/License.txt <== - -------------------------------------------------------------------------------- - - CHOLMOD/Core Module. Copyright (C) 2005-2006, Univ. of Florida. Author: - Timothy A. Davis. CHOLMOD is also available under other licenses; contact - authors for details. http://www.suitesparse.com - - Note that this license is for the CHOLMOD/Core module only. - All CHOLMOD modules are licensed separately. - - - ---------------------------------------------------------------------------- - - - This Module is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This Module is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - -------------------------------------------------------------------------------- - ==> Demo/License.txt <== - -------------------------------------------------------------------------------- - - CHOLMOD/Demo Module. Copyright (C) 2005-2006, Timothy A. Davis. CHOLMOD - is also available under other licenses; contact authors for details. - http://www.suitesparse.com - - Note that this license is for the CHOLMOD/Demo module only. - All CHOLMOD modules are licensed separately. - - - ---------------------------------------------------------------------------- - - - This Module is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This Module is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - - -------------------------------------------------------------------------------- - ==> Include/License.txt <== - -------------------------------------------------------------------------------- - - CHOLMOD/Include/* files. Copyright (C) 2005-2006, either Univ. of Florida - or T. Davis, depending on the file. - - Each file is licensed separately, according to the Module for which it - contains definitions and prototypes: - - Include/cholmod.h LGPL - Include/cholmod_blas.h LGPL - Include/cholmod_camd.h part of Partition module - Include/cholmod_check.h part of Check module - Include/cholmod_cholesky.h part of Cholesky module - Include/cholmod_complexity.h LGPL - Include/cholmod_config.h LGPL - Include/cholmod_core.h part of Core module - Include/cholmod_function.h no license; freely usable, no restrictions - Include/cholmod_gpu.h part of GPU module - Include/cholmod_gpu_kernels.h part of GPU module - Include/cholmod_internal.h LGPL - Include/cholmod_io64.h LGPL - Include/cholmod_matrixops.h part of MatrixOps module - Include/cholmod_modify.h part of Modify module - Include/cholmod_partition.h part of Partition module - Include/cholmod_supernodal.h part of Supernodal module - Include/cholmod_template.h LGPL - - -------------------------------------------------------------------------------- - ==> MATLAB/License.txt <== - -------------------------------------------------------------------------------- - - CHOLMOD/MATLAB Module. Copyright (C) 2005-2006, Timothy A. Davis. CHOLMOD - is also available under other licenses; contact authors for details. - MATLAB(tm) is a Registered Trademark of The MathWorks, Inc. - http://www.suitesparse.com - - Note that this license is for the CHOLMOD/MATLAB module only. - All CHOLMOD modules are licensed separately. - - ---------------------------------------------------------------------------- - - - This Module is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This Module is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - - -------------------------------------------------------------------------------- - ==> MatrixOps/License.txt <== - -------------------------------------------------------------------------------- - - CHOLMOD/MatrixOps Module. Copyright (C) 2005-2006, Timothy A. Davis. - CHOLMOD is also available under other licenses; contact authors for - details. http://www.suitesparse.com - - Note that this license is for the CHOLMOD/MatrixOps module only. - All CHOLMOD modules are licensed separately. - - - ---------------------------------------------------------------------------- - - - This Module is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This Module is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - - -------------------------------------------------------------------------------- - ==> Modify/License.txt <== - -------------------------------------------------------------------------------- - - CHOLMOD/Modify Module. Copyright (C) 2005-2006, Timothy A. Davis and - William W. Hager. CHOLMOD is also available under other licenses; contact - authors for details. http://www.suitesparse.com - - Note that this license is for the CHOLMOD/Modify module only. - All CHOLMOD modules are licensed separately. - - - ---------------------------------------------------------------------------- - - - This Module is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This Module is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - - -------------------------------------------------------------------------------- - ==> Partition/License.txt <== - -------------------------------------------------------------------------------- - - CHOLMOD/Partition Module. - Copyright (C) 2005-2006, Univ. of Florida. Author: Timothy A. Davis - CHOLMOD is also available under other licenses; contact authors for details. - http://www.suitesparse.com - - Note that this license is for the CHOLMOD/Partition module only. - All CHOLMOD modules are licensed separately. - - - ---------------------------------------------------------------------------- - - - This Module is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This Module is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - -------------------------------------------------------------------------------- - ==> Supernodal/License.txt <== - -------------------------------------------------------------------------------- - - CHOLMOD/Supernodal Module. - Copyright (C) 2005-2006, Timothy A. Davis - CHOLMOD is also available under other licenses; contact authors for details. - http://www.suitesparse.com - - Note that this license is for the CHOLMOD/Supernodal module only. - All CHOLMOD modules are licensed separately. - - - ---------------------------------------------------------------------------- - - - This Module is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This Module is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - - -------------------------------------------------------------------------------- - ==> Tcov/License.txt <== - -------------------------------------------------------------------------------- - - CHOLMOD/Tcov Module. Copyright (C) 2005-2006, Timothy A. Davis - CHOLMOD is also available under other licenses; contact authors for details. - http://www.suitesparse.com - - Note that this license is for the CHOLMOD/Tcov module only. - All CHOLMOD modules are licensed separately. - - - ---------------------------------------------------------------------------- - - - This Module is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This Module is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - - -------------------------------------------------------------------------------- - ==> Valgrind/License.txt <== - -------------------------------------------------------------------------------- - - CHOLMOD/Valgrind Module. Copyright (C) 2005-2006, Timothy A. Davis. - CHOLMOD is also available under other licenses; contact authors for details. - http://www.suitesparse.com - - Note that this license is for the CHOLMOD/Valgrind module only. - All CHOLMOD modules are licensed separately. - - - ---------------------------------------------------------------------------- - - - This Module is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This Module is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. - -==> COLAMD/Doc/License.txt <== - COLAMD, Copyright 1998-2016, Timothy A. Davis. http://www.suitesparse.com - http://www.suitesparse.com - - COLAMD License: BSD 3-clause - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the organizations to which the authors are - affiliated, nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - DAMAGE. - -==> CSparse/Doc/License.txt <== - CSparse: a Concise Sparse matrix package. - Copyright (c) 2006, Timothy A. Davis. - http://www.suitesparse.com - - -------------------------------------------------------------------------------- - - CSparse is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - CSparse is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -==> CXSparse/Doc/License.txt <== - CXSparse: a Concise Sparse matrix package - Extended. - Copyright (c) 2006, Timothy A. Davis. - http://www.suitesparse.com - - -------------------------------------------------------------------------------- - - CXSparse is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - CXSparse is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -==> CXSparse_newfiles/Doc/License.txt <== - CXSparse: a Concise Sparse matrix package - Extended. - Copyright (c) 2006, Timothy A. Davis. - http://www.suitesparse.com - - -------------------------------------------------------------------------------- - - CXSparse is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - CXSparse is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -==> GPUQREngine/Doc/License.txt <== - GPUQREngine Copyright (c) 2013, Timothy A. Davis, Sencer Nuri Yeralan, - and Sanjay Ranka. - http://www.suitesparse.com - - GPUQREngine 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. - - GPUQREngine is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - this Module; if not, write to the Free Software Foundation, Inc., 51 Franklin - Street, Fifth Floor, Boston, MA 02110-1301, USA. - - -==> KLU/Doc/License.txt <== - KLU, Copyright (C) 2004-2013, University of Florida - by Timothy A. Davis and Ekanathan Palamadai. - KLU is also available under other licenses; contact authors for details. - http://www.suitesparse.com - - -------------------------------------------------------------------------------- - - KLU is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - KLU is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -==> LDL/Doc/License.txt <== - LDL Copyright (c) 2005-2013 by Timothy A. Davis. - LDL is also available under other licenses; contact the author for details. - http://www.suitesparse.com - - -------------------------------------------------------------------------------- - - LDL is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - LDL is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -==> MATLAB_Tools/Doc/License.txt <== - The MATLAB_Tools collection of packages is - Copyright (c), Timothy A. Davis, All Rights Reserved, - with the exception of the spqr_rank package, which is - Copyright (c), Timothy A. Davis and Les Foster, All Rights Reserved, - - All packages are available under alternative licenses. - Contact the authors for details. - - -------------------------------------------------------------------------------- - MATLAB_Tools License, with the exception of SSMULT and SuiteSparseCollection: - -------------------------------------------------------------------------------- - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the organizations to which the authors are - affiliated, nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - DAMAGE. - - -------------------------------------------------------------------------------- - SuiteSparseCollection License: - -------------------------------------------------------------------------------- - - SuiteSparseCollection 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. - - SuiteSparseCollection is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this package; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - -------------------------------------------------------------------------------- - SSMULT License: - -------------------------------------------------------------------------------- - - SSMULT, Copyright (c) 2007-2011, Timothy A. Davis, - http://www.suitesparse.com. - - SSMULT 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. - - SSMULT is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along - with this package; if not, write to the Free Software Foundation, Inc., 51 - Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - -==> RBio/Doc/License.txt <== - RBio toolbox. Copyright (C) 2006-2009, Timothy A. Davis - RBio is also available under other licenses; contact authors for details. - http://www.suitesparse.com - - -------------------------------------------------------------------------------- - - RBio 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. - - RBio is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this Module; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - -==> SPQR/Doc/License.txt <== - SPQR, Copyright 2008-2016 by Timothy A. Davis. - All Rights Reserved. - SPQR is available under alternate licenses, contact T. Davis for details. - - SPQR License: - - Your use or distribution of SPQR or any modified version of - SPQR implies that you agree to this License. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 - USA - - Permission is hereby granted to use or copy this program under the - terms of the GNU GPL, provided that the Copyright, this License, - and the Availability of the original version is retained on all copies. - User documentation of any code that uses this code or any modified - version of this code must cite the Copyright, this License, the - Availability note, and "Used by permission." Permission to modify - the code and to distribute modified code is granted, provided the - Copyright, this License, and the Availability note are retained, - and a notice that the code was modified is included. - - Availability: - - http://www.suitesparse.com - - -==> SuiteSparse_GPURuntime/Doc/License.txt <== - SuiteSparse_GPURuntime Copyright (c) 2013-2016, Timothy A. Davis, - Sencer Nuri Yeralan, and Sanjay Ranka. http://www.suitesparse.com - - -------------------------------------------------------------------------------- - - SuiteSparse_GPURuntime 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. - - SuiteSparse_GPURuntime is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - this Module; if not, write to the Free Software Foundation, Inc., 51 Franklin - Street, Fifth Floor, Boston, MA 02110-1301, USA. - -==> ssget/Doc/License.txt <== - Copyright (c), 2009-2016, Timothy A. Davis, All Rights Reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the organizations to which the authors are - affiliated, nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - DAMAGE. - - -==> UMFPACK/Doc/License.txt <== - UMFPACK, Copyright 1995-2009 by Timothy A. Davis. - All Rights Reserved. - UMFPACK is available under alternate licenses, contact T. Davis for details. - - UMFPACK License: - - Your use or distribution of UMFPACK or any modified version of - UMFPACK implies that you agree to this License. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 - USA - - Permission is hereby granted to use or copy this program under the - terms of the GNU GPL, provided that the Copyright, this License, - and the Availability of the original version is retained on all copies. - User documentation of any code that uses this code or any modified - version of this code must cite the Copyright, this License, the - Availability note, and "Used by permission." Permission to modify - the code and to distribute modified code is granted, provided the - Copyright, this License, and the Availability note are retained, - and a notice that the code was modified is included. - - Availability: - - http://www.suitesparse.com - - -==> CSparse/MATLAB/ssget/Doc/License.txt <== - Copyright (c), 2009-2016, Timothy A. Davis, All Rights Reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the organizations to which the authors are - affiliated, nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - DAMAGE. - - -==> CXSparse/MATLAB/ssget/Doc/License.txt <== - Copyright (c), 2009-2016, Timothy A. Davis, All Rights Reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the organizations to which the authors are - affiliated, nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - DAMAGE. - -==> GraphBLAS/Doc/License.txt <== - SuiteSparse:GraphBLAS, Copyright 2017, Timothy A. Davis - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use SuiteSparse:GraphBLAS except in compliance with the - License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -==> Mongoose License <== - Mongoose, Copyright 2018, Timothy A. Davis, Scott P. Kolodziej, - William W. Hager, S. Nuri Yeralan - Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3, 29 June 2007 - - diff --git a/build/pkgs/suitesparse/distros/arch.txt b/build/pkgs/suitesparse/distros/arch.txt new file mode 100644 index 00000000000..8309d12f520 --- /dev/null +++ b/build/pkgs/suitesparse/distros/arch.txt @@ -0,0 +1 @@ +suitesparse diff --git a/build/pkgs/suitesparse/distros/conda.txt b/build/pkgs/suitesparse/distros/conda.txt new file mode 100644 index 00000000000..8309d12f520 --- /dev/null +++ b/build/pkgs/suitesparse/distros/conda.txt @@ -0,0 +1 @@ +suitesparse diff --git a/build/pkgs/suitesparse/distros/cygwin.txt b/build/pkgs/suitesparse/distros/cygwin.txt new file mode 100644 index 00000000000..fe7b2c3f23c --- /dev/null +++ b/build/pkgs/suitesparse/distros/cygwin.txt @@ -0,0 +1 @@ +libsuitesparseconfig-devel diff --git a/build/pkgs/suitesparse/distros/debian.txt b/build/pkgs/suitesparse/distros/debian.txt new file mode 100644 index 00000000000..f6e0709fedf --- /dev/null +++ b/build/pkgs/suitesparse/distros/debian.txt @@ -0,0 +1 @@ +libsuitesparse-dev diff --git a/build/pkgs/suitesparse/distros/fedora.txt b/build/pkgs/suitesparse/distros/fedora.txt new file mode 100644 index 00000000000..473f5afeb82 --- /dev/null +++ b/build/pkgs/suitesparse/distros/fedora.txt @@ -0,0 +1 @@ +suitesparse suitesparse-devel diff --git a/build/pkgs/suitesparse/distros/gentoo.txt b/build/pkgs/suitesparse/distros/gentoo.txt new file mode 100644 index 00000000000..3b1b625f3e6 --- /dev/null +++ b/build/pkgs/suitesparse/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/amd sci-libs/cholmod sci-libs/suitesparseconfig sci-libs/umfpack diff --git a/build/pkgs/suitesparse/distros/homebrew.txt b/build/pkgs/suitesparse/distros/homebrew.txt new file mode 100644 index 00000000000..55da6065f71 --- /dev/null +++ b/build/pkgs/suitesparse/distros/homebrew.txt @@ -0,0 +1 @@ +suite-sparse diff --git a/build/pkgs/suitesparse/distros/opensuse.txt b/build/pkgs/suitesparse/distros/opensuse.txt new file mode 100644 index 00000000000..8309d12f520 --- /dev/null +++ b/build/pkgs/suitesparse/distros/opensuse.txt @@ -0,0 +1 @@ +suitesparse diff --git a/build/pkgs/suitesparse/spkg-configure.m4 b/build/pkgs/suitesparse/spkg-configure.m4 new file mode 100644 index 00000000000..c22a8a3e94f --- /dev/null +++ b/build/pkgs/suitesparse/spkg-configure.m4 @@ -0,0 +1,24 @@ +SAGE_SPKG_CONFIGURE([suitesparse], [ + SAGE_SPKG_DEPCHECK([openblas], [ + AC_SEARCH_LIBS([cholmod_speye], [cholmod], [ + AC_SEARCH_LIBS([umfpack_di_solve], [umfpack], [ + AC_SEARCH_LIBS([SuiteSparse_version], [suitesparseconfig], [ + AC_CHECK_HEADERS([suitesparse/SuiteSparse_config.h SuiteSparse_config.h], [ + AC_CHECK_HEADERS([suitesparse/amd.h amd.h], [sage_spkg_install_suitesparse=no; break], + [sage_spkg_install_suitesparse=yes]) + break + ], [sage_spkg_install_suitesparse=yes]) + ], [sage_spkg_install_suitesparse=yes]) + ], [ + sage_spkg_install_suitesparse=yes]) + ], [ + sage_spkg_install_suitesparse=yes]) + ], [ + sage_spkg_install_suitesparse=yes]) +], [], [], [ + AS_IF([test x$sage_spkg_install_suitesparse = xyes], [ + AC_SUBST(SAGE_SUITESPARSE_LOCALINSTALL, ['$SAGE_LOCAL']) + ], [ + AC_SUBST(SAGE_SUITESPARSE_LOCALINSTALL, ['']) + ]) +]) diff --git a/build/pkgs/suitesparse/spkg-install b/build/pkgs/suitesparse/spkg-install.in similarity index 100% rename from build/pkgs/suitesparse/spkg-install rename to build/pkgs/suitesparse/spkg-install.in diff --git a/build/pkgs/surf/SPKG.rst b/build/pkgs/surf/SPKG.rst new file mode 100644 index 00000000000..1e2118c35ed --- /dev/null +++ b/build/pkgs/surf/SPKG.rst @@ -0,0 +1,39 @@ +surf +==== + +Description +----------- + +surf is a tool to visualize some real algebraic geometry: plane +algebraic curves, algebraic surfaces and hyperplane sections of +surfaces. surf is script driven and has (optionally) a nifty GUI using +the Gtk widget set. + +This is used by the Singular Jupyter kernel to produce 3D plots. + +License +------- + +GPL version 2 or later + + +Upstream Contact +---------------- + +http://surf.sourceforge.net (although the project is essentially dead) + +Dependencies +------------ + +- cups (optional) +- GNU flex Version 2.5 or higher +- GTK+ Version 1.2.0 or higher (optional) +- POSIX Threads +- GNU MP(gmp) Version 2 or higher +- lib-tiff +- lib-jpeg +- zlib +- ps2pdf (optional) + +This package is "experimental" because not all of these dependencies are +packaged with Sage. diff --git a/build/pkgs/surf/SPKG.txt b/build/pkgs/surf/SPKG.txt deleted file mode 100644 index 4967aa2cc72..00000000000 --- a/build/pkgs/surf/SPKG.txt +++ /dev/null @@ -1,33 +0,0 @@ -= surf = - -== Description == - -surf is a tool to visualize some real algebraic geometry: plane algebraic -curves, algebraic surfaces and hyperplane sections of surfaces. surf is script -driven and has (optionally) a nifty GUI using the Gtk widget set. - -This is used by the Singular Jupyter kernel to produce 3D plots. - -== License == - -GPL version 2 or later - -== Upstream Contact == - -http://surf.sourceforge.net -(although the project is essentially dead) - -== Dependencies == - -* cups (optional) -* GNU flex Version 2.5 or higher -* GTK+ Version 1.2.0 or higher (optional) -* POSIX Threads -* GNU MP(gmp) Version 2 or higher -* lib-tiff -* lib-jpeg -* zlib -* ps2pdf (optional) - -This package is "experimental" because not all of these dependencies are -packaged with Sage. diff --git a/build/pkgs/surf/spkg-install b/build/pkgs/surf/spkg-install.in similarity index 100% rename from build/pkgs/surf/spkg-install rename to build/pkgs/surf/spkg-install.in diff --git a/build/pkgs/symmetrica/SPKG.rst b/build/pkgs/symmetrica/SPKG.rst new file mode 100644 index 00000000000..bd82eda45f7 --- /dev/null +++ b/build/pkgs/symmetrica/SPKG.rst @@ -0,0 +1,57 @@ +symmetrica +========== + +Description +----------- + +Symmetrica is a program developed by Lehrstuhl Mathematik II of the +University of Bayreuth. It has routines to handle the following topics + +- ordinary representation theory of the symmetric group and related + groups (2/11/04) +- ordinary representation theory of the classical groups +- modular representation theory of the symmetric group +- projective representation theory of the symmetric group +- combinatorics of tableaux +- symmetric functions and polynomials (7/22/04) +- commutative and non commutative Schubert polynomials +- operations of finite groups. +- ordinary representation theory of Hecke algebras of type A_n + +For more details check http://www.symmetrica.de (currently redirects to +http://www.algorithm.uni-bayreuth.de/en/research/SYMMETRICA) + +License +------- + +Public Domain (see the above web site) + + +Upstream Contact +---------------- + +- Axel Kohnert - see http://www.mathe2.uni-bayreuth.de/axel/ + +Dependencies +------------ + +- GNU patch (for applying the patches to upstream) + + +Special Update/Build Instructions +--------------------------------- + +The following patches are applied in spkg-install: + +- ``bruch.patch``: store integers in a temporary variable before freeing + memory +- ``de.patch``: turn off banner +- ``int32.patch``: use ``int32_t`` and ``uint32_t`` for type INT. +- ``sort_sum_rename.patch``: rename ``sort`` to ``sym_sort``, ``sum`` to ``sym_sum`` +- We copy over our own ``Makefile``: + + ``patches/makefile`` (Fix compiler, i.e., use ``$CC``, and let it use + ``$CFLAGS``.) + +Permissions in the upstream tarball are funky, please run ``chmod 644 +src/*`` after unpacking. diff --git a/build/pkgs/symmetrica/SPKG.txt b/build/pkgs/symmetrica/SPKG.txt deleted file mode 100644 index 04e978bc418..00000000000 --- a/build/pkgs/symmetrica/SPKG.txt +++ /dev/null @@ -1,100 +0,0 @@ -= symmetrica = - -== Description == - -Symmetrica is a program developed by Lehrstuhl Mathematik II of the -University of Bayreuth. It has routines to handle the following topics - - * ordinary representation theory of the symmetric group and related groups (2/11/04) - * ordinary representation theory of the classical groups - * modular representation theory of the symmetric group - * projective representation theory of the symmetric group - * combinatorics of tableaux - * symmetric functions and polynomials (7/22/04) - * commutative and non commutative Schubert polynomials - * operations of finite groups. - * ordinary representation theory of Hecke algebras of type A_n - -For more details check http://www.symmetrica.de (currently redirects -to http://www.algorithm.uni-bayreuth.de/en/research/SYMMETRICA) - -== License == - -Public Domain (see the above web site) - -== Upstream Contact == - * Axel Kohnert - see http://www.mathe2.uni-bayreuth.de/axel/ - -== Dependencies == - * GNU patch (for applying the patches to upstream) - -== Special Update/Build Instructions == - -The following patches are applied in spkg-install: - - * bruch.patch: store integers in a temporary variable before freeing memory - * de.patch: turn off banner - * int32.patch: use int32_t and uint32_t for type INT. - * sort_sum_rename.patch: rename sort to sym_sort, sum to sym_sum - * We copy over our own Makefile: - patches/makefile (Fix compiler, i.e., use $CC, and let it use $CFLAGS.) - -Permissions in the upstream tarball are funky, please run -"chmod 644 src/*" after unpacking. - -== Changelog == - -=== symmetrica-2.0.p9 (Mike Zabrocki, 22 October 2013) === - * #15312: fix instances of accessing pointers to memory which was free'd - -=== symmetrica-2.0.p8 (Jeroen Demeyer, 17 October 2013) === - * #13413: fix integer overflow bug on 64-bit systems, see int32.patch. - * Remove macro.h.patch (no longer needed due to change in Sage library) - * Use standard spkg-install template for applying patches. - -=== symmetrica-2.0.p7 (Leif Leonhardy, October 6th 2011) === - #10719 (Fix linker errors on OpenSUSE 11.2 and Ubuntu 11.10): - Additional reviewer changes: - * Add more error checks, normalize error messages. - * Set up flags in spkg-check as well, as we build the test - program there. Also use $MAKE there. Put CFLAG64 into - LDFLAGS if appropriate (i.e. SAGE64=yes). - * Clean up our Makefile, also use LDFLAGS when linking the test program. - * Add GNU patch to the dependencies. - -=== symmetrica-2.0.p6 (Volker Braun, 28th September 2011) === - * #10719: Change -lm option order in Makefile. - * Removed dist/ directory (obsolete Debian stuff). - * src/ is now the pristine upstream source, patches are applied in - spkg-install. - * Added spkg-check. - -=== symmetrica-2.0.p5 (David Kirkby, 6th January 2009) === - * Allow SAGE64 to work on any platform, not just OS X. - * Update the makefile to use '$(CC)' rather than use 'gcc' - -=== symmetrica-2.0.p4 () === - * ??????????????????????? - -=== symmetrica-2.0.p3 (Michael Abshoff, May 15th, 2009) === - * Work around Solaris linker problem - * Apply patches to src directly against policy - see above - -=== symmetrica-2.0.p2 (Michael Abshoff, April 3rd, 2008) === - * OS 64 bit build support - * make sure SAGE_ROOT is defined - * Fix FreeBSD build issue - * Build symmetrica with -fPIC on Debian (Tim Abbott, #2791) - -=== symmetrica-2.0.p1 (Tim Abbott) === - * Add Debian build support - -=== symmetrica-2.0.p0 (Mike Hansen) === - * Change compile flags to "-O1" to reduce compile time by 2/3. - -=== symmetrica-2.0 (Mike Hansen) === - * update to latest release - -=== symmetrica-0.3.3 (Mike Hansen) === - * package ancient release - diff --git a/build/pkgs/symmetrica/distros/gentoo.txt b/build/pkgs/symmetrica/distros/gentoo.txt new file mode 100644 index 00000000000..d7d860b9c7e --- /dev/null +++ b/build/pkgs/symmetrica/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/symmetrica diff --git a/build/pkgs/symmetrica/spkg-check b/build/pkgs/symmetrica/spkg-check deleted file mode 100644 index 249a6203ef8..00000000000 --- a/build/pkgs/symmetrica/spkg-check +++ /dev/null @@ -1,32 +0,0 @@ -if [ -z "$SAGE_LOCAL" ] ; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -CFLAGS="-O2 -g $CFLAGS -DFAST -DALLTRUE" -export CFLAGS LDFLAGS # Currently redundant, but safe. - - -cd src - -$MAKE test # Just *builds* an example / test program. -if [ $? -ne 0 ]; then - echo >&2 "Error building Symmetrica's test program." - exit 1 -fi - -echo 123 | ./test > spkg-check.actual - -cat < spkg-check.expected - 12.146304.367025.329675.766243.241881.295855.454217.088483.382315. - 328918.161829.235892.362167.668831.156960.612640.202170.735835.221294. - 047782.591091.570411.651472.186029.519906.261646.730733.907419.814952. - 960000.000000.000000.000000.000000 -EOF - -if ! diff -b spkg-check.actual spkg-check.expected; then - echo >&2 "Error: The Symmetrica check failed." - exit 1 -fi -echo "The Symmetrica check passed." diff --git a/build/pkgs/symmetrica/spkg-check.in b/build/pkgs/symmetrica/spkg-check.in new file mode 100644 index 00000000000..a912bd9866c --- /dev/null +++ b/build/pkgs/symmetrica/spkg-check.in @@ -0,0 +1,26 @@ +CFLAGS="-O2 -g $CFLAGS -DFAST -DALLTRUE" +export CFLAGS LDFLAGS # Currently redundant, but safe. + + +cd src + +$MAKE test # Just *builds* an example / test program. +if [ $? -ne 0 ]; then + echo >&2 "Error building Symmetrica's test program." + exit 1 +fi + +echo 123 | ./test > spkg-check.actual + +cat < spkg-check.expected + 12.146304.367025.329675.766243.241881.295855.454217.088483.382315. + 328918.161829.235892.362167.668831.156960.612640.202170.735835.221294. + 047782.591091.570411.651472.186029.519906.261646.730733.907419.814952. + 960000.000000.000000.000000.000000 +EOF + +if ! diff -b spkg-check.actual spkg-check.expected; then + echo >&2 "Error: The Symmetrica check failed." + exit 1 +fi +echo "The Symmetrica check passed." diff --git a/build/pkgs/symmetrica/spkg-configure.m4 b/build/pkgs/symmetrica/spkg-configure.m4 index 019256e01a5..7dc77df703e 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);] + [fprintln(stderr, 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/build/pkgs/symmetrica/spkg-install b/build/pkgs/symmetrica/spkg-install.in similarity index 100% rename from build/pkgs/symmetrica/spkg-install rename to build/pkgs/symmetrica/spkg-install.in diff --git a/build/pkgs/sympow/SPKG.rst b/build/pkgs/sympow/SPKG.rst new file mode 100644 index 00000000000..4b3dd774afe --- /dev/null +++ b/build/pkgs/sympow/SPKG.rst @@ -0,0 +1,64 @@ +sympow +====== + +Description +----------- + +SYMPOW is a package to compute special values of symmetric power +elliptic curve L-functions. It can compute up to about 64 digits of +precision. + +License +------- + +- See the file src/COPYING + + +Upstream Contact +---------------- + +SYMPOW does not appear to be maintained any longer, so there is no +upstream web site. +Mark Watkins, the package author, now works at Magma. +Previous (possibly still usable) email is watkins@maths.usyd.edu.au + +Dependencies +------------ + +- GNU patch + + +Special Update/Build Instructions +--------------------------------- + +- Some of the code is very dubious, and it is anyones guess really what + the compiler does with it. For example, the following line exists in + src/eulerfactors.c: + + if ((HECKE) && (d==1)) return hecke_good(p,ap,m,v); + + But since hecke_good is defined as returning void, it's hard to know + exactly how this code behaves. I would not be surprised by any bugs + that might show up. I (David Kirkby) would personally not trust this + code much at all. + +- This is a difficult package to maintain. A trac ticket (#9758) has + been + opened to implement Watkins-Delaunay's algorithm for computing + modular + degrees in Sage. Once implemented, it should be possible to remove + this + package. + +- The package is configured such that the data files are in a directory + below where 'sympow' is installed. If Sage is installed globally, + then + it will be impossible to create the data files without being root. + This has been fixed in the Gentoo Linux distribution. Some + information + from Christopher can be seen on + http://trac.sagemath.org/sage_trac/ticket/9703 + This package will generate binary versions of all shipped datafiles, + so these will work. However, creating totally new datafiles from + scratch + will not work. diff --git a/build/pkgs/sympow/SPKG.txt b/build/pkgs/sympow/SPKG.txt deleted file mode 100644 index 89dcf729da9..00000000000 --- a/build/pkgs/sympow/SPKG.txt +++ /dev/null @@ -1,98 +0,0 @@ -= sympow = - -== Description == -SYMPOW is a package to compute special values of symmetric power elliptic -curve L-functions. It can compute up to about 64 digits of precision. - -== License == - - * See the file src/COPYING - -== Upstream Contact == - SYMPOW does not appear to be maintained any longer, so there is no - upstream web site. - Mark Watkins, the package author, now works at Magma. - Previous (possibly still usable) email is watkins@maths.usyd.edu.au - -== Dependencies == - * GNU patch - -== Special Update/Build Instructions == - * Some of the code is very dubious, and it is anyones guess really what - the compiler does with it. For example, the following line exists in - src/eulerfactors.c: - - if ((HECKE) && (d==1)) return hecke_good(p,ap,m,v); - - But since hecke_good is defined as returning void, it's hard to know - exactly how this code behaves. I would not be surprised by any bugs - that might show up. I (David Kirkby) would personally not trust this - code much at all. - * This is a difficult package to maintain. A trac ticket (#9758) has been - opened to implement Watkins-Delaunay's algorithm for computing modular - degrees in Sage. Once implemented, it should be possible to remove this - package. - * The package is configured such that the data files are in a directory - below where 'sympow' is installed. If Sage is installed globally, then - it will be impossible to create the data files without being root. - This has been fixed in the Gentoo Linux distribution. Some information - from Christopher can be see on http://trac.sagemath.org/sage_trac/ticket/9703 - This package will generate binary versions of all shipped datafiles, - so these will work. However, creating totally new datafiles from scratch - will not work. - -== Changelog == - -=== sympow-1.018.1.p11 (Jeroen Demeyer, 19 Jan 2012) === - * #11920: Remove -fno-expensive-optimizations workaround, instead try - various flags which might force 53-bit precision doubles. - * Find out the actual FPU precision with config/fpubits1.c and - config/fpubits2.c. - * Move all x86 extended precision FPU-control word stuff from - src/Configure to spkg-install - * Generate binary datafiles when installing SYMPOW. This ensures that - all users (not only the one which installed Sage) can use the standard - datafiles. - * execlp.patch: Use execlp() instead of execl() to execute "sh". This - is needed for the -new_data option to work (which surely could never - have worked before). - * Use `patch` instead of `cp` for patching. - * Lots of small fixes in spkg-install. - * Remove dist/debian directory. - -=== sympow-1.018.1.p9 (Jeroen Demeyer, 2 May 2011) === - * #11226: Add flag -fno-expensive-optimizations when compiling with - gcc 4.6.x on a ia64 system. See also gcc bugzilla: - http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48823 - -=== sympow-1.018.1.p8 (David Kirkby, 21st August 2010) === - * #9703 + #9166 Implement inline assembly code to set the control - word of the floating point processor to round to an - IEEE-754 double (53-bit mantissa, 64-bits in total), rather - than the default extended precision. This resolves problems with - doctest failures on Solaris 10 x86, OpenSolaris on x86 and Cygwin. - This is in the file patches/fpu.c - * Tidied up SPKG.txt, to conform to the Sage Developers Guide. - * Move part of the contents of SPKG.txt to a file called 'email-exchange.txt' - which shows some email exchanges between William Stein and Mark - Watkins. It was previously here in SPKG.txt, but is rather out of place. - * Changed the very badly written Configure script to work with any compiler - (not gcc as before). Actually, since the C code is so badly written, - the Sun compiler will not compile it, but at least the errors can be seen - if one tries. - * Changed the Configure script so the code to change the precision control - of the floating point processor is implemented on any non-OS X system - with an x86 CPU. It now no longer assumes Linux. - * Removed code from spkg-install which tries to first build SYMPOW with - assembly code, then without it. The code must be old and redundant, as - the varibles set are not anywhere in the SYMPOW source code. - -=== sympow-1.018.1.p7 (David Kirkby, 25th May 2010) === - * #9029 Allow to build 64-bit - -=== sympow-1.018.1.p6 (Michael Abshoff, November 30th, 2008) === - * add build fix for tcsh by Willem Jan Palenstijn (trac #4261) - * small cleanups - -=== sympow-1.018.1.p5 === - * make sure we pick gcc over cc diff --git a/build/pkgs/sympow/checksums.ini b/build/pkgs/sympow/checksums.ini index 39e54344677..f6ad79b8f93 100644 --- a/build/pkgs/sympow/checksums.ini +++ b/build/pkgs/sympow/checksums.ini @@ -1,4 +1,5 @@ -tarball=sympow-VERSION.tar.bz2 -sha1=0058f22576b20413dc20fd8aa812f6edeb340115 -md5=b6cbb7488870d70d92d11176049cb91b -cksum=2605478641 +tarball=sympow-vVERSION.tar.gz +sha1=37a909c26009415197b5088a2f1b53dd3558f494 +md5=51f2c717c84ec9c2840af740751cf797 +cksum=1444149964 +upstream_url=https://github.com/mkoeppe/sympow/releases/download/v2.023.6/sympow-v2.023.6.tar.gz diff --git a/build/pkgs/sympow/config/fpubits1.c b/build/pkgs/sympow/config/fpubits1.c deleted file mode 100644 index d4edb4aee88..00000000000 --- a/build/pkgs/sympow/config/fpubits1.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Jeroen Demeyer 2011 - * - * Find out the precision of "double" floating-point numbers. - * Return 0 if the precision is exactly 53, - * return 1 if the precision is different from 53. - */ - -#include - -double mul_and_add(double, double, double); /* config/fpubits2.c */ -void fpu_53bits(); /* src/fpu.c */ - -int main(int argc, char** argv) -{ - /* If x86 is defined, set the FPU to 53 bits */ -#ifdef x86 - fpu_53bits(); -#endif - - /* Let a = 1 + x, - * b = 1 + y, - * and compute - * a*b - 1 = (1 + x + y + x*y) - 1 - * = x + y + x*y - * The last equality will hold computationally if (1 + x + y + x*y) - * can be represented exactly as floating-point number. - * Assuming x and y are negative powers of 2, this will work as long - * as x*y >= (1/2)^(p-1) where p is the precision of the FPU in bits. - */ - - int n = 0; - double x = 1; - double y = 1; - for (;;) - { - /* Make sure x*y = (1/2)^n - * and that x and y are roughly equal. */ - n++; - if (x >= y) - x /= 2; - else - y /= 2; - - double r = mul_and_add(1+x, 1+y, -1); - if (r != x + y + x*y) break; - - if (n >= 600) - { - printf("The double precision of your FPU seems to be more than %i bits, bailing out.\n", n); - return 1; - } - } - - printf("The double precision of your FPU is %i bits.\n", n); - if (n == 53) return 0; - return 1; -} diff --git a/build/pkgs/sympow/config/fpubits2.c b/build/pkgs/sympow/config/fpubits2.c deleted file mode 100644 index 02ef54b21a2..00000000000 --- a/build/pkgs/sympow/config/fpubits2.c +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Jeroen Demeyer 2011 - * - * Compute a*b + c - * If the processor has a fused multiply-and-add instruction - * (as on ia64), such an instruction will normally be used here. Since - * a fused multiply-and-add only rounds after the addition, it will - * cause a larger apparent precision. - * - * We put this function in a separate file to make sure the compiler - * does not optimize away the call to this function. - */ -double mul_and_add(double a, double b, double c) -{ - return a*b + c; -} diff --git a/build/pkgs/sympow/dependencies b/build/pkgs/sympow/dependencies index 3546cda4614..e78bf13b4a2 100644 --- a/build/pkgs/sympow/dependencies +++ b/build/pkgs/sympow/dependencies @@ -1,4 +1,4 @@ -# no dependencies +| pari ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sympow/distros/conda.txt b/build/pkgs/sympow/distros/conda.txt new file mode 100644 index 00000000000..a2ae7a8a59c --- /dev/null +++ b/build/pkgs/sympow/distros/conda.txt @@ -0,0 +1 @@ +sympow diff --git a/build/pkgs/sympow/distros/debian.txt b/build/pkgs/sympow/distros/debian.txt new file mode 100644 index 00000000000..a2ae7a8a59c --- /dev/null +++ b/build/pkgs/sympow/distros/debian.txt @@ -0,0 +1 @@ +sympow diff --git a/build/pkgs/sympow/distros/fedora.txt b/build/pkgs/sympow/distros/fedora.txt new file mode 100644 index 00000000000..a2ae7a8a59c --- /dev/null +++ b/build/pkgs/sympow/distros/fedora.txt @@ -0,0 +1 @@ +sympow diff --git a/build/pkgs/sympow/distros/gentoo.txt b/build/pkgs/sympow/distros/gentoo.txt new file mode 100644 index 00000000000..082eebb6daa --- /dev/null +++ b/build/pkgs/sympow/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/sympow diff --git a/build/pkgs/sympow/package-version.txt b/build/pkgs/sympow/package-version.txt index 804af29ef26..4331be82a98 100644 --- a/build/pkgs/sympow/package-version.txt +++ b/build/pkgs/sympow/package-version.txt @@ -1 +1 @@ -1.018.1.p13 +2.023.6 diff --git a/build/pkgs/sympow/patches/0001-Configure-Use-the-discovered-CFLAGS-for-the-next-tes.patch b/build/pkgs/sympow/patches/0001-Configure-Use-the-discovered-CFLAGS-for-the-next-tes.patch new file mode 100644 index 00000000000..c64f00dee4a --- /dev/null +++ b/build/pkgs/sympow/patches/0001-Configure-Use-the-discovered-CFLAGS-for-the-next-tes.patch @@ -0,0 +1,25 @@ +From 3dc4312874c48029d9481896bbc4b42acc43d9ad Mon Sep 17 00:00:00 2001 +From: Matthias Koeppe +Date: Wed, 8 Jul 2020 11:51:23 -0700 +Subject: [PATCH] Configure: Use the discovered CFLAGS for the next test + +--- + Configure | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Configure b/Configure +index 1ef9756..a9c8528 100755 +--- a/Configure ++++ b/Configure +@@ -207,7 +207,7 @@ fi + ####################################################################### + # Determine architecture endianess-tuple + ####################################################################### +-CC_ARGS="config/endiantuple.c -o config/endiantuple" ++CC_ARGS="$ORIGINALCFLAGS -O3 $CFLAGS config/endiantuple.c -o config/endiantuple" + $CC $CC_ARGS + if [ $? -ne 0 ]; then + echo >&2 "Error: the command below failed:" +-- +2.26.2 + diff --git a/build/pkgs/sympow/patches/0002-Remove-help2man.patch b/build/pkgs/sympow/patches/0002-Remove-help2man.patch new file mode 100644 index 00000000000..e77106587ba --- /dev/null +++ b/build/pkgs/sympow/patches/0002-Remove-help2man.patch @@ -0,0 +1,39 @@ +From f8d1e73d58ee135957dfd165a43f3baba54626cb Mon Sep 17 00:00:00 2001 +From: Timo Kaufmann +Date: Mon, 27 Aug 2018 18:27:32 +0200 +Subject: [PATCH 2/2] Remove help2man + +Requires autotools. Wel'll have to live without the manpages. +--- + Configure | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/Configure b/Configure +index d6dddad..c6d0b5c 100755 +--- a/Configure ++++ b/Configure +@@ -65,12 +65,6 @@ export CC + ##echo "**ERROR**: Could not find uname"; exit; + ##fi + +-HELP2MAN=`which \help2man` && echo "HELP2MAN = $HELP2MAN" >> $FILE +-if [ -z "$HELP2MAN" ]; +-then +-echo "**ERROR**: Could not find help2man"; exit; +-fi +- + ## Sage material (spkg-install) + ####################################################################### + # Fix FPU precision +@@ -304,7 +298,7 @@ echo " \$(TOUCH) datafiles/param_data" >> $FILE + echo " \$(SH) armd.sh" >> $FILE + echo " \$(SED) -i -e '/logfile =/d' datafiles/*.txt" >> $FILE + echo "sympow.1: sympow" >> $FILE +-echo " \$(HELP2MAN) \$(H2MFLAGS) -s 1 -n \"SYMPOW program\" -I sympow.h2m -o \$@ ./\$<" >> $FILE ++echo " touch sympow.1" >> $FILE + echo "clean:" >> $FILE + ##echo " \$(RM) -f \$(OBJSf) sympow \$(TILDES) \$(TARS)" >> $FILE + echo " \$(RM) -f \$(OBJS) sympow sympow.1 \$(TILDES)" >> $FILE +-- +2.16.4 + diff --git a/build/pkgs/sympow/patches/Configure.patch b/build/pkgs/sympow/patches/Configure.patch deleted file mode 100644 index 594b91ba882..00000000000 --- a/build/pkgs/sympow/patches/Configure.patch +++ /dev/null @@ -1,80 +0,0 @@ -diff -ur src/Configure src.new/Configure ---- src/Configure 2007-08-21 06:57:01.000000000 +0200 -+++ src.new/Configure 2011-10-19 22:20:50.000000000 +0200 -@@ -1,23 +1,26 @@ - #! /bin/sh -+# Despite making some small changes to this rather strange shell script -+# I did not write 99% of it. David Kirkby, 21st August 2010. - - if [ "$1" != "" ]; then - echo "**ERROR**: Configure does not take any options for SYMPOW"; exit 1; - fi - - whichexe() { -- if [ -f /bin/$1 ]; then -- echo /bin/$1 -- return; -- fi; -- if [ -f /usr/bin/$1 ]; then -- echo /usr/bin/$1 -- return; -- fi; -- if [ -f /usr/local/bin/$1 ]; then -- echo /usr/local/bin/$1 -- return; -- fi; -- echo `which $1` -+# if [ -f /bin/$1 ]; then -+# echo /bin/$1 -+# return; -+# fi; -+# if [ -f /usr/bin/$1 ]; then -+# echo /usr/bin/$1 -+# return; -+# fi; -+# if [ -f /usr/local/bin/$1 ]; then -+# echo /usr/local/bin/$1 -+# return; -+# fi; -+# echo `which $1` -+ echo $1 - } - - FILE="Makefile.new" -@@ -70,13 +73,6 @@ - echo "SH = $SH" - fi - --CC=`whichexe cc` && echo "CC = $CC" >> $FILE --if [ -z "$CC" ]; then -- echo "**ERROR**: Could not find cc"; exit 1; --else -- echo "CC = $CC" --fi -- - UNAME=`whichexe uname` - if [ -z "$UNAME" ];then - echo "**ERROR**: Could not find uname"; exit 1; -@@ -84,18 +80,11 @@ - echo "UNAME = $UNAME" - fi - --MACH=`"$UNAME" -m` --for x in ix86 i386 i486 i586 i686 x86_64 ia64 --do -- if [ "$MACH" = "$x" -a `uname` = "Linux" ]; then -- echo "You appear to have a $x based Linux system --- using fpu.c" -- DEFS="-Dx86" -- fi --done --if [ -z "$DEFS" ]; then -- echo "You do not appear to have an x86 based system --- not using fpu.c" --fi -+echo "CC=$CC" >> $FILE - -+# All x86 extended precision FPU-control word stuff: moved to spkg-install -+# -- Jeroen Demeyer -+DEFS="" - echo "DEFS = $DEFS" >> $FILE - - OPT="-O3" && echo "OPT = $OPT" >> $FILE diff --git a/build/pkgs/sympow/patches/execlp.patch b/build/pkgs/sympow/patches/execlp.patch deleted file mode 100644 index 7b0fa32849b..00000000000 --- a/build/pkgs/sympow/patches/execlp.patch +++ /dev/null @@ -1,9 +0,0 @@ -diff -ur src/generate.c src.new/generate.c ---- src/generate.c 2007-08-21 06:57:01.000000000 +0200 -+++ src.new/generate.c 2011-10-17 21:55:10.000000000 +0200 -@@ -201,4 +201,4 @@ - else if (c) sprintf(ARGS,"-cm -sp %i",sp); - else if (sp&1) sprintf(ARGS,"-sp %i -dv %i",sp,dv); - else sprintf(ARGS,"-sp %i",sp); -- execl(SH,SH,PATH,SH,GP,ARGS,NULL);} -+ execlp(SH,SH,PATH,SH,GP,ARGS,NULL);} diff --git a/build/pkgs/sympow/patches/fpu.patch b/build/pkgs/sympow/patches/fpu.patch deleted file mode 100644 index 9f7f25c434e..00000000000 --- a/build/pkgs/sympow/patches/fpu.patch +++ /dev/null @@ -1,54 +0,0 @@ -diff -u -r src/fpu.c src.new/fpu.c ---- src/fpu.c 2007-08-21 06:57:01.000000000 +0200 -+++ src.new/fpu.c 2011-10-19 22:50:37.000000000 +0200 -@@ -1,8 +1,46 @@ -+/* David Kirkby 21st August 2010 -+Licenced under the GPL version 2 or at your option any later version. -+ -+Set the FPU's precision control to 53 bits (double precision) instead of -+the default 64-bits (extended precision). I've commented it fairly -+liberally, with the hope it's helpful if anyone needs to edit it. -+ -+Note, the extended precision uses 80 bits in total, -+of which 64 are for the mantissa. -+ -+Double precsion uses 64 bits in total, but ony 53 are -+for the mantissa. -+ -+The precision is set by bits 8 and 9 of the Control Word in the floating -+point processor. The Control word has 16 bits. -+ -+Data taken from The 80387 Programmer's reference Manual, Intel, -+1987. -+ -+00 = 24-bits (single precision) -+01 = reserved (or at least it was at the time the 387 was released) -+10 = 53-bits (double precision) -+11 = 64-bits (extended precision). -+ -+FLDCW is an x86 instruction to "Load the Control Word" -+FNSTCW is an x88 instruction to "Store FPU Control Word" -+It does so without checking for pending unmasked floating-point -+exceptions. (A similar FSTCW checks for them first). -+*/ -+ -+#ifdef x86 -+ -+#define _SET_FPU_CONTROL_WORD(x) asm volatile ("fldcw %0": :"m" (x)); -+#define _READ_FPU_CONTROL_WORD(x) asm volatile ("fnstcw %0":"=m" (x)); - - void fpu_53bits() - { --#ifdef x86 --#include --fpu_control_t fpu_control=0x027f; _FPU_SETCW(fpu_control); --#endif -+ /* The control word is 16 bits, numbered 0 to 15 */ -+ volatile unsigned short control_word; -+ -+ _READ_FPU_CONTROL_WORD(control_word); /* Read the FPU control word */ -+ control_word=control_word & 0xfeff; /* Set bit 8 = 0 */ -+ control_word=control_word | 0x200; /* Set bit 9 = 1 */ -+ _SET_FPU_CONTROL_WORD(control_word); /* Force double-precision, 53-bit mantissa */ - } -+#endif diff --git a/build/pkgs/sympow/patches/initialize-tacks.patch b/build/pkgs/sympow/patches/initialize-tacks.patch deleted file mode 100644 index cfa11dfb28b..00000000000 --- a/build/pkgs/sympow/patches/initialize-tacks.patch +++ /dev/null @@ -1,38 +0,0 @@ -diff -u sympow-1.018.1/analrank.c sympow/analrank.c ---- src/analrank.c 2007-08-21 06:57:01.000000000 +0200 -+++ analrank.c 2017-04-24 10:13:45.757479996 +0200 -@@ -18,7 +18,8 @@ - S[20]+=2; S[31]+=4; S[42]+=6; S[53]+=8; - NT=process_string(S,UB); go(NT,NT); return;} - else {AP_SAVE=1; apsave=malloc(primes_upper_bound(NT)*sizeof(int));} -- if (NT0.0001) - {if (w1==1) printf("Analytic Rank is 0 : L-value %.5e\n",f); - else printf("Analytic Rank is 1 : L'-value %.5e\n",f); return;} -diff -u sympow-1.018.1/prepare.c sympow/prepare.c ---- src/prepare.c 2007-08-21 06:57:01.000000000 +0200 -+++ prepare.c 2017-04-24 10:50:10.941042660 +0200 -@@ -147,7 +147,7 @@ - WIGGLE=malloc(K*sizeof(QD)); WIGSQI=malloc(K*sizeof(QD)); - EXPAND0_LIM=malloc(K*sizeof(QD)); STEP_SIZE=malloc(K*sizeof(QD)); - TOO_BIG=malloc(K*sizeof(QD)); NUM_LOGS=malloc(K*sizeof(QD)); -- TACKON=malloc(K*sizeof(int)); TACKS=malloc(K*sizeof(QD*)); -+ TACKON=malloc(K*sizeof(int)); TACKS=calloc(K,sizeof(QD*)); - w=malloc(K*sizeof(int)); wprec=malloc(K*sizeof(int)); - DECAY=malloc(K*sizeof(QD)); NUM_WIGS=malloc(K*sizeof(QD)); - for (i=0;i=2) printf("Done with get_primes_ll\n"); free(auxp);} - - void free_data() --{QD **P; int i; if (DEBUG) printf("free_data\n"); free(TACKS[0]); -+{QD **P; int i; if (DEBUG) printf("free_data\n"); -+ if(TACKS[0]!=NULL) free(TACKS[0]); - P=TABLE[0]; for (i=0;i +Date: Fri, 15 May 2020 16:49:49 -0400 +Subject: [PATCH 1/1] main.c: hide pkgdatafilesbindir warnings behind VERBOSE + >= 2. + +The default "pkgdatafilesbindir" is something like /var/cache/sympow +that will never be writable by unprivileged users (and cannot safely +be made that way). There is already a fallback to $HOME in the code +that works perfectly well, but by default sympow emits a warning when +it realizes that it can't write to e.g. /var/cache/sympow on the first +try. Since that's completely expected, we hide the warnings behind an +additional level of verbosity (VERBOSE >= 2 instead of VERBOSE >= 1). +--- + main.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/main.c b/main.c +index 1d018df..fecd7d1 100644 +--- a/main.c ++++ b/main.c +@@ -136,17 +136,17 @@ static void prepare_main(char *argv0) + asprintf(&pkgdatafilesbindir,"%s/datafiles/"ENDIANTUPLE,pkgcachedir); + if (stat(pkgdatafilesbindir,&infodb)) {mode_t mask=umask(0); + if (mkdir(pkgdatafilesbindir,(S_IRWXU|S_IRWXG|S_IRWXO|S_ISVTX))) +- {if (VERBOSE>=1) fprintf(stderr,"**WARNING** failed to create data bin package cache folder %s\n",pkgdatafilesbindir); ++ {if (VERBOSE>=2) fprintf(stderr,"**WARNING** failed to create data bin package cache folder %s\n",pkgdatafilesbindir); + free(pkgdatafilesbindir); pkgdatafilesbindir=NULL;} + else + {stat(pkgdatafilesbindir,&infodb); pkgdatamode= infodb.st_mode & ~MASK;} + umask(mask);} + else + {if (!S_ISDIR(infodb.st_mode)) +- {if (VERBOSE>=1) fprintf(stderr,"**WARNING** %s exists but is not a directory\n",pkgdatafilesbindir); ++ {if (VERBOSE>=2) fprintf(stderr,"**WARNING** %s exists but is not a directory\n",pkgdatafilesbindir); + free(pkgdatafilesbindir); pkgdatafilesbindir=NULL;} + else if (access(pkgdatafilesbindir,(R_OK|W_OK|X_OK))) +- {if (VERBOSE>=1) fprintf(stderr,"**WARNING** %s yields insufficient permissions\n",pkgdatafilesbindir); ++ {if (VERBOSE>=2) fprintf(stderr,"**WARNING** %s yields insufficient permissions\n",pkgdatafilesbindir); + free(pkgdatafilesbindir); pkgdatafilesbindir=NULL;} + else {pkgdatamode= infodb.st_mode & ~MASK;}} + asprintf(&datafilesdir,"%s/datafiles",cachedir); +-- +2.26.2 + diff --git a/build/pkgs/sympow/spkg-configure.m4 b/build/pkgs/sympow/spkg-configure.m4 new file mode 100644 index 00000000000..9404dc1a475 --- /dev/null +++ b/build/pkgs/sympow/spkg-configure.m4 @@ -0,0 +1,14 @@ +SAGE_SPKG_CONFIGURE([sympow], [ + AC_PATH_PROG([SYMPOW], [sympow]) + AS_IF([test -z "$ac_cv_path_SYMPOW"], [sage_spkg_install_sympow=yes + ], [ + AC_MSG_CHECKING([whether sympow works well (cf. :trac:30147)]) + sympow_rank_test=`echo "@<:@1,-1,0,-79,289@:>@" | sympow -analrank | grep ^"Analytic Rank is 4"` + AS_IF([test x"$sympow_rank_test" = x], [ + AC_MSG_RESULT([no; cannot use system sympow]) + sage_spkg_install_sympow=yes + ], [ + AC_MSG_RESULT([yes; use system sympow]) + ]) + ]) +]) diff --git a/build/pkgs/sympow/spkg-install b/build/pkgs/sympow/spkg-install deleted file mode 100644 index 8fe518f2d04..00000000000 --- a/build/pkgs/sympow/spkg-install +++ /dev/null @@ -1,139 +0,0 @@ -####################################################################### -# Fix FPU precision -####################################################################### -# SYMPOW really needs doubles to have *exactly* 53 bits precision, -# they need to be true IEEE-754 double precision numbers. -# In particular, we need to avoid 80 bits extended precision on i386 -# and we need to avoid fused multiply-add instructions (e.g. on ia64). -# We might need to add various flags to CFLAGS to ensure this. -# See Trac tickets #9703, #9734, #9166, #11226, #11920. - -# Usage: try_add_CFLAG $FLAG -# Try adding $FLAG to $CFLAGS, compile and run the fpubits program. -# If it compiles and running it doesn't crash it (with an Illegal -# Instruction for example), then we add $FLAG to $CFLAGS. -# Return 0 if we added $FLAG and the FPU correctly used 53 bits, -# return 1 if we added $FLAG but the FPU doesn't use 53 bits, -# return 2 if we did not add $FLAG. -try_add_CFLAG() -{ - # We use -O3 here to really force generation of fused - # multiply-add instructions and to keep floats as much as - # possible in registers. - # We compile in the (already patched!) src/fpu.c which only does - # something if the macro x86 is defined. - if $CC -O3 $CFLAGS $FLAG config/fpubits1.c config/fpubits2.c src/fpu.c -o config/fpubits 2>/dev/null; then - # Compiled successfully, now run it - config/fpubits >/dev/null 2>/dev/null - status=$? - if [ $status -le 1 ]; then - # The program ran successfully. For now, we don't need - # the exit status to be zero (indicating exactly 53 bits), - # we simply need the program not to crash (which would - # give an exit status > 128). - CFLAGS="$CFLAGS $FLAG" - return $status - fi - fi - return 2 -} - -# These flags never hurt, so add them if possible -for FLAG in '-fno-fast-math' '-mfpmath=sse' '-Dx86'; do - try_add_CFLAG $FLAG -done - -# Add at most one flag of the following to avoid gcc warnings: -# gcc versions which support -ffp-contract deprecate -mno-fused-madd -for FLAG in '-ffp-contract=on' '-mno-fused-madd'; do - try_add_CFLAG $FLAG && break -done - -# Some flags to try as last resort. These hurt performance, so only add -# them if needed. -for FLAG in '' '-ffloat-store' '-O0'; do - # Stop the loop if the FPU precision already is 53 bits - try_add_CFLAG $FLAG && break -done - - -# Check the actual FPU precision with our new flags. -$CC -O3 $CFLAGS config/fpubits1.c config/fpubits2.c src/fpu.c -o config/fpubits -if [ $? -ne 0 ]; then - echo >&2 "Error: the command below failed:" - echo >&2 "$CC -O3 $CFLAGS config/fpubits1.c config/fpubits2.c src/fpu.c -o config/fpubits" - exit 1 -fi -export CFLAGS - -echo "CFLAGS for SYMPOW:$CFLAGS" - -config/fpubits -status=$? -[ $status -eq 1 ] && sdh_die <<_EOF_ -Error: the Quad Double library used by SYMPOW assumes IEEE-754 double -precision numbers with exactly 53 bits in the mantissa (64 bits in -total). Unfortunately, this is not the case on your system and we -currently have no workaround for your system. Running SYMPOW will -almost certainly fail on some inputs. - -Please report this problem to sage-devel -(http://groups.google.com/group/sage-devel), mentioning in particular -your operating system, processor type and compiler version -(run $CC --version). -_EOF_ - -[ $status -ne 0 ] && sdh_die <<_EOF_ -Error: something very bad happened while checking the precision of your -FPU. Please report this problem (mentioning any error messages above) -to sage-devel (http://groups.google.com/group/sage-devel). -Mention in particular your operating system and compiler version -(run $CC --version). -_EOF_ - - -####################################################################### -# Configure -####################################################################### -cd src/ -# Force bash as shell -bash ./Configure || sdh_die "Error configuring SYMPOW" - -####################################################################### -# Build -####################################################################### -sdh_make - -####################################################################### -# Install -####################################################################### -TARGET="$SAGE_LOCAL/lib/sympow" - -# Remove old install if any -rm -rf "$TARGET" - -sdh_install sympow *.gp new_data datafiles "$TARGET" - -# Create binary versions of datafiles -# Note: This should probably be a post-install step since it requires calling -# the compiled sympow binary; however in this case it happens to work fine -# since it does not have any external dependencies and can run from the temp -# install dir -cd "${SAGE_DESTDIR}${TARGET}/datafiles" -for file in *.txt; do - NUM=`grep -c AT $file` - ../sympow -txt2bin "$NUM" <$file ${file/%txt/bin} || \ - sdh_die "Error running SYMPOW" -done - -# Create sympow script in $SAGE_LOCAL/bin -BIN="$SAGE_DESTDIR_LOCAL/bin" -mkdir -p "$BIN" -SCRIPT="$BIN/sympow" - -cat >"$SCRIPT" <<'EOF' -cd "$SAGE_LOCAL/lib/sympow" -exec ./sympow "$@" -EOF - -chmod +x "$SCRIPT" diff --git a/build/pkgs/sympow/spkg-install.in b/build/pkgs/sympow/spkg-install.in new file mode 100644 index 00000000000..c72de8742bf --- /dev/null +++ b/build/pkgs/sympow/spkg-install.in @@ -0,0 +1,10 @@ +cd src/ + +# Force bash as shell +export VARPREFIX="$SAGE_LOCAL/var" +export PREFIX="$SAGE_LOCAL" +bash ./Configure || sdh_die "Error configuring SYMPOW" + +sdh_make + +sdh_make_install diff --git a/build/pkgs/sympy/SPKG.rst b/build/pkgs/sympy/SPKG.rst new file mode 100644 index 00000000000..ced31dd1e05 --- /dev/null +++ b/build/pkgs/sympy/SPKG.rst @@ -0,0 +1,39 @@ +SymPy +===== + +Description +----------- + +SymPy is a Python library for symbolic mathematics. It aims to become a +full-featured computer algebra system (CAS) while keeping the code as +simple as possible in order to be comprehensible and easily extensible. +SymPy is written entirely in Python and does not require any external +libraries, except optionally for plotting support. + +Website +------- + +http://sympy.org/ + +License +------- + +New BSD: http://www.opensource.org/licenses/bsd-license.php + + +Upstream Contact +---------------- + +sympy mailinglist: http://groups.google.com/group/sympy + +Dependencies +------------ + +- Python 2.5 or later + + +Special Update/Build Instructions +--------------------------------- + +- A simple script can be used to ease the updating of the SPKG. See the + README. diff --git a/build/pkgs/sympy/SPKG.txt b/build/pkgs/sympy/SPKG.txt deleted file mode 100644 index 3b384bb0c1d..00000000000 --- a/build/pkgs/sympy/SPKG.txt +++ /dev/null @@ -1,83 +0,0 @@ -= SymPy = - -== Description == - -SymPy is a Python library for symbolic mathematics. It aims to become a full-featured computer algebra system (CAS) while keeping the code as simple as possible in order to be comprehensible and easily extensible. SymPy is written entirely in Python and does not require any external libraries, except optionally for plotting support. - -== Website == - -http://sympy.org/ - -== License == - -New BSD: http://www.opensource.org/licenses/bsd-license.php - -== Upstream Contact == - -sympy mailinglist: http://groups.google.com/group/sympy - -== Dependencies == - * Python 2.5 or later - -== Special Update/Build Instructions == - - * A simple script can be used to ease the updating of the SPKG. See the README. - -== Changelog == - -=== sympy-0.7.5 (Sergey B Kirpichev, 29 September 2014) === - * new upstream release - * fix SPKG.txt - -=== sympy-0.7.3 (Eviatar Bach, 17 August 2013) === - * Trac #14694: new upstream release - * updating SymPy download URL - -=== sympy-0.7.1.p0 (Francois Bissey, 22 February 2012) === - * Trac #12563: add -S option to python to prevent sympy's installer - from importing sage. - * Clean up spkg-install. - -=== sympy-0.7.1 (Francois Bissey, Sep 16, 2011) === - * trac 11560: update to new upstream release - * clean old obsolete script. - * mention the update instruction of the README in this file - * update spkg-install to clean old sympy version in all python 2.x install not just python2.5, - when sage is currently using 2.6 - * delete old version of sympy only if building of new sympy is successful - -=== sympy-0.6.4 (Ondrej Certik, April 5th, 2008) === - * new upstream release - -=== sympy-0.6.3.p0 (Michael Abshoff, November 22nd, 2008) === - * Clean up SPKG.txt and spkg-install - -=== sympy-0.6.3 (Ondrej Certik, November 19th, 2008) === - * new upstream release - -=== sympy-0.6.2 (Ondrej Certik) === - * new upstream release - -=== sympy-0.6.0 (Ondrej Certik) === - * new upstream release - -=== sympy-0.5.13 (Ondrej Certik) === - * new upstream release - * added a README file - -=== sympy-0.5.11 (Ondrej Certik) === - * new upstream release - -=== sympy-0.5.7 (Ondrej Certik) === - * new upstream release - * added a script for getting a hg version - * added a script for downloading the upstream sources, creating the src dir - -=== sympy-0.5.6 (Ondrej Certik) === - * new upstream release - -=== sympy-0.5.5 (Ondrej Certik) === - * new upstream release - -=== sympy-0.5.3 (William Stein) === - * initial version of the packaging diff --git a/build/pkgs/sympy/checksums.ini b/build/pkgs/sympy/checksums.ini index 1b98ad8879c..e5b80927ab8 100644 --- a/build/pkgs/sympy/checksums.ini +++ b/build/pkgs/sympy/checksums.ini @@ -1,4 +1,5 @@ tarball=sympy-VERSION.tar.gz -sha1=be2e740860f7900f0ee2a8102d2943fded44125c -md5=fa9ad424535075312df022964ede21bc -cksum=3298250000 +sha1=067078df2d0401f3c4b49ee2e50a4105f92c5272 +md5=dbb7b21d2972c41f37d48f744b6189a3 +cksum=575244204 +upstream_url=https://github.com/sympy/sympy/releases/download/sympy-VERSION/sympy-VERSION.tar.gz diff --git a/build/pkgs/sympy/dependencies b/build/pkgs/sympy/dependencies index a0975d6f483..c12b8329ca4 100644 --- a/build/pkgs/sympy/dependencies +++ b/build/pkgs/sympy/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) mpmath | pip +$(PYTHON) mpmath | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sympy/package-version.txt b/build/pkgs/sympy/package-version.txt index c239c60cba2..810ee4e91e2 100644 --- a/build/pkgs/sympy/package-version.txt +++ b/build/pkgs/sympy/package-version.txt @@ -1 +1 @@ -1.5 +1.6 diff --git a/build/pkgs/sympy/spkg-install b/build/pkgs/sympy/spkg-install deleted file mode 100644 index f75b1ec7161..00000000000 --- a/build/pkgs/sympy/spkg-install +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src/ - -echo "installing sympy" -sdh_pip_install . - -if [ $? -ne 0 ]; then - echo >&2 "Error installing sympy" - exit 1 -fi diff --git a/build/pkgs/sympy/spkg-install.in b/build/pkgs/sympy/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/sympy/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/tachyon/SPKG.rst b/build/pkgs/tachyon/SPKG.rst new file mode 100644 index 00000000000..702a8d92a7d --- /dev/null +++ b/build/pkgs/tachyon/SPKG.rst @@ -0,0 +1,75 @@ +tachyon +======= + +Description +----------- + +Tachyon is a raytracer developed by John E. Stone. Tachyon supports the +typical ray tracer features, most of the common geometric primitives, +shading and texturing modes, etc. It also supports less common features +such as HDR image output, ambient occlusion lighting, and support for +various triangle mesh and volumetric texture formats beneficial for +molecular visualization (e.g. rendering VMD scenes). + +Currently not all of Tachyon's functionality is exported by the Sage +interface. + +License +------- + +Copyright (c) 1994-2010 John E. Stone +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote + products + derived from this software without specific prior written permission. + + +Upstream Contact +---------------- + +- http://jedi.ks.uiuc.edu/~johns/raytracer/ +- John Stone + +Dependencies +------------ + +This spkg depends on: + +- libpng + + +Special Update/Build Instructions +--------------------------------- + +- Delete the scenes directory, which has lots of cool examples. +- Delete the msvc directory, which is also large and not used within + Sage. +- The CVS subdirectories are currently (almost) empty, but should + otherwise be deleted. + +- The upstream files had strange permissions, i.e. some source files + were executable, while almost all files weren't world-readable. + +- There's seems to be some crap like ``tachyon.html.tar.gz`` and a few + ``.#*`` files I haven't [yet] deleted, since they're not that large. + +- TODO: Check whether building multi-threaded versions on MacOS X + meanwhile works. (This was said to fail with an old beta.) + +- TODO: Use ``patch`` instead of copying over pre-patched files. +- TODO: [Optionally] also install some of the documentation. +- TODO: I doubt the CFLAGS set for AIX and HP-UX won't get overridden + by the created Makefile, but that's a minor issue. -leif diff --git a/build/pkgs/tachyon/SPKG.txt b/build/pkgs/tachyon/SPKG.txt deleted file mode 100644 index 5c3deaba733..00000000000 --- a/build/pkgs/tachyon/SPKG.txt +++ /dev/null @@ -1,61 +0,0 @@ -= tachyon = - -== Description == - -Tachyon is a raytracer developed by John E. Stone. Tachyon supports -the typical ray tracer features, most of the common geometric -primitives, shading and texturing modes, etc. It also supports less -common features such as HDR image output, ambient occlusion lighting, -and support for various triangle mesh and volumetric texture formats -beneficial for molecular visualization (e.g. rendering VMD scenes). - -Currently not all of Tachyon's functionality is exported by the Sage -interface. - - -== License == - - * Copyright (c) 1994-2010 John E. Stone - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - - -== Upstream Contact == - -http://jedi.ks.uiuc.edu/~johns/raytracer/ -John Stone - - -== Dependencies == - -This spkg depends on: - - * libpng - - -== Special Update/Build Instructions == - - * Delete the scenes directory, which has lots of cool examples. - * Delete the msvc directory, which is also large and not used within Sage. - * The CVS subdirectories are currently (almost) empty, but should - otherwise be deleted. - * The upstream files had strange permissions, i.e. some source files - were executable, while almost all files weren't world-readable. - * There's seems to be some crap like `tachyon.html.tar.gz` and a few - `.#*` files I haven't [yet] deleted, since they're not that large. - * TODO: Check whether building multi-threaded versions on MacOS X - meanwhile works. (This was said to fail with an old beta.) - * TODO: Use `patch` instead of copying over pre-patched files. - * TODO: [Optionally] also install some of the documentation. - * TODO: I doubt the CFLAGS set for AIX and HP-UX won't get overridden - by the created Makefile, but that's a minor issue. -leif diff --git a/build/pkgs/tachyon/distros/fedora.txt b/build/pkgs/tachyon/distros/fedora.txt new file mode 100644 index 00000000000..b1ee1e1bdbe --- /dev/null +++ b/build/pkgs/tachyon/distros/fedora.txt @@ -0,0 +1 @@ +tachyon tachyon-devel diff --git a/build/pkgs/tachyon/distros/gentoo.txt b/build/pkgs/tachyon/distros/gentoo.txt new file mode 100644 index 00000000000..f8a67baa05b --- /dev/null +++ b/build/pkgs/tachyon/distros/gentoo.txt @@ -0,0 +1 @@ +media-gfx/tachyon diff --git a/build/pkgs/tachyon/spkg-install b/build/pkgs/tachyon/spkg-install.in similarity index 100% rename from build/pkgs/tachyon/spkg-install rename to build/pkgs/tachyon/spkg-install.in diff --git a/build/pkgs/tdlib/SPKG.rst b/build/pkgs/tdlib/SPKG.rst new file mode 100644 index 00000000000..84034a66e59 --- /dev/null +++ b/build/pkgs/tdlib/SPKG.rst @@ -0,0 +1,32 @@ +TdLib +===== + +Description +----------- + +Providing algorithms concerning treedecompositions + +website: http://www.tdi.informatik.uni-frankfurt.de/~lukas/tdlib.html + +License +------- + +GNU General Public License v2 + + +SPKG Maintainers +---------------- + +Lukas Larisch (larisch@informatik.uni-frankfurt.de) + + +Upstream Contact +---------------- + +- Lukas Larisch (larisch@informatik.uni-frankfurt.de) +- git-repo: git://pholia.tdi.cs.uni-frankfurt.de/git/tdlib + +Dependencies +------------ + +- None diff --git a/build/pkgs/tdlib/SPKG.txt b/build/pkgs/tdlib/SPKG.txt deleted file mode 100644 index a4567c1b9c4..00000000000 --- a/build/pkgs/tdlib/SPKG.txt +++ /dev/null @@ -1,20 +0,0 @@ -= TdLib = - -== Description == -Providing algorithms concerning treedecompositions - -website: http://www.tdi.informatik.uni-frankfurt.de/~lukas/tdlib.html - -== License == -GNU General Public License v2 - -== SPKG Maintainers == -Lukas Larisch (larisch@informatik.uni-frankfurt.de) - -== Upstream Contact == -Lukas Larisch (larisch@informatik.uni-frankfurt.de) -git-repo: git://pholia.tdi.cs.uni-frankfurt.de/git/tdlib - -== Dependencies == - * None - diff --git a/build/pkgs/tdlib/spkg-install b/build/pkgs/tdlib/spkg-install.in similarity index 100% rename from build/pkgs/tdlib/spkg-install rename to build/pkgs/tdlib/spkg-install.in diff --git a/build/pkgs/termcap/SPKG.rst b/build/pkgs/termcap/SPKG.rst new file mode 100644 index 00000000000..65e96b3469a --- /dev/null +++ b/build/pkgs/termcap/SPKG.rst @@ -0,0 +1,30 @@ +termcap +======= + +Description +----------- + +A library of C functions that enable programs to send control strings to +terminals in a way independent of the terminal type. + +License +------- + +GPL version 2 + + +Upstream Contact +---------------- + +Please report any bugs in this library to bug-gnu-emacs@prep.ai.mit.edu + +Dependencies +------------ + +- GNU patch + + +Special Update/Build Instructions +--------------------------------- + +None diff --git a/build/pkgs/termcap/SPKG.txt b/build/pkgs/termcap/SPKG.txt deleted file mode 100644 index e6007fe8581..00000000000 --- a/build/pkgs/termcap/SPKG.txt +++ /dev/null @@ -1,55 +0,0 @@ -= termcap = - -== Description == - -A library of C functions that enable programs to send control strings -to terminals in a way independent of the terminal type. - -== License == - -GPL version 2 - -== Upstream Contact == - -Please report any bugs in this library to bug-gnu-emacs@prep.ai.mit.edu - -== Dependencies == - - * GNU patch - -== Special Update/Build Instructions == - -None - -== Changelog == - -=== termcap-1.3.1.p3 (Jeroen Demeyer, 28 March 2012) === - * Trac #12725: Symlink libtermcap.a to libncurses.a if we cannot link - programs against -lncurses. - -=== termcap-1.3.1.p2 (Jeroen Demeyer, 11 January 2012) === - * Trac #12282: Add patches/strcmp_NULL.patch to fix a bug when the - environment variable TERM is not set. - * Restore upstream sources, put existing patch in - patches/Makefile.in.patch - * Use patch for patching - * Standardize SPKG.txt - -=== termcap-1.3.1.p1 (Jaap Spies, Jan 28th, 2010) === - * If $SAGE64="yes" add -m64 to CFLAGS. This used to work only on Darwin. - This works now on Open Solaris x64 64 bit and may work on other 64 bit systems. - * SPKG.txt needs more work!!!!! Not by me now. - * This is trac http://trac.sagemath.org/sage_trac/ticket/8097 - -=== termcap-1.3.1.p0 (Michael Abshoff, May 18th, 2008) === - * add 64 OSX build support - * check in all files - * add .hgignore - * Changes from upstream: -1) Deleted some lines from Makefile.in to prevent info docs being built - -2) In Makefile.in I commented out the line - - oldincludedir = /usr/include - -since SAGE install should work as not-root. diff --git a/build/pkgs/termcap/spkg-install b/build/pkgs/termcap/spkg-install.in similarity index 100% rename from build/pkgs/termcap/spkg-install rename to build/pkgs/termcap/spkg-install.in diff --git a/build/pkgs/terminado/SPKG.rst b/build/pkgs/terminado/SPKG.rst new file mode 100644 index 00000000000..38970eddb75 --- /dev/null +++ b/build/pkgs/terminado/SPKG.rst @@ -0,0 +1,12 @@ +terminado +========= + +Description +----------- + +This is a Tornado websocket backend for the term.js Javascript terminal +emulator library. + +It evolved out of pyxterm, which was part of GraphTerm (as lineterm.py), +v0.57.0 (2014-07-18), and ultimately derived from the public-domain +Ajaxterm code, v0.11 (2008-11-13) (also on Github as part of QWeb). diff --git a/build/pkgs/terminado/SPKG.txt b/build/pkgs/terminado/SPKG.txt deleted file mode 100644 index 8f5a4b8d6ff..00000000000 --- a/build/pkgs/terminado/SPKG.txt +++ /dev/null @@ -1,10 +0,0 @@ -= terminado = - -== Description == - -This is a Tornado websocket backend for the term.js Javascript terminal -emulator library. - -It evolved out of pyxterm, which was part of GraphTerm (as lineterm.py), -v0.57.0 (2014-07-18), and ultimately derived from the public-domain -Ajaxterm code, v0.11 (2008-11-13) (also on Github as part of QWeb). diff --git a/build/pkgs/terminado/dependencies b/build/pkgs/terminado/dependencies index 2904ced1d75..c61d11251e3 100644 --- a/build/pkgs/terminado/dependencies +++ b/build/pkgs/terminado/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip ptyprocess tornado +$(PYTHON) | $(PYTHON_TOOLCHAIN) ptyprocess tornado ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/terminado/spkg-install.in b/build/pkgs/terminado/spkg-install.in new file mode 100644 index 00000000000..cb4ba894442 --- /dev/null +++ b/build/pkgs/terminado/spkg-install.in @@ -0,0 +1,8 @@ +cd src + +# Make sure that modern pip uses the generated setup.py +# that is distributed with the PyPI tarball, +# so we do not have to have flit. Trac #29803. +rm -f pyproject.toml + +sdh_pip_install . diff --git a/build/pkgs/testpath/SPKG.rst b/build/pkgs/testpath/SPKG.rst new file mode 100644 index 00000000000..3a0a44b070d --- /dev/null +++ b/build/pkgs/testpath/SPKG.rst @@ -0,0 +1,8 @@ +testpath +======== + +Description +----------- + +Testpath is a collection of utilities for testing code which uses and +manipulates the filesystem and system commands diff --git a/build/pkgs/testpath/SPKG.txt b/build/pkgs/testpath/SPKG.txt deleted file mode 100644 index 2ea91ce9d57..00000000000 --- a/build/pkgs/testpath/SPKG.txt +++ /dev/null @@ -1,8 +0,0 @@ -= testpath = - -== Description == - -Testpath - -Testpath is a collection of utilities for testing code which uses and -manipulates the filesystem and system commands diff --git a/build/pkgs/testpath/dependencies b/build/pkgs/testpath/dependencies index 5dbcef67c94..15df0c4d6d8 100644 --- a/build/pkgs/testpath/dependencies +++ b/build/pkgs/testpath/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/testpath/spkg-install.in b/build/pkgs/testpath/spkg-install.in new file mode 100644 index 00000000000..cb4ba894442 --- /dev/null +++ b/build/pkgs/testpath/spkg-install.in @@ -0,0 +1,8 @@ +cd src + +# Make sure that modern pip uses the generated setup.py +# that is distributed with the PyPI tarball, +# so we do not have to have flit. Trac #29803. +rm -f pyproject.toml + +sdh_pip_install . diff --git a/build/pkgs/texlive/SPKG.rst b/build/pkgs/texlive/SPKG.rst new file mode 100644 index 00000000000..a81008a27cc --- /dev/null +++ b/build/pkgs/texlive/SPKG.rst @@ -0,0 +1,39 @@ +TeXlive +======= + +Description +----------- + +TeX Live is an easy way to get up and running with the TeX document +production system. It provides a comprehensive TeX system with binaries +for most flavors of Unix, including GNU/Linux, and also Windows. It +includes all the major TeX-related programs, macro packages, and fonts +that are free software, including support for many languages around the +world. + +This package installs all texlive packages required to build Sage. If +necessary, texlive itself is installed. + +License +------- + +Various FSF-approved free software licenses. See +https://www.tug.org/texlive/copying.html for details. + + +Upstream Contact +---------------- + +Home page: https://www.tug.org/texlive + +Dependencies +------------ + +- python + + +Special Update/Build Instructions +--------------------------------- + +This package requires internet access to download texlive packages for +the TeX mirrors. diff --git a/build/pkgs/texlive/SPKG.txt b/build/pkgs/texlive/SPKG.txt deleted file mode 100644 index 36e2c9825bd..00000000000 --- a/build/pkgs/texlive/SPKG.txt +++ /dev/null @@ -1,31 +0,0 @@ -= TeXlive = - -== Description == - -TeX Live is an easy way to get up and running with the TeX document -production system. It provides a comprehensive TeX system with -binaries for most flavors of Unix, including GNU/Linux, and also -Windows. It includes all the major TeX-related programs, macro -packages, and fonts that are free software, including support for many -languages around the world. - -This package installs all texlive packages required to build Sage. If -necessary, texlive itself is installed. - -== License == - -Various FSF-approved free software licenses. See -https://www.tug.org/texlive/copying.html for details. - -== Upstream Contact == - -Home page: https://www.tug.org/texlive - -== Dependencies == - -* python - -== Special Update/Build Instructions == - -This package requires internet access to download texlive packages for -the TeX mirrors. diff --git a/build/pkgs/texlive/type b/build/pkgs/texlive/type index 84f7e31d99b..134d9bc32d5 100644 --- a/build/pkgs/texlive/type +++ b/build/pkgs/texlive/type @@ -1 +1 @@ -script +optional diff --git a/build/pkgs/thebe/SPKG.rst b/build/pkgs/thebe/SPKG.rst new file mode 100644 index 00000000000..39a07327e9f --- /dev/null +++ b/build/pkgs/thebe/SPKG.rst @@ -0,0 +1,37 @@ +thebe +===== + +Description +----------- + +Jupyter javascript plugin for static sites. Thebe takes the Jupyter +front end, and make it work outside of the notebook context. + +This is used by Sage's Sphinx-based documentation build system to +produce html documentation that can be turned live (see +https://trac.sagemath.org/ticket/20690). + +License +------- + +MIT + + +Upstream Contact +---------------- + +- Home page: https://oreillymedia.github.io/thebe/ +- Source: https://github.com/oreillymedia/thebe/ + +Dependencies +------------ + +None. + + +Special Update/Build Instructions +--------------------------------- + +There are no release numbers, hence find the latest commit, download +https://github.com/oreillymedia/thebe/archive/$%7BCOMMIT%7D.zip and +rename it thebe-${COMMIT:0:8}.zip diff --git a/build/pkgs/thebe/SPKG.txt b/build/pkgs/thebe/SPKG.txt deleted file mode 100644 index de5c05c7c5b..00000000000 --- a/build/pkgs/thebe/SPKG.txt +++ /dev/null @@ -1,30 +0,0 @@ -= thebe = - -== Description == - -Jupyter javascript plugin for static sites. Thebe takes the Jupyter front end, -and make it work outside of the notebook context. - -This is used by Sage's Sphinx-based documentation build system to produce html -documentation that can be turned live (see -https://trac.sagemath.org/ticket/20690). - -== License == - -MIT - -== Upstream Contact == - -Home page: https://oreillymedia.github.io/thebe/ -Source: https://github.com/oreillymedia/thebe/ - -== Dependencies == - -None. - -== Special Update/Build Instructions == - -There are no release numbers, hence find the latest commit, download -https://github.com/oreillymedia/thebe/archive/${COMMIT}.zip and rename it -thebe-${COMMIT:0:8}.zip - diff --git a/build/pkgs/thebe/spkg-install b/build/pkgs/thebe/spkg-install.in similarity index 100% rename from build/pkgs/thebe/spkg-install rename to build/pkgs/thebe/spkg-install.in diff --git a/build/pkgs/threejs/SPKG.rst b/build/pkgs/threejs/SPKG.rst new file mode 100644 index 00000000000..2891a05b51c --- /dev/null +++ b/build/pkgs/threejs/SPKG.rst @@ -0,0 +1,29 @@ +Three.js +======== + +Description +----------- + +Three.js is a JavaScript library to display 3D graphics in the browser. + +License +------- + +MIT License + + +Upstream Contact +---------------- + +Home page: http://threejs.org + +Dependencies +------------ + +None. + + +Special Update/Build Instructions +--------------------------------- + +None. diff --git a/build/pkgs/threejs/SPKG.txt b/build/pkgs/threejs/SPKG.txt deleted file mode 100644 index 57052228b76..00000000000 --- a/build/pkgs/threejs/SPKG.txt +++ /dev/null @@ -1,22 +0,0 @@ -= Three.js = - -== Description == - -Three.js is a JavaScript library to display 3D graphics in the browser. - -== License == - -MIT License - -== Upstream Contact == - -Home page: http://threejs.org - -== Dependencies == - -None. - -== Special Update/Build Instructions == - -None. - diff --git a/build/pkgs/threejs/checksums.ini b/build/pkgs/threejs/checksums.ini index 162dff75979..924f689a6ac 100644 --- a/build/pkgs/threejs/checksums.ini +++ b/build/pkgs/threejs/checksums.ini @@ -1,4 +1,4 @@ tarball=threejs-VERSION.tar.gz -sha1=fe1ebb981e0347c8562acf356eab4c6fe5b86896 -md5=73eac3ca4f95eb42165328798df4a026 -cksum=3268794484 +sha1=55e28aff39d60e54069504eb84146961e1ca1cfd +md5=6082b27846f5b63a342e9cc91928785e +cksum=1458447267 diff --git a/build/pkgs/threejs/package-version.txt b/build/pkgs/threejs/package-version.txt index 3174e2cb4d7..cc32ce05abb 100644 --- a/build/pkgs/threejs/package-version.txt +++ b/build/pkgs/threejs/package-version.txt @@ -1 +1 @@ -r110 +r117 diff --git a/build/pkgs/threejs/spkg-install b/build/pkgs/threejs/spkg-install.in similarity index 100% rename from build/pkgs/threejs/spkg-install rename to build/pkgs/threejs/spkg-install.in diff --git a/build/pkgs/threejs/spkg-src b/build/pkgs/threejs/spkg-src index 1194cdafe10..ae0d79dc6c0 100755 --- a/build/pkgs/threejs/spkg-src +++ b/build/pkgs/threejs/spkg-src @@ -3,7 +3,7 @@ set -e # https://github.com/mrdoob/three.js/releases -GIT_VERSION=r110 +GIT_VERSION=r117 [ -n "${SAGE_ROOT}" ] || SAGE_ROOT="$(pwd)/../../../" diff --git a/build/pkgs/tides/SPKG.rst b/build/pkgs/tides/SPKG.rst new file mode 100644 index 00000000000..ba7738d2eff --- /dev/null +++ b/build/pkgs/tides/SPKG.rst @@ -0,0 +1,33 @@ +TIDES +===== + +Description +----------- + +TIDES is a library for integration of ODE's with high precision. + +License +------- + +GPLv3+ + + +Upstream Contact +---------------- + +- Marcos Rodriguez (marcos@unizar.es) + +Dependencies +------------ + +- gcc +- mpfr +- gmp + + +Special Update/Build Instructions +--------------------------------- + +minc_tides.patch changes the size of the name of the temporal files, so +there is no problem in systems that use long names. Also solves a bug in +the inverse function. diff --git a/build/pkgs/tides/SPKG.txt b/build/pkgs/tides/SPKG.txt deleted file mode 100644 index da9bbbd729c..00000000000 --- a/build/pkgs/tides/SPKG.txt +++ /dev/null @@ -1,25 +0,0 @@ -= TIDES = - -== Description == - -TIDES is a library for integration of ODE's with high precision. - -== License == - -GPLv3+ - -== Upstream Contact == - -* Marcos Rodriguez (marcos@unizar.es) - -== Dependencies == - -* gcc -* mpfr -* gmp - -== Special Update/Build Instructions == - -minc_tides.patch changes the size of the name of the temporal files, so -there is no problem in systems that use long names. Also solves a bug -in the inverse function. diff --git a/build/pkgs/tides/spkg-check b/build/pkgs/tides/spkg-check deleted file mode 100644 index 709f4602cbc..00000000000 --- a/build/pkgs/tides/spkg-check +++ /dev/null @@ -1,2 +0,0 @@ -cd src -sdh_make check AM_CFLAGS="" AM_FFLAGS="" diff --git a/build/pkgs/tides/spkg-check.in b/build/pkgs/tides/spkg-check.in new file mode 100644 index 00000000000..1b5261b0fc4 --- /dev/null +++ b/build/pkgs/tides/spkg-check.in @@ -0,0 +1,2 @@ +cd src +sdh_make_check AM_CFLAGS="" AM_FFLAGS="" diff --git a/build/pkgs/tides/spkg-install b/build/pkgs/tides/spkg-install.in similarity index 100% rename from build/pkgs/tides/spkg-install rename to build/pkgs/tides/spkg-install.in diff --git a/build/pkgs/topcom/SPKG.rst b/build/pkgs/topcom/SPKG.rst new file mode 100644 index 00000000000..60d53f2f8aa --- /dev/null +++ b/build/pkgs/topcom/SPKG.rst @@ -0,0 +1,47 @@ +TOPCOM +====== + +Description +----------- + +TOPCOM is a collection of clients to compute Triangulations Of Point +Configurations and Oriented Matroids, resp. + +The algorithms use only combinatorial data of the point configuration as +is given by its oriented matroid. Some basic commands for computing and +manipulating oriented matroids can also be accessed by the user. + +It was very much inspired by the maple program PUNTOS, which was written +by Jesus de Loera. TOPCOM is entirely written in C++, so there is a +significant speed up compared to PUNTOS. + +License +------- + +GPL v2 + + +Upstream Contact +---------------- + +:: + + Prof. Dr. Jörg Rambau + Lehrstuhl für Wirtschaftsmathematik + Raum FAN-D.1.29 (Sekretariat: FAN-D.1.30) + Universität Bayreuth + D-95440 Bayreuth + Germany + Tel: +49-921-55-7350, Fax: +49-921-55-7352 + http://www.rambau.wm.uni-bayreuth.de + +Dependencies +------------ + +- gmp, libcdd + + +Special Update/Build Instructions +--------------------------------- + +See spkg-src diff --git a/build/pkgs/topcom/SPKG.txt b/build/pkgs/topcom/SPKG.txt deleted file mode 100644 index 51772f4a3b8..00000000000 --- a/build/pkgs/topcom/SPKG.txt +++ /dev/null @@ -1,34 +0,0 @@ -= TOPCOM = - -== Description == -TOPCOM is a collection of clients to compute Triangulations Of Point -Configurations and Oriented Matroids, resp. - -The algorithms use only combinatorial data of the point configuration -as is given by its oriented matroid. Some basic commands for computing -and manipulating oriented matroids can also be accessed by the user. - -It was very much inspired by the maple program PUNTOS, which was -written by Jesus de Loera. TOPCOM is entirely written in C++, so there -is a significant speed up compared to PUNTOS. - -== License == -GPL v2 - -== Upstream Contact == -Prof. Dr. Jörg Rambau -Lehrstuhl für Wirtschaftsmathematik -Raum FAN-D.1.29 (Sekretariat: FAN-D.1.30) -Universität Bayreuth -D-95440 Bayreuth -Germany -Tel: +49-921-55-7350, Fax: +49-921-55-7352 -http://www.rambau.wm.uni-bayreuth.de - -== Dependencies == -* gmp, libcdd - -== Special Update/Build Instructions == - -See spkg-src - diff --git a/build/pkgs/topcom/spkg-install b/build/pkgs/topcom/spkg-install deleted file mode 100644 index 90307ef5d21..00000000000 --- a/build/pkgs/topcom/spkg-install +++ /dev/null @@ -1,28 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src -./configure \ - --prefix="$SAGE_LOCAL" \ - --libdir="$SAGE_LOCAL/lib" \ - $SAGE_CONFIGURE_GMP \ - --with-cdd="$SAGE_LOCAL" -if [ $? -ne 0 ]; then - echo "Error configuring TOPCOM." - exit 1 -fi - -$MAKE -if [ $? -ne 0 ]; then - echo "Error building TOPCOM." - exit 1 -fi - -$MAKE install-strip -if [ $? -ne 0 ]; then - echo "Error installing TOPCOM." - exit 1 -fi diff --git a/build/pkgs/topcom/spkg-install.in b/build/pkgs/topcom/spkg-install.in new file mode 100644 index 00000000000..53eb50b1b98 --- /dev/null +++ b/build/pkgs/topcom/spkg-install.in @@ -0,0 +1,22 @@ +cd src +./configure \ + --prefix="$SAGE_LOCAL" \ + --libdir="$SAGE_LOCAL/lib" \ + $SAGE_CONFIGURE_GMP \ + --with-cdd="$SAGE_LOCAL" +if [ $? -ne 0 ]; then + echo "Error configuring TOPCOM." + exit 1 +fi + +$MAKE +if [ $? -ne 0 ]; then + echo "Error building TOPCOM." + exit 1 +fi + +$MAKE install-strip +if [ $? -ne 0 ]; then + echo "Error installing TOPCOM." + exit 1 +fi diff --git a/build/pkgs/tornado/SPKG.rst b/build/pkgs/tornado/SPKG.rst new file mode 100644 index 00000000000..db446514d67 --- /dev/null +++ b/build/pkgs/tornado/SPKG.rst @@ -0,0 +1,23 @@ +tornado +======= + +Description +----------- + +Python web framework and asynchronous networking library + +License +------- + +Apache License + + +Upstream Contact +---------------- + +Home page: http://www.tornadoweb.org + +Dependencies +------------ + +Python diff --git a/build/pkgs/tornado/SPKG.txt b/build/pkgs/tornado/SPKG.txt deleted file mode 100644 index 7f0068d6516..00000000000 --- a/build/pkgs/tornado/SPKG.txt +++ /dev/null @@ -1,24 +0,0 @@ -= tornado = - -== Description == - -Python web framework and asynchronous networking library - -== License == - -Apache License - -== Upstream Contact == - -Home page: http://www.tornadoweb.org - -== Dependencies == - -Python - -== Changelog == - -=== tornado-3.1.1 (John H. Palmieri, 20 December 2013) === - - * Trac #14993: initial release. - diff --git a/build/pkgs/tornado/dependencies b/build/pkgs/tornado/dependencies index 7ac8b7bac09..7a3a585116d 100644 --- a/build/pkgs/tornado/dependencies +++ b/build/pkgs/tornado/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) backports_abc backports_ssl_match_hostname certifi singledispatch | setuptools pip +$(PYTHON) certifi | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/tornado/spkg-install b/build/pkgs/tornado/spkg-install deleted file mode 100644 index 1fe7b067f81..00000000000 --- a/build/pkgs/tornado/spkg-install +++ /dev/null @@ -1,13 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -sdh_pip_install . -if [ $? -ne 0 ]; then - echo "Error installing tornado ... exiting" - exit 1 -fi diff --git a/build/pkgs/tornado/spkg-install.in b/build/pkgs/tornado/spkg-install.in new file mode 100644 index 00000000000..058b1344dc2 --- /dev/null +++ b/build/pkgs/tornado/spkg-install.in @@ -0,0 +1,3 @@ +cd src + +sdh_pip_install . diff --git a/build/pkgs/tox/requirements.txt b/build/pkgs/tox/requirements.txt new file mode 100644 index 00000000000..053148f8486 --- /dev/null +++ b/build/pkgs/tox/requirements.txt @@ -0,0 +1 @@ +tox diff --git a/build/pkgs/tox/type b/build/pkgs/tox/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/tox/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/traitlets/SPKG.rst b/build/pkgs/traitlets/SPKG.rst new file mode 100644 index 00000000000..0d624dc30c8 --- /dev/null +++ b/build/pkgs/traitlets/SPKG.rst @@ -0,0 +1,9 @@ +traitlets +========= + +Description +----------- + +Traitlets Python config system + +A configuration system for Python applications. diff --git a/build/pkgs/traitlets/SPKG.txt b/build/pkgs/traitlets/SPKG.txt deleted file mode 100644 index 0cca54ec4bf..00000000000 --- a/build/pkgs/traitlets/SPKG.txt +++ /dev/null @@ -1,7 +0,0 @@ -= traitlets = - -== Description == - -Traitlets Python config system - -A configuration system for Python applications. diff --git a/build/pkgs/traitlets/checksums.ini b/build/pkgs/traitlets/checksums.ini index 3f00b9ccfae..dacc612f744 100644 --- a/build/pkgs/traitlets/checksums.ini +++ b/build/pkgs/traitlets/checksums.ini @@ -1,4 +1,4 @@ tarball=traitlets-VERSION.tar.gz -sha1=5f87b54cc7888b20e1c1c15e052a8c89f23879ea -md5=3068663f2f38fd939a9eb3a500ccc154 -cksum=3471003302 +sha1=1bfff63fcf608ae57202774015b7448e85f650b6 +md5=3a4f263af65d3d79f1c279f0247077ef +cksum=3794332761 diff --git a/build/pkgs/traitlets/dependencies b/build/pkgs/traitlets/dependencies index 9f3baafa568..242140707df 100644 --- a/build/pkgs/traitlets/dependencies +++ b/build/pkgs/traitlets/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip ipython_genutils decorator six enum34 +$(PYTHON) | $(PYTHON_TOOLCHAIN) ipython_genutils decorator six ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/traitlets/package-version.txt b/build/pkgs/traitlets/package-version.txt index cc2fbe89b6c..e91d9be2a86 100644 --- a/build/pkgs/traitlets/package-version.txt +++ b/build/pkgs/traitlets/package-version.txt @@ -1 +1 @@ -4.3.2 +4.3.3 diff --git a/build/pkgs/testpath/spkg-install b/build/pkgs/traitlets/spkg-install.in similarity index 100% rename from build/pkgs/testpath/spkg-install rename to build/pkgs/traitlets/spkg-install.in diff --git a/build/pkgs/twisted/SPKG.txt b/build/pkgs/twisted/SPKG.txt deleted file mode 100644 index d2122e44f7e..00000000000 --- a/build/pkgs/twisted/SPKG.txt +++ /dev/null @@ -1,8 +0,0 @@ -= twisted = - -== Description == - -An asynchronous networking framework written in Python - -An extensible framework for Python programming, with special focus on -event-based network programming and multiprotocol integration. diff --git a/build/pkgs/twisted/checksums.ini b/build/pkgs/twisted/checksums.ini deleted file mode 100644 index a720c8839e7..00000000000 --- a/build/pkgs/twisted/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=Twisted-VERSION.tar.bz2 -sha1=b9f183ae63a49c99619f7d37d1ae3a368d6cf886 -md5=e044af844623e9fbcbe29f578db6053a -cksum=3273908979 diff --git a/build/pkgs/twisted/dependencies b/build/pkgs/twisted/dependencies deleted file mode 100644 index 7d60db4e7a0..00000000000 --- a/build/pkgs/twisted/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | setuptools pip zope_interface - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/twisted/package-version.txt b/build/pkgs/twisted/package-version.txt deleted file mode 100644 index ff595906737..00000000000 --- a/build/pkgs/twisted/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -16.3.0.p0 diff --git a/build/pkgs/twisted/type b/build/pkgs/twisted/type deleted file mode 100644 index a6a7b9cd726..00000000000 --- a/build/pkgs/twisted/type +++ /dev/null @@ -1 +0,0 @@ -standard diff --git a/build/pkgs/typing/SPKG.txt b/build/pkgs/typing/SPKG.txt deleted file mode 100644 index 273da4324fc..00000000000 --- a/build/pkgs/typing/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= typing = - -== Description == - -Typing – Type Hints for Python - -This is a backport of the standard library typing module to Python -versions older than 3.6. - -Typing defines a standard notation for Python function and variable type -annotations. The notation can be used for documenting code in a concise, -standard format, and it has been designed to also be used by static and -runtime type checkers, static analyzers, IDEs and other tools. - -== License == - -Python Software Foundation License diff --git a/build/pkgs/typing/checksums.ini b/build/pkgs/typing/checksums.ini deleted file mode 100644 index a421db5e1c5..00000000000 --- a/build/pkgs/typing/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=typing-VERSION.tar.gz -sha1=8414f7e523f1f286f72392e9f8929d346df6f6a2 -md5=64614206b4bdc0864fc0e0bccd69efc9 -cksum=2256549185 diff --git a/build/pkgs/typing/dependencies b/build/pkgs/typing/dependencies deleted file mode 100644 index d5dab729e18..00000000000 --- a/build/pkgs/typing/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | pip - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/typing/package-version.txt b/build/pkgs/typing/package-version.txt deleted file mode 100644 index 4f2c1d15f6d..00000000000 --- a/build/pkgs/typing/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -3.6.6 diff --git a/build/pkgs/typing/spkg-install b/build/pkgs/typing/spkg-install deleted file mode 100644 index ee68cc6792e..00000000000 --- a/build/pkgs/typing/spkg-install +++ /dev/null @@ -1,6 +0,0 @@ -# The Python module typing is only useful for Python 2 and early -# Python 3 versions. See https://trac.sagemath.org/ticket/28499 -if [ $SAGE_PYTHON_VERSION -eq 2 ] -then - cd src && sdh_pip_install . -fi diff --git a/build/pkgs/typing/type b/build/pkgs/typing/type deleted file mode 100644 index a6a7b9cd726..00000000000 --- a/build/pkgs/typing/type +++ /dev/null @@ -1 +0,0 @@ -standard diff --git a/build/pkgs/tzlocal/SPKG.rst b/build/pkgs/tzlocal/SPKG.rst new file mode 100644 index 00000000000..ebeba1ad914 --- /dev/null +++ b/build/pkgs/tzlocal/SPKG.rst @@ -0,0 +1,16 @@ +pytz +==== + +Description +----------- + +World Timezone Definitions for Python + + +Special Update/Build Instructions +--------------------------------- + +The upstream tarball was repackaged after sanitizing the file +permissions with + +$ chmod go-w diff --git a/build/pkgs/tzlocal/checksums.ini b/build/pkgs/tzlocal/checksums.ini new file mode 100644 index 00000000000..d595a9d3d16 --- /dev/null +++ b/build/pkgs/tzlocal/checksums.ini @@ -0,0 +1,5 @@ +tarball=tzlocal-VERSION.tar.gz +sha1=7d2d590f68849e6b6371210bd808b40ec5619faf +md5=c0877603ff9de71cd8ca6ee2b50d2ebd +cksum=950088034 +upstream_url=https://pypi.io/packages/source/t/tzlocal/tzlocal-VERSION.tar.gz diff --git a/build/pkgs/tzlocal/dependencies b/build/pkgs/tzlocal/dependencies new file mode 100644 index 00000000000..15df0c4d6d8 --- /dev/null +++ b/build/pkgs/tzlocal/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/tzlocal/package-version.txt b/build/pkgs/tzlocal/package-version.txt new file mode 100644 index 00000000000..879b416e609 --- /dev/null +++ b/build/pkgs/tzlocal/package-version.txt @@ -0,0 +1 @@ +2.1 diff --git a/build/pkgs/traitlets/spkg-install b/build/pkgs/tzlocal/spkg-install.in similarity index 100% rename from build/pkgs/traitlets/spkg-install rename to build/pkgs/tzlocal/spkg-install.in diff --git a/build/pkgs/ipaddress/type b/build/pkgs/tzlocal/type similarity index 100% rename from build/pkgs/ipaddress/type rename to build/pkgs/tzlocal/type diff --git a/build/pkgs/valgrind/SPKG.rst b/build/pkgs/valgrind/SPKG.rst new file mode 100644 index 00000000000..e3553325b7f --- /dev/null +++ b/build/pkgs/valgrind/SPKG.rst @@ -0,0 +1,54 @@ +Valgrind +======== + +Description +----------- + +This is an optional spkg. It supports Linux on x86, x86-64, ppc, ppc64 +and ARM as well as Darwin (Mac OS X 10.5 and 10.6) on x86 and x86-64. + +Valgrind is an instrumentation framework for building dynamic analysis +tools. There are Valgrind tools that can automatically detect many +memory management and threading bugs, and profile your programs in +detail. You can also use Valgrind to build new tools. + +The Valgrind distribution currently includes six production-quality +tools: a memory error detector, two thread error detectors, a cache and +branch-prediction profiler, a call-graph generating cache and +branch-prediction profiler, and a heap profiler. It also includes three +experimental tools: a heap/stack/global array overrun detector, a second +heap profiler that examines how heap blocks are used, and a SimPoint +basic block vector generator. It runs on the following platforms: +X86/Linux, AMD64/Linux, ARM/Linux, PPC32/Linux, PPC64/Linux, +S390X/Linux, ARM/Android (2.3.x), X86/Darwin and AMD64/Darwin (Mac OS X +10.6 and 10.7). + +License +------- + +Valgrind is Open Source / Free Software, and is freely available under +the GNU General Public License, version 2. + + +Upstream Contact +---------------- + +- http://www.valgrind.org/ +- valgrind-user, valgrind-devel mailing lists + +Dependencies +------------ + +- None + + +Special Build Instructions +-------------------------- + +- To build on OS X, you need to use Apple's compiler. FSF GCC is + unsupported. + +Patches +~~~~~~~ + +- None. diff --git a/build/pkgs/valgrind/SPKG.txt b/build/pkgs/valgrind/SPKG.txt deleted file mode 100644 index fa065c32de4..00000000000 --- a/build/pkgs/valgrind/SPKG.txt +++ /dev/null @@ -1,44 +0,0 @@ -= Valgrind = - -== Description == - -This is an optional spkg. It supports Linux on x86, x86-64, ppc, ppc64 and ARM -as well as Darwin (Mac OS X 10.5 and 10.6) on x86 and x86-64. - -Valgrind is an instrumentation framework for building dynamic analysis tools. -There are Valgrind tools that can automatically detect many memory management -and threading bugs, and profile your programs in detail. You can also use -Valgrind to build new tools. - -The Valgrind distribution currently includes six production-quality tools: -a memory error detector, two thread error detectors, a cache and -branch-prediction profiler, a call-graph generating cache and branch-prediction -profiler, and a heap profiler. It also includes three experimental tools: a -heap/stack/global array overrun detector, a second heap profiler that examines -how heap blocks are used, and a SimPoint basic block vector generator. It runs -on the following platforms: X86/Linux, AMD64/Linux, ARM/Linux, PPC32/Linux, -PPC64/Linux, S390X/Linux, ARM/Android (2.3.x), X86/Darwin and AMD64/Darwin -(Mac OS X 10.6 and 10.7). - -== License == - -Valgrind is Open Source / Free Software, and is freely available under the -GNU General Public License, version 2. - -== Upstream Contact == - - * http://www.valgrind.org/ - * valgrind-user, valgrind-devel mailing lists - -== Dependencies == - - * None - -== Special Build Instructions == - - * To build on OS X, you need to use Apple's compiler. FSF GCC is unsupported. - -=== Patches === - - * None. - diff --git a/build/pkgs/valgrind/spkg-check b/build/pkgs/valgrind/spkg-check deleted file mode 100644 index ec8ddfb4780..00000000000 --- a/build/pkgs/valgrind/spkg-check +++ /dev/null @@ -1,13 +0,0 @@ -if [ -z "$SAGE_LOCAL" ] ; then - echo >&2 "Error - SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src/ - -$MAKE regtest -if [ $? -ne 0 ]; then - echo >&2 "Error testing Valgrind" - exit 1 -fi diff --git a/build/pkgs/valgrind/spkg-check.in b/build/pkgs/valgrind/spkg-check.in new file mode 100644 index 00000000000..fa01545f4a8 --- /dev/null +++ b/build/pkgs/valgrind/spkg-check.in @@ -0,0 +1,3 @@ +cd src + +$MAKE regtest diff --git a/build/pkgs/valgrind/spkg-install b/build/pkgs/valgrind/spkg-install deleted file mode 100644 index 42c2fc17804..00000000000 --- a/build/pkgs/valgrind/spkg-install +++ /dev/null @@ -1,26 +0,0 @@ -if [ -z "$SAGE_LOCAL" ] ; then - echo >&2 "Error - SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src/ - -./configure --prefix=$SAGE_LOCAL -if [ $? -ne 0 ]; then - echo >&2 "Error configuring Valgrind" - exit 1 -fi - -$MAKE -if [ $? -ne 0 ]; then - echo >&2 "Error building Valgrind" - exit 1 -fi - -$MAKE install -if [ $? -ne 0 ]; then - echo >&2 "Error installing Valgrind" - exit 1 -fi - diff --git a/build/pkgs/valgrind/spkg-install.in b/build/pkgs/valgrind/spkg-install.in new file mode 100644 index 00000000000..2aa2762a7ed --- /dev/null +++ b/build/pkgs/valgrind/spkg-install.in @@ -0,0 +1,20 @@ +cd src + +./configure --prefix=$SAGE_LOCAL +if [ $? -ne 0 ]; then + echo >&2 "Error configuring Valgrind" + exit 1 +fi + +$MAKE +if [ $? -ne 0 ]; then + echo >&2 "Error building Valgrind" + exit 1 +fi + +$MAKE install +if [ $? -ne 0 ]; then + echo >&2 "Error installing Valgrind" + exit 1 +fi + diff --git a/build/pkgs/vcversioner/SPKG.rst b/build/pkgs/vcversioner/SPKG.rst new file mode 100644 index 00000000000..31ade501ff3 --- /dev/null +++ b/build/pkgs/vcversioner/SPKG.rst @@ -0,0 +1,25 @@ +vcversioner +=========== + +Description +----------- + +Write a setup.py with no version information specified, and vcversioner +will find a recent, properly-formatted VCS tag and extract a version +from it. + +License +------- + +Python Software Foundation License + + +Upstream Contact +---------------- + +Home page: https://pypi.python.org/pypi/vcversioner/ + +Dependencies +------------ + +Python, Setuptools diff --git a/build/pkgs/vcversioner/SPKG.txt b/build/pkgs/vcversioner/SPKG.txt deleted file mode 100644 index eae91aa062e..00000000000 --- a/build/pkgs/vcversioner/SPKG.txt +++ /dev/null @@ -1,20 +0,0 @@ -= vcversioner = - -== Description == - -Write a setup.py with no version information specified, and -vcversioner will find a recent, properly-formatted VCS tag and extract -a version from it. - -== License == - -Python Software Foundation License - -== Upstream Contact == - -Home page: https://pypi.python.org/pypi/vcversioner/ - -== Dependencies == - -Python, Setuptools - diff --git a/build/pkgs/vcversioner/dependencies b/build/pkgs/vcversioner/dependencies index 2573414ce8a..15df0c4d6d8 100644 --- a/build/pkgs/vcversioner/dependencies +++ b/build/pkgs/vcversioner/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/vcversioner/spkg-install b/build/pkgs/vcversioner/spkg-install.in similarity index 100% rename from build/pkgs/vcversioner/spkg-install rename to build/pkgs/vcversioner/spkg-install.in diff --git a/build/pkgs/wcwidth/SPKG.rst b/build/pkgs/wcwidth/SPKG.rst new file mode 100644 index 00000000000..ed233830d3a --- /dev/null +++ b/build/pkgs/wcwidth/SPKG.rst @@ -0,0 +1,9 @@ +wcwidth +======= + +Description +----------- + +Measures number of Terminal column cells of wide-character codes + +https://pypi.python.org/pypi/wcwidth diff --git a/build/pkgs/wcwidth/SPKG.txt b/build/pkgs/wcwidth/SPKG.txt deleted file mode 100644 index d91b994aacf..00000000000 --- a/build/pkgs/wcwidth/SPKG.txt +++ /dev/null @@ -1,7 +0,0 @@ -= wcwidth = - -== Description == - -Measures number of Terminal column cells of wide-character codes - -https://pypi.python.org/pypi/wcwidth diff --git a/build/pkgs/wcwidth/dependencies b/build/pkgs/wcwidth/dependencies index 551ec7ee0ee..15df0c4d6d8 100644 --- a/build/pkgs/wcwidth/dependencies +++ b/build/pkgs/wcwidth/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip setuptools +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/twisted/spkg-install b/build/pkgs/wcwidth/spkg-install.in similarity index 100% rename from build/pkgs/twisted/spkg-install rename to build/pkgs/wcwidth/spkg-install.in diff --git a/build/pkgs/webencodings/SPKG.rst b/build/pkgs/webencodings/SPKG.rst new file mode 100644 index 00000000000..3fa1c3aed62 --- /dev/null +++ b/build/pkgs/webencodings/SPKG.rst @@ -0,0 +1,23 @@ +webencodings +============ + +Description +----------- + +Character encoding aliases for legacy web content. + +License +------- + +BSD License + + +Upstream Contact +---------------- + +Home Page: https://github.com/gsnedders/python-webencodings + +Dependencies +------------ + +Python diff --git a/build/pkgs/webencodings/SPKG.txt b/build/pkgs/webencodings/SPKG.txt deleted file mode 100644 index 7480c468420..00000000000 --- a/build/pkgs/webencodings/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= webencodings = - -== Description == - -Character encoding aliases for legacy web content. - -== License == - -BSD License - -== Upstream Contact == - -Home Page: https://github.com/gsnedders/python-webencodings - -== Dependencies == - -Python diff --git a/build/pkgs/webencodings/dependencies b/build/pkgs/webencodings/dependencies index 2573414ce8a..15df0c4d6d8 100644 --- a/build/pkgs/webencodings/dependencies +++ b/build/pkgs/webencodings/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/wcwidth/spkg-install b/build/pkgs/webencodings/spkg-install.in similarity index 100% rename from build/pkgs/wcwidth/spkg-install rename to build/pkgs/webencodings/spkg-install.in diff --git a/build/pkgs/werkzeug/SPKG.txt b/build/pkgs/werkzeug/SPKG.txt deleted file mode 100644 index fac6f309dee..00000000000 --- a/build/pkgs/werkzeug/SPKG.txt +++ /dev/null @@ -1,18 +0,0 @@ -= Werkzeug = - -== Description == - -The Swiss Army knife of Python web development - -Werkzeug started as simple collection of various utilities for WSGI -applications and has become one of the most advanced WSGI utility modules. It -includes a powerful debugger, full featured request and response objects, HTTP -utilities to handle entity tags, cache control headers, HTTP dates, cookie -handling, file uploads, a powerful URL routing system and a bunch of community -contributed addon modules. - -Werkzeug is unicode aware and doesn't enforce a specific template engine, -database adapter or anything else. It doesn’t even enforce a specific way of -handling requests and leaves all that up to the developer. It's most useful -for end user applications which should work on as many server environments as -possible (such as blogs, wikis, bulletin boards, etc.). diff --git a/build/pkgs/werkzeug/checksums.ini b/build/pkgs/werkzeug/checksums.ini deleted file mode 100644 index 409b4650988..00000000000 --- a/build/pkgs/werkzeug/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=Werkzeug-VERSION.tar.gz -sha1=4b979fb960c5b5507ccb8a705931fa217013483d -md5=6d20b5be2d245be4ac7706cc390d130c -cksum=1712124090 diff --git a/build/pkgs/werkzeug/dependencies b/build/pkgs/werkzeug/dependencies deleted file mode 100644 index d5dab729e18..00000000000 --- a/build/pkgs/werkzeug/dependencies +++ /dev/null @@ -1,5 +0,0 @@ -$(PYTHON) | pip - ----------- -All lines of this file are ignored except the first. -It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/werkzeug/package-version.txt b/build/pkgs/werkzeug/package-version.txt deleted file mode 100644 index 930e3000bdc..00000000000 --- a/build/pkgs/werkzeug/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -0.14.1 diff --git a/build/pkgs/werkzeug/spkg-install b/build/pkgs/werkzeug/spkg-install deleted file mode 100644 index deba1bb42bb..00000000000 --- a/build/pkgs/werkzeug/spkg-install +++ /dev/null @@ -1 +0,0 @@ -cd src && sdh_pip_install . diff --git a/build/pkgs/werkzeug/type b/build/pkgs/werkzeug/type deleted file mode 100644 index a6a7b9cd726..00000000000 --- a/build/pkgs/werkzeug/type +++ /dev/null @@ -1 +0,0 @@ -standard diff --git a/build/pkgs/wheel/checksums.ini b/build/pkgs/wheel/checksums.ini new file mode 100644 index 00000000000..4365ece9d85 --- /dev/null +++ b/build/pkgs/wheel/checksums.ini @@ -0,0 +1,5 @@ +tarball=wheel-VERSION.tar.gz +sha1=6c3ef86120c454fefb3154460c466a82a985982a +md5=ce2a27f99c130a927237b5da1ff5ceaf +cksum=3327534776 +upstream_url=https://pypi.io/packages/source/w/wheel/wheel-VERSION.tar.gz diff --git a/build/pkgs/wheel/dependencies b/build/pkgs/wheel/dependencies new file mode 100644 index 00000000000..dbe5d796c45 --- /dev/null +++ b/build/pkgs/wheel/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) setuptools + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/wheel/package-version.txt b/build/pkgs/wheel/package-version.txt new file mode 100644 index 00000000000..3f8003cd4c4 --- /dev/null +++ b/build/pkgs/wheel/package-version.txt @@ -0,0 +1 @@ +0.34.2 diff --git a/build/pkgs/wheel/spkg-install.in b/build/pkgs/wheel/spkg-install.in new file mode 100644 index 00000000000..9c0353a87ef --- /dev/null +++ b/build/pkgs/wheel/spkg-install.in @@ -0,0 +1,9 @@ +cd src + +versions=3 + +for vers in $versions; do + python${vers} setup.py --no-user-cfg install \ + --single-version-externally-managed --root="$SAGE_DESTDIR" || \ + sdh_die "Error building / installing wheel for Python ${vers}" +done diff --git a/build/pkgs/pathlib2/type b/build/pkgs/wheel/type similarity index 100% rename from build/pkgs/pathlib2/type rename to build/pkgs/wheel/type diff --git a/build/pkgs/widgetsnbextension/SPKG.rst b/build/pkgs/widgetsnbextension/SPKG.rst new file mode 100644 index 00000000000..7c14261cf95 --- /dev/null +++ b/build/pkgs/widgetsnbextension/SPKG.rst @@ -0,0 +1,7 @@ +widgetsnbextension +================== + +Description +----------- + +Interactive HTML widgets for Jupyter notebooks. diff --git a/build/pkgs/widgetsnbextension/SPKG.txt b/build/pkgs/widgetsnbextension/SPKG.txt deleted file mode 100644 index e432175c9e0..00000000000 --- a/build/pkgs/widgetsnbextension/SPKG.txt +++ /dev/null @@ -1,6 +0,0 @@ -= widgetsnbextension = - -== Description == - -Interactive HTML widgets for Jupyter notebooks. - diff --git a/build/pkgs/widgetsnbextension/dependencies b/build/pkgs/widgetsnbextension/dependencies index a1db344087a..f5b9be8f947 100644 --- a/build/pkgs/widgetsnbextension/dependencies +++ b/build/pkgs/widgetsnbextension/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | setuptools pip notebook jupyter_core +$(PYTHON) | $(PYTHON_TOOLCHAIN) notebook jupyter_core ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/widgetsnbextension/spkg-install b/build/pkgs/widgetsnbextension/spkg-install deleted file mode 100644 index deba1bb42bb..00000000000 --- a/build/pkgs/widgetsnbextension/spkg-install +++ /dev/null @@ -1 +0,0 @@ -cd src && sdh_pip_install . diff --git a/build/pkgs/webencodings/spkg-install b/build/pkgs/widgetsnbextension/spkg-install.in similarity index 100% rename from build/pkgs/webencodings/spkg-install rename to build/pkgs/widgetsnbextension/spkg-install.in diff --git a/build/pkgs/xz/SPKG.rst b/build/pkgs/xz/SPKG.rst new file mode 100644 index 00000000000..ded4e934abb --- /dev/null +++ b/build/pkgs/xz/SPKG.rst @@ -0,0 +1,23 @@ +xz +== + +Description +----------- + +XZ Utils is free general-purpose data compression software with a high +compression ratio. + +License +------- + +Some parts public domain, other parts GNU LGPLv2.1, GNU GPLv2, or GNU +GPLv3. + + +Upstream Contact +---------------- + +http://tukaani.org/xz/ + +Dependencies +------------ diff --git a/build/pkgs/xz/SPKG.txt b/build/pkgs/xz/SPKG.txt deleted file mode 100644 index 7a66b43d63e..00000000000 --- a/build/pkgs/xz/SPKG.txt +++ /dev/null @@ -1,17 +0,0 @@ -= xz = - -== Description == - -XZ Utils is free general-purpose data compression software with a high compression ratio. - -== License == - -Some parts public domain, -other parts GNU LGPLv2.1, GNU GPLv2, or GNU GPLv3. - -== Upstream Contact == - -http://tukaani.org/xz/ - -== Dependencies == - diff --git a/build/pkgs/xz/distros/cygwin.txt b/build/pkgs/xz/distros/cygwin.txt new file mode 100644 index 00000000000..0267fff5270 --- /dev/null +++ b/build/pkgs/xz/distros/cygwin.txt @@ -0,0 +1 @@ +xz liblzma-devel diff --git a/build/pkgs/xz/distros/homebrew.txt b/build/pkgs/xz/distros/homebrew.txt new file mode 100644 index 00000000000..d66e95ca507 --- /dev/null +++ b/build/pkgs/xz/distros/homebrew.txt @@ -0,0 +1 @@ +xz diff --git a/build/pkgs/xz/distros/slackware.txt b/build/pkgs/xz/distros/slackware.txt new file mode 100644 index 00000000000..d66e95ca507 --- /dev/null +++ b/build/pkgs/xz/distros/slackware.txt @@ -0,0 +1 @@ +xz diff --git a/build/pkgs/xz/spkg-check b/build/pkgs/xz/spkg-check deleted file mode 100644 index 5119b4f7646..00000000000 --- a/build/pkgs/xz/spkg-check +++ /dev/null @@ -1,14 +0,0 @@ -cd src/ - -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" - exit 1 -fi - -$MAKE check -if [ $? -ne 0 ]; then - echo "Error in testing xz" - exit 1 -fi - diff --git a/build/pkgs/xz/spkg-check.in b/build/pkgs/xz/spkg-check.in new file mode 100644 index 00000000000..917c8eb7209 --- /dev/null +++ b/build/pkgs/xz/spkg-check.in @@ -0,0 +1,3 @@ +cd src + +$MAKE check diff --git a/build/pkgs/xz/spkg-configure.m4 b/build/pkgs/xz/spkg-configure.m4 index 820b5187802..ec6c4a9a9bb 100644 --- a/build/pkgs/xz/spkg-configure.m4 +++ b/build/pkgs/xz/spkg-configure.m4 @@ -2,11 +2,11 @@ SAGE_SPKG_CONFIGURE([xz], [ AC_CHECK_LIB([lzma], [lzma_raw_decoder], [lzma_cv_liblzma=yes], [lzma_cv_liblzma=no]) AC_CHECK_HEADER([lzma.h], [lzma_cv_lzma_h=yes], [lzma_cv_lzma_h=no]) if test "$lzma_cv_liblzma" = "yes" && test "$lzma_cv_lzma_h" = "yes"; then - AC_CACHE_CHECK([for xz >= 5.0.0], [ac_cv_path_XZ], [ + AC_CACHE_CHECK([for xz >= 4.999.0], [ac_cv_path_XZ], [ AC_PATH_PROGS_FEATURE_CHECK([XZ], [xz], [ xz_version=`$ac_path_XZ --version 2>&1 | cut -d' ' -f4 | $SED -n 1p` AS_IF([test -n "$xz_version"], [ - AX_COMPARE_VERSION([$xz_version], [ge], [5.0.0], [ + AX_COMPARE_VERSION([$xz_version], [ge], [4.999.0], [ ac_cv_path_XZ="$ac_path_XZ" ]) ]) diff --git a/build/pkgs/xz/spkg-install b/build/pkgs/xz/spkg-install.in similarity index 100% rename from build/pkgs/xz/spkg-install rename to build/pkgs/xz/spkg-install.in diff --git a/build/pkgs/yasm/SPKG.rst b/build/pkgs/yasm/SPKG.rst new file mode 100644 index 00000000000..63a33301845 --- /dev/null +++ b/build/pkgs/yasm/SPKG.rst @@ -0,0 +1,41 @@ +yasm +==== + +Description +----------- + +Yasm is a complete rewrite of the NASM assembler under the “new” BSD +License (some portions are under other licenses, see COPYING for +details). + +Yasm currently supports the x86 and AMD64 instruction sets, accepts NASM +and GAS assembler syntaxes, outputs binary, ELF32, ELF64, 32 and 64-bit +Mach-O, RDOFF2, COFF, Win32, and Win64 object formats, and generates +source debugging information in STABS, DWARF 2, and CodeView 8 formats. + +Yasm can be easily integrated into Visual Studio 2005/2008 and 2010 for +assembly of NASM or GAS syntax code into Win32 or Win64 object files. + +See https://yasm.tortall.net + +License +------- + +Yasm is licensed under the 2-clause and 3-clause “revised” BSD licenses, +with one exception: the Bit::Vector module used by the mainline version +of Yasm to implement its large integer and machine-independent floating +point support is triple-licensed under the Artistic license, GPL, and +LGPL. The “yasm-nextgen” codebase uses a different BSD-licensed +implementation and is thus entirely under BSD-equivalent licenses. The +full text of the licenses are provided in the Yasm source distribution. + + +Upstream Contact +---------------- + +- https://yasm.tortall.net + +Dependencies +------------ + +- none diff --git a/build/pkgs/yasm/SPKG.txt b/build/pkgs/yasm/SPKG.txt deleted file mode 100644 index 3dd72875678..00000000000 --- a/build/pkgs/yasm/SPKG.txt +++ /dev/null @@ -1,19 +0,0 @@ -= yasm = - -== Description == -Yasm is a complete rewrite of the NASM assembler under the “new” BSD License (some portions are under other licenses, see COPYING for details). - -Yasm currently supports the x86 and AMD64 instruction sets, accepts NASM and GAS assembler syntaxes, outputs binary, ELF32, ELF64, 32 and 64-bit Mach-O, RDOFF2, COFF, Win32, and Win64 object formats, and generates source debugging information in STABS, DWARF 2, and CodeView 8 formats. - -Yasm can be easily integrated into Visual Studio 2005/2008 and 2010 for assembly of NASM or GAS syntax code into Win32 or Win64 object files. - -See https://yasm.tortall.net - -== License == -Yasm is licensed under the 2-clause and 3-clause “revised” BSD licenses, with one exception: the Bit::Vector module used by the mainline version of Yasm to implement its large integer and machine-independent floating point support is triple-licensed under the Artistic license, GPL, and LGPL. The “yasm-nextgen” codebase uses a different BSD-licensed implementation and is thus entirely under BSD-equivalent licenses. The full text of the licenses are provided in the Yasm source distribution. - -== Upstream Contact == - * https://yasm.tortall.net - -== Dependencies == - * none diff --git a/build/pkgs/yasm/distros/alpine.txt b/build/pkgs/yasm/distros/alpine.txt new file mode 100644 index 00000000000..eff8d5c7abd --- /dev/null +++ b/build/pkgs/yasm/distros/alpine.txt @@ -0,0 +1 @@ +yasm diff --git a/build/pkgs/yasm/distros/cygwin.txt b/build/pkgs/yasm/distros/cygwin.txt new file mode 100644 index 00000000000..eff8d5c7abd --- /dev/null +++ b/build/pkgs/yasm/distros/cygwin.txt @@ -0,0 +1 @@ +yasm diff --git a/build/pkgs/yasm/distros/homebrew.txt b/build/pkgs/yasm/distros/homebrew.txt new file mode 100644 index 00000000000..eff8d5c7abd --- /dev/null +++ b/build/pkgs/yasm/distros/homebrew.txt @@ -0,0 +1 @@ +yasm diff --git a/build/pkgs/yasm/distros/opensuse.txt b/build/pkgs/yasm/distros/opensuse.txt new file mode 100644 index 00000000000..eff8d5c7abd --- /dev/null +++ b/build/pkgs/yasm/distros/opensuse.txt @@ -0,0 +1 @@ +yasm diff --git a/build/pkgs/yasm/distros/slackware.txt b/build/pkgs/yasm/distros/slackware.txt new file mode 100644 index 00000000000..eff8d5c7abd --- /dev/null +++ b/build/pkgs/yasm/distros/slackware.txt @@ -0,0 +1 @@ +yasm diff --git a/build/pkgs/yasm/spkg-check b/build/pkgs/yasm/spkg-check deleted file mode 100644 index 7f43d969f3d..00000000000 --- a/build/pkgs/yasm/spkg-check +++ /dev/null @@ -1,15 +0,0 @@ -if [ "$SAGE_LOCAL" = "" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -cd src - -# -j1 needed to avoid a possible race condition in the tests -# see https://trac.sagemath.org/ticket/23217 -$MAKE -j1 check -if [ $? -ne 0 ]; then - echo >&2 "Error checking yasm." - exit 1 -fi diff --git a/build/pkgs/yasm/spkg-check.in b/build/pkgs/yasm/spkg-check.in new file mode 100644 index 00000000000..ae888d81474 --- /dev/null +++ b/build/pkgs/yasm/spkg-check.in @@ -0,0 +1,5 @@ +cd src + +# -j1 needed to avoid a possible race condition in the tests +# see https://trac.sagemath.org/ticket/23217 +$MAKE -j1 check diff --git a/build/pkgs/yasm/spkg-install b/build/pkgs/yasm/spkg-install.in similarity index 100% rename from build/pkgs/yasm/spkg-install rename to build/pkgs/yasm/spkg-install.in diff --git a/build/pkgs/zeromq/SPKG.rst b/build/pkgs/zeromq/SPKG.rst new file mode 100644 index 00000000000..37d6c669b58 --- /dev/null +++ b/build/pkgs/zeromq/SPKG.rst @@ -0,0 +1,31 @@ +zeromq +====== + +Description +----------- + +A modern networking library. Also known as 0mq or zmq. The same API is +provided by http://www.crossroads.io, though we currently use the +http://www.zeromq.org implementation. + +License +------- + +LGPLv3+ + + +Upstream Contact +---------------- + +http://www.zeromq.org + +Dependencies +------------ + +A working compiler. + + +Special Update/Build Instructions +--------------------------------- + +N/A diff --git a/build/pkgs/zeromq/SPKG.txt b/build/pkgs/zeromq/SPKG.txt deleted file mode 100644 index 7459f724a82..00000000000 --- a/build/pkgs/zeromq/SPKG.txt +++ /dev/null @@ -1,24 +0,0 @@ -= zeromq = - -== Description == - -A modern networking library. Also known as 0mq or zmq. The same API is -provided by http://www.crossroads.io, though we currently use the -http://www.zeromq.org implementation. - -== License == - -LGPLv3+ - -== Upstream Contact == - -http://www.zeromq.org - -== Dependencies == - -A working compiler. - -== Special Update/Build Instructions == - -N/A - diff --git a/build/pkgs/zeromq/distros/cygwin.txt b/build/pkgs/zeromq/distros/cygwin.txt new file mode 100644 index 00000000000..57737d06afb --- /dev/null +++ b/build/pkgs/zeromq/distros/cygwin.txt @@ -0,0 +1 @@ +libzmq-devel diff --git a/build/pkgs/zeromq/distros/fedora.txt b/build/pkgs/zeromq/distros/fedora.txt new file mode 100644 index 00000000000..901dce11d4f --- /dev/null +++ b/build/pkgs/zeromq/distros/fedora.txt @@ -0,0 +1 @@ +zeromq zeromq-devel diff --git a/build/pkgs/zeromq/distros/homebrew.txt b/build/pkgs/zeromq/distros/homebrew.txt new file mode 100644 index 00000000000..bd4caa9e83d --- /dev/null +++ b/build/pkgs/zeromq/distros/homebrew.txt @@ -0,0 +1 @@ +zeromq diff --git a/build/pkgs/zeromq/spkg-check b/build/pkgs/zeromq/spkg-check.in similarity index 100% rename from build/pkgs/zeromq/spkg-check rename to build/pkgs/zeromq/spkg-check.in diff --git a/build/pkgs/zeromq/spkg-install b/build/pkgs/zeromq/spkg-install.in similarity index 100% rename from build/pkgs/zeromq/spkg-install rename to build/pkgs/zeromq/spkg-install.in diff --git a/build/pkgs/zlib/SPKG.rst b/build/pkgs/zlib/SPKG.rst new file mode 100644 index 00000000000..0d32a6327f9 --- /dev/null +++ b/build/pkgs/zlib/SPKG.rst @@ -0,0 +1,33 @@ +zlib +==== + +Description +----------- + +Massively Spiffy Yet Delicately Unobtrusive Compression Library (Also +Free, Not to Mention Unencumbered by Patents) + +License +------- + +- Modified BSD. + + +Upstream Contact +---------------- + +- http://www.zlib.net/ + +Dependencies +------------ + +- None + + +Special Update/Build Instructions +--------------------------------- + +Patches +~~~~~~~ + +- cygwin_symbols.patch: remove undefined symbols on Cygwin. diff --git a/build/pkgs/zlib/SPKG.txt b/build/pkgs/zlib/SPKG.txt deleted file mode 100644 index d5f3d58f208..00000000000 --- a/build/pkgs/zlib/SPKG.txt +++ /dev/null @@ -1,69 +0,0 @@ -= zlib = - -== Description == - -Massively Spiffy Yet Delicately Unobtrusive Compression Library (Also -Free, Not to Mention Unencumbered by Patents) - -== License == - - * Modified BSD. - -== Upstream Contact == - - * http://www.zlib.net/ - -== Dependencies == - - * None - -== Special Update/Build Instructions == - -=== Patches === - - * cygwin_symbols.patch: remove undefined symbols on Cygwin. - -=== zlib-1.2.8.p0 (Jean-Pierre Flori, 6 August 2013) === - * Trac #14985: Let zlib 1.2.>=7 build on Cygwin. - -=== zlib-1.2.8 (Jeroen Demeyer, 30 May 2013) === - * Trac #14661: Upgrade to version 1.2.8 - * Do not unset AR (why was that needed?) - -=== zlib-1.2.6.p0 (Jean-Pierre Flori, 5 January 2013) === - * Trac #13914: Install shared objects on Cygwin. - -=== zlib-1.2.6 (Jeroen Demeyer, Leif Leonhardy, 11 April 2012) === - * Trac #12800: Upgrade to zlib-1.2.6 - * Remove patches/Makefile.in.patch which is no longer needed - * Exit when building fails - * Clean up spkg-install - -=== zlib-1.2.5.p0 (Jeroen Demeyer, 9 December 2011) === - * Trac #12138: Fix parallel build by patching Makefile.in - * Use $MAKE variable in spkg-install - -=== zlib-1.2.5 (David Kirkby, May 21st 2010) === - * Update to the latest upstream version - 1.2.5 - * Remove the patches directory. - * Remove hacks for OSX - * Remove the -fPIC flag from spkg-install, which was added so zlib - worked properly on at least Debian on the Opteron processor. The - -fPIC flag is added by the new zlib, so there is no need to add it - in spkg-install. This has been tested on OS X, Linux and OpenSolaris. - In each case, -fPIC gets added. - * There was no need to remove object files *(.obj) are there - are none in the latest source code. - -=== zlib-1.2.3.p5 (William Stein, December 17, 2009) === - * Remove some binary crap - -=== zlib-1.2.3.p4 (Michael Abshoff, August 18th, 2008) === - * handle 64 bit OSX build case - -=== zlib-1.2.3.p3 (Michael Abshoff) === - * add "-g" to CFLAGS to get better backtraces and valgrind stack traces. - -=== zlib-1.2.3.p2 === - * first documented version - diff --git a/build/pkgs/zlib/distros/cygwin.txt b/build/pkgs/zlib/distros/cygwin.txt new file mode 100644 index 00000000000..f47c16b504b --- /dev/null +++ b/build/pkgs/zlib/distros/cygwin.txt @@ -0,0 +1 @@ +zlib-devel diff --git a/build/pkgs/zlib/distros/homebrew.txt b/build/pkgs/zlib/distros/homebrew.txt new file mode 100644 index 00000000000..f22003e83c1 --- /dev/null +++ b/build/pkgs/zlib/distros/homebrew.txt @@ -0,0 +1 @@ +zlib diff --git a/build/pkgs/zlib/distros/slackware.txt b/build/pkgs/zlib/distros/slackware.txt new file mode 100644 index 00000000000..f22003e83c1 --- /dev/null +++ b/build/pkgs/zlib/distros/slackware.txt @@ -0,0 +1 @@ +zlib diff --git a/build/pkgs/zlib/spkg-install b/build/pkgs/zlib/spkg-install.in similarity index 100% rename from build/pkgs/zlib/spkg-install rename to build/pkgs/zlib/spkg-install.in diff --git a/build/pkgs/zn_poly/SPKG.rst b/build/pkgs/zn_poly/SPKG.rst new file mode 100644 index 00000000000..5448fa62620 --- /dev/null +++ b/build/pkgs/zn_poly/SPKG.rst @@ -0,0 +1,96 @@ +zn_poly +======= + +Description +----------- + +zn_poly is a C library for polynomial arithmetic in Z/nZ[x], where n is +any modulus that fits into an unsigned long. + +Website: https://gitlab.com/sagemath/zn_poly + +Note: Original website is at http://cims.nyu.edu/~harvey/zn_poly/ but is +no longer maintained. Sage maintains an "official" continuation of the +project at the above link. + +License +------- + +GPL V2 or V3. Some of the code has been copied from other projects - see +the file src/COPYING for details. + + +Upstream Contact +---------------- + +- David Harvey +- \E. M. Bray + +Dependencies +------------ + +- GMP/MPIR +- (some) Python (to create the Makefile) +- GNU patch +- NTL apparently only if we configured zn_poly differently (same for + FLINT) + + +Special Update/Build Instructions +--------------------------------- + +- Make sure the patches still apply. + + Especially changes in ``makemakefile.py`` may also require changes to + ``spkg-install`` (and perhaps also ``spkg-check``). + +- There's also a ``--use-flint`` option to ``configure``; no idea what + it does, + and we currently don't use it either. + +- TODO: +- Use ``make install`` instead of manually "installing" (copying and + symlinking) the [shared] libraries and header files. This requires + further + tweaking of ``makemakefile.py``, since it currently only installs a + static + library and the headers. + +- If everything's fine, i.e., no problems arise, some comments and + especially some code I currently just commented out can certainly be removed. + (-leif, 04/2012) + +- The version number "0.9.p11" is used as a doctest in the function + package_versions in sage/misc/packages.py, so if this package gets + upgraded, that doctest needs to be changed. + +Patches +~~~~~~~ + +- All patches from Sage have been merged into upstream. These include: +- makemakefile.py.patch: + + Improves the Python script creating the Makeefile for better use at + least within Sage; see patch for details. (Last modified at #12433, + which added and changed a lot.) + +- profiler.c.patch, zn_poly.h.patch: + + Fix potential redefinition of ``ulong`` (in combination with other + headers). + +- mpn_mulmid-tune.c.patch, mulmid-tune.c.patch, mul-tune.c.patch: + + Fix "jump into scope of identifier with variably modified type" + errors. (See #8771). + +- mpn_mulmid-test.c.patch: + + Fix a potential problem when the value of ZNP_mpn_smp_kara_thresh is + SIZE_MAX, this is usually irrealistic but can happen at least on + linux on power7 with gcc-4.7.1 (see #14098). + +- fix_fudge_factor_in_nuss-test.c.patch: + + As the name says; fix provided by upstream (David Harvey); see + #13947. diff --git a/build/pkgs/zn_poly/SPKG.txt b/build/pkgs/zn_poly/SPKG.txt deleted file mode 100644 index 2f9b0c44a80..00000000000 --- a/build/pkgs/zn_poly/SPKG.txt +++ /dev/null @@ -1,196 +0,0 @@ -= zn_poly = - -== Description == - -zn_poly is a C library for polynomial arithmetic in Z/nZ[x], where n is any -modulus that fits into an unsigned long. - -Website: https://gitlab.com/sagemath/zn_poly - -Note: Original website is at http://cims.nyu.edu/~harvey/zn_poly/ but is no -longer maintained. Sage maintains an "official" continuation of the project -at the above link. - -== License == - -GPL V2 or V3. Some of the code has been copied from other projects - see -the file src/COPYING for details. - -== Upstream Contact == - - * David Harvey - * E. M. Bray - -== Dependencies == - - * GMP/MPIR - * (some) Python (to create the Makefile) - * GNU patch - * NTL apparently only if we configured zn_poly differently (same for FLINT) - -== Special Update/Build Instructions == - - * Make sure the patches still apply. - Especially changes in `makemakefile.py` may also require changes to - `spkg-install` (and perhaps also `spkg-check`). - * There's also a `--use-flint` option to `configure`; no idea what it does, - and we currently don't use it either. - * TODO: - - Use `make install` instead of manually "installing" (copying and sym- - linking) the [shared] libraries and header files. This requires further - tweaking of `makemakefile.py`, since it currently only installs a static - library and the headers. - - If everything's fine, i.e., no problems arise, some comments and especial- - ly some code I currently just commented out can certainly be removed. - (-leif, 04/2012) - * The version number "0.9.p11" is used as a doctest in the function - package_versions in sage/misc/packages.py, so if this package gets - upgraded, that doctest needs to be changed. - -=== Patches === - - * All patches from Sage have been merged into upstream. These include: - * makemakefile.py.patch: - Improves the Python script creating the Makeefile for better use at - least within Sage; see patch for details. (Last modified at #12433, - which added and changed a lot.) - * profiler.c.patch, zn_poly.h.patch: - Fix potential redefinition of `ulong` (in combination with other - headers). - * mpn_mulmid-tune.c.patch, mulmid-tune.c.patch, mul-tune.c.patch: - Fix "jump into scope of identifier with variably modified type" - errors. (See #8771). - * mpn_mulmid-test.c.patch: - Fix a potential problem when the value of ZNP_mpn_smp_kara_thresh is - SIZE_MAX, this is usually irrealistic but can happen at least on - linux on power7 with gcc-4.7.1 (see #14098). - * fix_fudge_factor_in_nuss-test.c.patch: - As the name says; fix provided by upstream (David Harvey); see - #13947. - -== Changelog == - -=== zn_poly-0.9.1 === - * Update to new upstream sources. - -=== zn_poly-0.9.p11 (Leif Leonhardy, May 24th, 2013) === - * #13947: Fix `nuss_mul()` test failing especially if tuning happened - under "heavy" load (at least on MacOS X and Cygwin) - Add `fix_fudge_factor_in_nuss-test.c.patch`; fix suggested by David - Harvey. - -=== zn_poly-0.9.p10 ( Francois Bissey, 14 February 2013 ) === - * Trac #14098: Fix a potential problem with the tests where values can get out of - range. The problem occurs only in rare case but the code is more sane that way. - The fix was gracously provided by the original zn_poly author. - -=== zn_poly-0.9.p9 (Jeroen Demeyer, 28 May 2012) === - * Trac #12751: remove the gcc-4.7.0 workaround for ia64 since this bug - has been fixed in gcc-4.7.1 and we will not fully support building - Sage with gcc-4.7.0 on Itanium. - * Don't override user-set CFLAGS and CXXFLAGS. - -=== zn_poly-0.9.p8 (Leif Leonhardy, April 20th, 2012) === - * #12433: Further reviewer changes / additions. - * Work around GCC 4.7.0 bug on ia64 (by almost completely disabling optimi- - zation if the compiler is GCC 4.7.x, of course only on that platform); cf. - #12751, #12765. - * Add a patch to avoid conflicting definitions (i.e., redundant `typedefs`) - of `ulong`, by changing `ulong` to a macro. - * Don't hardcode the zn_poly version number in `spkg-install`; instead read - it from the file `VERSION`, like the generated `makefile` does. - * Remove some of the code previously just commented out, as well as some - obsolete comments. - * Document patches (and correct ticket reference in an old changelog entry). - -=== zn_poly-0.9.p7 (Leif Leonhardy, April 8th, 2012) === - * #12433: Reviewer changes. - * Restore upstream sources. (One file in `src/tune/` was already patched.) - * Remove the obsolete Debian `dist/` directory. - * Use `patch` to apply the patches. - * Remove `patches/` from `.hgignore`! (And remove the prepatched files.) - * Add Python to the dependencies, since (some) Python is needed to create - the Makefile during build / `configure`. (`spkg/standard/deps` already - reflects this.) - * Rework (upstream's) `makemakefile.py` to create a proper Makefile, - respecting `CC`, `CXX`, `CFLAGS`, `CXXFLAGS`, `CPPFLAGS` etc. with their - *usual* meaning (i.e., not using `CPP` to compile C++!), and using `LDFLAGS` - consistently, also not hardcoding e.g. `-m64` (which was added by Sage). - * Do not add `-O3` to `CFLAGS` (in `spkg-install`) without the possibility to - get overridden by user-provided `CFLAGS`. Also honor `SAGE_DEBUG=yes` by - completely disabling optimization in that case. - * Fix typo in `spkg-check`, which certainly would break building the test - program when `SAGE64=yes`. (Although it is actually already built from - within `spkg-install`.) - * Clean up `spkg-install` and `spkg-check`; redirect error messages to - `stderr`, add more error checks, use `$MAKE` in `spkg-check` as well, - quote more environment variables, use `cp [-f]` instead of `$CP`, don't - create an absolute symbolic link on Cygwin. - -=== zn_poly-0.9.p6 (R. Andrew Ohana, February 4th, 2012) === - * #12433: Make spkg respect global CC flag - -=== zn_poly-0.9.p5 (David Kirkby, July 14th, 2010) === - * #9358 Ensure that spkg-install can handle the case - of where SAGE64=yes and Solaris with a Sun linker. Previously - this worked properly if SAGE64 was yes, OR if the operating system - was Solaris with the sun linker. But spkg-install failed - to work properly if both SAGE64=yes AND the operating system - was Solaris with the Sun linker. - * No longer run 'make check' from spkg-check. Since this was quick to run - it has already been run from spkg-install, so it was pointless running - it from spkg-check too. Instead a much more comprehensive test suite is - run, using - $ test/test all - * Change the dependancy from GMP to MPIR in this SPKG.txt file, as Sage no - longer uses GMP. - -=== zn_poly-0.9.p4 (Willem Jan Palenstijn, William Stein, April 26th, 2010) === - * Ticket #8771: Fix build error on gcc 4.5, and check if make tune succeeded. - * Added more checks for errors in spkg-install. - -=== zn_poly-0.9.p3 (Jaap Spies, February 21th, 2010) === - * Ticket #8178: if SAGE64=yes we set CFLAGS appropriate. In patches/makemakefile.py we add $(LDFLAGS) - when we build the shared library. This works for Darwin and Open Solaris. - - -=== zn_poly-0.9.p2 (Mike Hansen, February 15th, 2010) === - * Ticket #8280: cygwin: zn_poly shared library named incorrectly on cygwin - -=== zn_poly-0.9.p1 (David Kirkby, June 29th, 2009) === - * Ticket #6443 A GNUism in zn_poly-0.9.p0 causes linking problems with Sun's linker - This was an easy fix - just substitue -soname for -h in src/makemakefile.py - I did this with a sed script in spkg-install - -=== zn_poly-0.9.p0 (Michael Abshoff, September 26th, 2008) === - * remerge OSX 64 bit fixes - * clean up spkg-install - -=== zn_poly-0.9 (David Harvey and Timothy Abbott, September 19th, 2008) === - * updated to zn_poly-0.9 - * shared versioning filename issues - * re-enabled test suite (now only takes ~10 seconds) - * updated upstream URL - -=== zn_poly-0.8.p2 (Michael Abshoff, August 19th, 2008) === - * Add 64 bit OSX build support - -=== zn_poly-0.8.p1 (Michael Abshoff, June 9th, 2008) === - * Do not run the test suite per default any more (#3386) - -=== zn_poly-0.8.p0 (David Harvey, April 4th, 2008) === - * updated to zn_poly-0.8 (fix minor bugs and memleaks) - * updated SPKG.txt - -=== zn_poly-0.8 (David Harvey, April 2nd, 2008) === - * updated to zn_poly-0.8.alpha0 - * updated SPKG.txt - * changed spkg-install to run tuning program, test suite - -=== zn_poly-0.4.1.p0 (Michael Abshoff, March 19th, 2008) === - * updated SPKG.txt - * add hg repo, hg ignore - * cleaned up spkg-install - -=== zn_poly-0.4.1 (David Harvey, Dec. 18th, 2007) === - * created spkg from zn_poly-0.4.1 diff --git a/build/pkgs/zn_poly/checksums.ini b/build/pkgs/zn_poly/checksums.ini index 157c596bd45..25cdb79c3b3 100644 --- a/build/pkgs/zn_poly/checksums.ini +++ b/build/pkgs/zn_poly/checksums.ini @@ -1,4 +1,4 @@ tarball=zn_poly-VERSION.tar.gz -sha1=13639b65d43c14809c3c74a22a12c0e458913957 -md5=2c123368da39d4bf4b8a988e9eed13e2 -cksum=1598413125 +sha1=ac13121e8ba03f12edd22b0a2e0ae3b3a79fc26e +md5=81f09badfe4f941159df70805681d9eb +cksum=2478176718 diff --git a/build/pkgs/zn_poly/distros/conda.txt b/build/pkgs/zn_poly/distros/conda.txt new file mode 100644 index 00000000000..5ff8997d672 --- /dev/null +++ b/build/pkgs/zn_poly/distros/conda.txt @@ -0,0 +1 @@ +zn_poly diff --git a/build/pkgs/zn_poly/distros/debian.txt b/build/pkgs/zn_poly/distros/debian.txt new file mode 100644 index 00000000000..f155e4856dd --- /dev/null +++ b/build/pkgs/zn_poly/distros/debian.txt @@ -0,0 +1 @@ +libzn-poly-dev diff --git a/build/pkgs/zn_poly/distros/fedora.txt b/build/pkgs/zn_poly/distros/fedora.txt new file mode 100644 index 00000000000..631dd81dfe1 --- /dev/null +++ b/build/pkgs/zn_poly/distros/fedora.txt @@ -0,0 +1 @@ +zn_poly zn_poly-devel diff --git a/build/pkgs/zn_poly/distros/opensuse.txt b/build/pkgs/zn_poly/distros/opensuse.txt new file mode 100644 index 00000000000..5ff8997d672 --- /dev/null +++ b/build/pkgs/zn_poly/distros/opensuse.txt @@ -0,0 +1 @@ +zn_poly diff --git a/build/pkgs/zn_poly/package-version.txt b/build/pkgs/zn_poly/package-version.txt index 2305b1d26e3..2003b639c40 100644 --- a/build/pkgs/zn_poly/package-version.txt +++ b/build/pkgs/zn_poly/package-version.txt @@ -1 +1 @@ -0.9.1.p0 +0.9.2 diff --git a/build/pkgs/zn_poly/patches/python3.patch b/build/pkgs/zn_poly/patches/python3.patch deleted file mode 100644 index b61d2014c97..00000000000 --- a/build/pkgs/zn_poly/patches/python3.patch +++ /dev/null @@ -1,433 +0,0 @@ -commit f2bada31fd09f09c744d8ff0e1fab82c53dbf830 -Author: Jeroen Demeyer -Date: Tue Feb 12 15:08:39 2019 +0100 - - Make makemakefile.py compatible with Python 3 - -diff --git a/makemakefile.py b/makemakefile.py -index fa2b851..acb6084 100644 ---- a/makemakefile.py -+++ b/makemakefile.py -@@ -147,213 +147,221 @@ cpp_libs = libs + " -L" + ntl_lib_dir + " -lntl" - - import time - --print "#" --print "# Do not edit directly -- this file was auto-generated" --print "# by makemakefile.py on " + time.strftime("%a, %d %b %Y %H:%M:%S +0000", -- time.gmtime()) --print "#" --print "# (makemakefile.py patched for Sage, 04/2012)" --print -- --print --print "CC ?= gcc" --print "CPP ?= cpp" --print "CFLAGS = " + cflags --print "CPPFLAGS = " + cppflags --print "LDFLAGS = " + ldflags --print "INCLUDES = " + includes # These are options to the C preprocessor. --print "LIBS = " + libs # These are linker options passed to the compiler. --print --print "AR ?= ar" --print "RANLIB ?= ranlib" --print --print "SHARED_FLAG ?= -shared" --print "SONAME_FLAG ?= -soname" # '-h' for the Sun/Solaris linker -- --print --print "CXX ?= g++" # The C++ compiler. --print "CXXFLAGS = " + cxxflags # Options passed to the C++ compiler. --print "CPP_INCLUDES = " + cpp_includes --print "CPP_LIBS = " + cpp_libs -- --print --print "HEADERS = " + " ".join(install_headers + other_headers) --print "LIBOBJS = " + " ".join([x + ".o" for x in lib_modules]) --print "TESTOBJS = " + " ".join([x + "-DEBUG.o" for x in -- lib_modules + test_modules + testprof_modules]) --print "PROFOBJS = " + " ".join([x + ".o" for x in -- lib_modules + prof_modules + noncpp_prof_modules + testprof_modules]) --print "CPP_PROFOBJS = " + " ".join([x + ".o" for x in -- lib_modules + prof_modules + cpp_prof_modules + testprof_modules]) --print "TUNEOBJS = " + " ".join([x + ".o" for x in -- lib_modules + tune_modules + testprof_modules + prof_modules + -- noncpp_prof_modules if x not in ("src/tuning", "profile/prof_main")]) --print "ZN_POLY_TUNING = " + str(int(zn_poly_tuning)) --print "ZN_POLY_VERSION = " + version --print "ZN_POLY_ABI_VERSION = " + abi_version -- --print --print "all: libzn_poly.a" --print --print "test: test/test" --print "tune: tune/tune" --print --print "check: test" --print "\ttest/test -quick all" --print --print "install:" --print "\tmkdir -p %s/include/zn_poly" % prefix --print "\tmkdir -p %s/lib" % prefix --print "\tcp libzn_poly.a %s/lib" % prefix --print "\tcp include/zn_poly.h %s/include/zn_poly" % prefix --print "\tcp include/wide_arith.h %s/include/zn_poly" % prefix --print --print "clean:" --print "\trm -f *.o" --print "\trm -f test/*.o" --print "\trm -f profile/*.o" --print "\trm -f tune/*.o" --print "\trm -f src/tuning.c" --print "\trm -f src/*.o" --print "\trm -f demo/bernoulli/*.o" --print "\trm -f libzn_poly.a" --print "\trm -f libzn_poly.dylib" --print "\trm -f libzn_poly*.so*" --print "\trm -f libzn_poly*.dll.a" --print "\trm -f cygzn_poly.dll" --print "\trm -f test/test" --print "\trm -f tune/tune" -+now = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) -+print( -+"""# -+# Do not edit directly -- this file was auto-generated -+# by {} on {} -+# -+# (makemakefile.py patched for Sage, 04/2012) -+""".format(__file__, now)) -+ -+ -+print( -+""" -+CC ?= gcc -+CPP ?= cpp -+CFLAGS = {} -+CPPFLAGS = {} -+LDFLAGS = {} -+INCLUDES = {} # These are options to the C preprocessor. -+LIBS = {} # These are linker options passed to the compiler. -+ -+AR ?= ar -+RANLIB ?= ranlib -+ -+SHARED_FLAG ?= -shared -+SONAME_FLAG ?= -soname # '-h' for the Sun/Solaris linker -+""".format(cflags, cppflags, ldflags, includes, libs)) -+ -+ -+print( -+"""CXX ?= g++ # The C++ compiler. -+CXXFLAGS = {} # Options passed to the C++ compiler. -+CPP_INCLUDES = {} -+CPP_LIBS = {} -+""".format(cxxflags, cpp_includes, cpp_libs)) -+ -+ -+print( -+"""HEADERS = {} -+LIBOBJS = {} -+TESTOBJS = {} -+PROFOBJS = {} -+CPP_PROFOBJS = {} -+TUNEOBJS = {}""".format( -+ " ".join(install_headers + other_headers), -+ " ".join([x + ".o" for x in lib_modules]), -+ " ".join([x + "-DEBUG.o" for x in -+ lib_modules + test_modules + testprof_modules]), -+ " ".join([x + ".o" for x in -+ lib_modules + prof_modules + noncpp_prof_modules + testprof_modules]), -+ " ".join([x + ".o" for x in -+ lib_modules + prof_modules + cpp_prof_modules + testprof_modules]), -+ " ".join([x + ".o" for x in -+ lib_modules + tune_modules + testprof_modules + prof_modules + -+ noncpp_prof_modules if x not in ("src/tuning", "profile/prof_main")]) -+)) -+ -+ -+print( -+"""ZN_POLY_TUNING = {} -+ZN_POLY_VERSION = {} -+ZN_POLY_ABI_VERSION = {} -+""".format(int(zn_poly_tuning), version, abi_version)) -+ -+ -+print( -+"""all: libzn_poly.a -+ -+test: test/test -+tune: tune/tune -+ -+check: test -+\ttest/test -quick all -+ -+install: -+\tmkdir -p {prefix}/include/zn_poly -+\tmkdir -p {prefix}/lib -+\tcp libzn_poly.a {prefix}/lib -+\tcp include/zn_poly.h {prefix}/include/zn_poly -+\tcp include/wide_arith.h {prefix}/include/zn_poly -+""".format(prefix=prefix)) -+ -+ -+print( -+"""clean: -+\trm -f *.o -+\trm -f test/*.o -+\trm -f profile/*.o -+\trm -f tune/*.o -+\trm -f src/tuning.c -+\trm -f src/*.o -+\trm -f demo/bernoulli/*.o -+\trm -f libzn_poly.a -+\trm -f libzn_poly.dylib -+\trm -f libzn_poly*.so* -+\trm -f libzn_poly*.dll.a -+\trm -f cygzn_poly.dll -+\trm -f test/test -+\trm -f tune/tune""") - for x in prof_progs: -- print "\trm -f " + x -- print "\trm -f " + x + "-ntl" -+ print("\trm -f " + x) -+ print("\trm -f " + x + "-ntl") - for x in demo_progs: -- print "\trm -f " + x --print --print "distclean: clean" --print "\trm -f makefile" --print --print "dist: distclean" --print "\ttar --exclude-vcs --exclude=.gitignore -czf zn_poly-$(ZN_POLY_VERSION).tar.gz *" -- -- --print --print --print "##### library targets" --print --print "ifeq ($(ZN_POLY_TUNING), 1)" --print "src/tuning.c: tune/tune" --print "\ttune/tune > src/tuning.c" --print "else" --print "src/tuning.c: tune/tuning.c" --print "\tcp tune/tuning.c src/tuning.c" --print "endif" --print --print "libzn_poly.a: $(LIBOBJS)" --print "\t$(AR) -r libzn_poly.a $(LIBOBJS)" --print "\t$(RANLIB) libzn_poly.a" --print --print "# TODO: Put '-single_module -fPIC -dynamiclib' into $(SHARED_FLAG)" --print "# and use that; also support $(SO_EXTENSION)..." --print "libzn_poly.dylib: $(LIBOBJS)" --print "\t$(CC) $(LDFLAGS) -single_module -fPIC -dynamiclib -o libzn_poly.dylib " \ -- "$(LIBOBJS) $(LIBS)" --print --print "# Left for compatibility with previous versions of Sage's 'spkg-install':" --print "libzn_poly.dylib64: $(LIBOBJS)" --print "\t$(CC) -m64 -single_module -fPIC -dynamiclib -o libzn_poly.dylib $(LIBOBJS) $(LIBS)" --print --print "cygzn_poly.dll: $(LIBOBJS)" --print "\t$(CC) $(SHARED_FLAG) $(LDFLAGS) " \ -- "-Wl,--out-implib,libzn_poly-$(ZN_POLY_VERSION).dll.a " \ -- "-o cygzn_poly.dll $(LIBOBJS) $(LIBS)" --print --print "libzn_poly-$(ZN_POLY_VERSION).dll.a: cygzn_poly.dll" --print --print "libzn_poly.dll.a: libzn_poly-$(ZN_POLY_VERSION).dll.a" --print "\tln -sf libzn_poly-$(ZN_POLY_VERSION).dll.a libzn_poly.dll.a" --print "\tln -sf libzn_poly-$(ZN_POLY_VERSION).dll.a libzn_poly-$(ZN_POLY_ABI_VERSION).dll.a" --print --print "libzn_poly.so: libzn_poly-$(ZN_POLY_VERSION).so" --print "\tln -sf libzn_poly-$(ZN_POLY_VERSION).so libzn_poly.so" --print "\tln -sf libzn_poly-$(ZN_POLY_VERSION).so libzn_poly-$(ZN_POLY_ABI_VERSION).so" -- --print --print "libzn_poly-$(ZN_POLY_VERSION).so: $(LIBOBJS)" --print "\t$(CC) $(SHARED_FLAG) $(LDFLAGS) -Wl,-soname,libzn_poly-$(ZN_POLY_ABI_VERSION).so " \ -- "-o libzn_poly-$(ZN_POLY_VERSION).so $(LIBOBJS) $(LIBS)" -- --print --print --print "##### test program" --print --print "test/test: $(TESTOBJS) $(HEADERS)" --print "\t$(CC) -g $(LDFLAGS) -o test/test $(TESTOBJS) $(LIBS)" -- --print --print --print "##### profiling programs" --print -+ print("\trm -f " + x) -+print( -+""" -+distclean: clean -+\trm -f makefile -+ -+dist: distclean -+\ttar --exclude-vcs --exclude=.gitignore -czf zn_poly-$(ZN_POLY_VERSION).tar.gz * -+ -+ -+##### library targets -+ -+ifeq ($(ZN_POLY_TUNING), 1) -+src/tuning.c: tune/tune -+\ttune/tune > src/tuning.c -+else -+src/tuning.c: tune/tuning.c -+\tcp tune/tuning.c src/tuning.c -+endif -+ -+libzn_poly.a: $(LIBOBJS) -+\t$(AR) -r libzn_poly.a $(LIBOBJS) -+\t$(RANLIB) libzn_poly.a -+ -+# TODO: Put '-single_module -fPIC -dynamiclib' into $(SHARED_FLAG) -+# and use that; also support $(SO_EXTENSION)... -+libzn_poly.dylib: $(LIBOBJS) -+\t$(CC) $(LDFLAGS) -single_module -fPIC -dynamiclib -o libzn_poly.dylib $(LIBOBJS) $(LIBS) -+ -+# Left for compatibility with previous versions of Sage's 'spkg-install': -+libzn_poly.dylib64: $(LIBOBJS) -+\t$(CC) -m64 -single_module -fPIC -dynamiclib -o libzn_poly.dylib $(LIBOBJS) $(LIBS) -+ -+cygzn_poly.dll: $(LIBOBJS) -+\t$(CC) $(SHARED_FLAG) $(LDFLAGS) -Wl,--out-implib,libzn_poly-$(ZN_POLY_VERSION).dll.a -o cygzn_poly.dll $(LIBOBJS) $(LIBS) -+ -+libzn_poly-$(ZN_POLY_VERSION).dll.a: cygzn_poly.dll -+ -+libzn_poly.dll.a: libzn_poly-$(ZN_POLY_VERSION).dll.a -+\tln -sf libzn_poly-$(ZN_POLY_VERSION).dll.a libzn_poly.dll.a -+\tln -sf libzn_poly-$(ZN_POLY_VERSION).dll.a libzn_poly-$(ZN_POLY_ABI_VERSION).dll.a -+ -+libzn_poly.so: libzn_poly-$(ZN_POLY_VERSION).so -+\tln -sf libzn_poly-$(ZN_POLY_VERSION).so libzn_poly.so -+\tln -sf libzn_poly-$(ZN_POLY_VERSION).so libzn_poly-$(ZN_POLY_ABI_VERSION).so -+ -+libzn_poly-$(ZN_POLY_VERSION).so: $(LIBOBJS) -+\t$(CC) $(SHARED_FLAG) $(LDFLAGS) -Wl,-soname,libzn_poly-$(ZN_POLY_ABI_VERSION).so -o libzn_poly-$(ZN_POLY_VERSION).so $(LIBOBJS) $(LIBS) -+ -+ -+##### test program -+ -+test/test: $(TESTOBJS) $(HEADERS) -+\t$(CC) -g $(LDFLAGS) -o test/test $(TESTOBJS) $(LIBS) -+ -+ -+##### profiling programs -+""") -+ - for x in prof_progs: -- print "%s-main.o: %s-main.c $(HEADERS)" % (x, x) -- print "\t$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -DNDEBUG -o %s-main.o -c %s-main.c" \ -- % (x, x) -- print -- print "%s: %s-main.o $(PROFOBJS)" % (x, x) -- print "\t$(CC) $(CFLAGS) $(LDFLAGS) -o %s %s-main.o $(PROFOBJS) $(LIBS)" \ -- % (x, x) -- print -- print "%s-main-ntl.o: %s-main.c $(HEADERS)" % (x, x) -- print "\t$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -DPROFILE_NTL -DNDEBUG " \ -- "-o %s-main-ntl.o -c %s-main.c" % (x, x) -- print -- print "%s-ntl: %s-main-ntl.o $(CPP_PROFOBJS)" % (x, x) -- print "\t$(CXX) $(CXXFLAGS) $(LDFLAGS) -o %s-ntl %s-main-ntl.o " \ -- "$(CPP_PROFOBJS) $(CPP_LIBS)" % (x, x) -- print -- --print --print --print "##### tuning utility" --print --print "tune/tune: $(TUNEOBJS)" --print "\t$(CC) $(CFLAGS) $(LDFLAGS) -o tune/tune $(TUNEOBJS) $(LIBS)" -- -- --print --print --print "##### demo programs" -+ print( -+"""{0}-main.o: {0}-main.c $(HEADERS) -+\t$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -DNDEBUG -o {0}-main.o -c {0}-main.c -+ -+{0}: {0}-main.o $(PROFOBJS) -+\t$(CC) $(CFLAGS) $(LDFLAGS) -o {0} {0}-main.o $(PROFOBJS) $(LIBS) -+ -+{0}-main-ntl.o: {0}-main.c $(HEADERS) -+\t$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -DPROFILE_NTL -DNDEBUG -o {0}-main-ntl.o -c {0}-main.c -+ -+{0}-ntl: {0}-main-ntl.o $(CPP_PROFOBJS) -+\t$(CXX) $(CXXFLAGS) $(LDFLAGS) -o {0}-ntl {0}-main-ntl.o $(CPP_PROFOBJS) $(CPP_LIBS) -+""".format(x)) -+ -+ -+print( -+""" -+ -+##### tuning utility -+ -+tune/tune: $(TUNEOBJS) -+\t$(CC) $(CFLAGS) $(LDFLAGS) -o tune/tune $(TUNEOBJS) $(LIBS) -+ -+ -+##### demo programs -+""") - for x in demo_progs: -- print -- print "%s: %s.o $(LIBOBJS)" % (x, x) -- print "\t$(CC) $(CFLAGS) $(LDFLAGS) -o %s %s.o $(LIBOBJS) $(LIBS)" % (x, x) -+ print( -+"""{0}: {0}.o $(LIBOBJS) -+\t$(CC) $(CFLAGS) $(LDFLAGS) -o {0} {0}.o $(LIBOBJS) $(LIBS) -+""".format(x)) - - --print --print --print "##### object files (with debug code)" -+print("\n##### object files (with debug code)\n") - for x in lib_modules + test_modules + testprof_modules + demo_progs: -- print -- print "%s-DEBUG.o: %s.c $(HEADERS)" % (x, x) -- print "\t$(CC) -g $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -DDEBUG -o %s-DEBUG.o -c %s.c" \ -- % (x, x) -- --print --print --print "##### object files (no debug code)" --for x in lib_modules + prof_modules + testprof_modules + \ -- tune_modules + demo_progs: -- print -- print "%s.o: %s.c $(HEADERS)" % (x, x) -- print "\t$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -DNDEBUG -o %s.o -c %s.c" % (x, x) -- --print --print --print "##### object files (C++, no debug code)" --for x in cpp_prof_modules: -- print -- print "%s.o: %s.c $(HEADERS)" % (x, x) -- print "\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(CPP_INCLUDES) -DNDEBUG -o %s.o -c %s.c" \ -- % (x, x) -+ print( -+"""{0}-DEBUG.o: {0}.c $(HEADERS) -+\t$(CC) -g $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -DDEBUG -o {0}-DEBUG.o -c {0}.c -+""".format(x)) -+ - -+print("\n##### object files (no debug code)\n") -+for x in (lib_modules + prof_modules + testprof_modules + -+ tune_modules + demo_progs): -+ print( -+"""{0}.o: {0}.c $(HEADERS) -+\t$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -DNDEBUG -o {0}.o -c {0}.c -+""".format(x)) - --### end of file -+ -+print("\n##### object files (C++, no debug code)\n") -+for x in cpp_prof_modules: -+ print( -+"""{0}.o: {0}.c $(HEADERS) -+\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(CPP_INCLUDES) -DNDEBUG -o {0}.o -c {0}.c -+""".format(x)) diff --git a/build/pkgs/zn_poly/patches/python_2_6_format_support.patch b/build/pkgs/zn_poly/patches/python_2_6_format_support.patch deleted file mode 100644 index f8a59665487..00000000000 --- a/build/pkgs/zn_poly/patches/python_2_6_format_support.patch +++ /dev/null @@ -1,72 +0,0 @@ -diff --git a/makemakefile.py b/makemakefile.py -index acb6084..9c0fb96 100644 ---- a/makemakefile.py -+++ b/makemakefile.py -@@ -151,7 +151,7 @@ now = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) - print( - """# - # Do not edit directly -- this file was auto-generated --# by {} on {} -+# by {0} on {1} - # - # (makemakefile.py patched for Sage, 04/2012) - """.format(__file__, now)) -@@ -161,11 +161,11 @@ print( - """ - CC ?= gcc - CPP ?= cpp --CFLAGS = {} --CPPFLAGS = {} --LDFLAGS = {} --INCLUDES = {} # These are options to the C preprocessor. --LIBS = {} # These are linker options passed to the compiler. -+CFLAGS = {0} -+CPPFLAGS = {1} -+LDFLAGS = {2} -+INCLUDES = {3} # These are options to the C preprocessor. -+LIBS = {4} # These are linker options passed to the compiler. - - AR ?= ar - RANLIB ?= ranlib -@@ -177,19 +177,19 @@ SONAME_FLAG ?= -soname # '-h' for the Sun/Solaris linker - - print( - """CXX ?= g++ # The C++ compiler. --CXXFLAGS = {} # Options passed to the C++ compiler. --CPP_INCLUDES = {} --CPP_LIBS = {} -+CXXFLAGS = {0} # Options passed to the C++ compiler. -+CPP_INCLUDES = {1} -+CPP_LIBS = {2} - """.format(cxxflags, cpp_includes, cpp_libs)) - - - print( --"""HEADERS = {} --LIBOBJS = {} --TESTOBJS = {} --PROFOBJS = {} --CPP_PROFOBJS = {} --TUNEOBJS = {}""".format( -+"""HEADERS = {0} -+LIBOBJS = {1} -+TESTOBJS = {2} -+PROFOBJS = {3} -+CPP_PROFOBJS = {4} -+TUNEOBJS = {5}""".format( - " ".join(install_headers + other_headers), - " ".join([x + ".o" for x in lib_modules]), - " ".join([x + "-DEBUG.o" for x in -@@ -205,9 +205,9 @@ TUNEOBJS = {}""".format( - - - print( --"""ZN_POLY_TUNING = {} --ZN_POLY_VERSION = {} --ZN_POLY_ABI_VERSION = {} -+"""ZN_POLY_TUNING = {0} -+ZN_POLY_VERSION = {1} -+ZN_POLY_ABI_VERSION = {2} - """.format(int(zn_poly_tuning), version, abi_version)) - - diff --git a/build/pkgs/zn_poly/spkg-check b/build/pkgs/zn_poly/spkg-check deleted file mode 100644 index 0ff73cbd81b..00000000000 --- a/build/pkgs/zn_poly/spkg-check +++ /dev/null @@ -1,102 +0,0 @@ -############################################################################### -# -# zn_poly Sage check script -# -############################################################################### - -if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "Error: SAGE_LOCAL undefined - exiting..." - echo >&2 "Maybe run 'sage -sh'?" - exit 1 -fi - -############################################################################### -# Set up environment variables: -############################################################################### - -if [ "$SAGE_DEBUG" = yes ]; then - echo >&2 "Warning: Setting SAGE_DEBUG to 'yes' completely disables optimization." - CFLAGS="$CFLAGS -O0 -g -fPIC" - CXXFLAGS="$CXXFLAGS -O0 -g -fPIC" -else - CFLAGS="-O3 -g $CFLAGS -fPIC" - CXXFLAGS="-O3 -g $CXXFLAGS -fPIC" -fi - -# Work around a bug in GCC 4.7.0 which breaks the build on Itanium CPUs with -# '-O3', '-O2' and '-O1' (cf. #12765, #12751, and the bug URL below.) -if [ "`uname -m`" = ia64 ] && [ "`testcc.sh $CC`" = GCC ]; then - gcc_version=`$CC -dumpversion` - case "$gcc_version" in - 4.7.*) - CFLAGS="$CFLAGS -O0 -finline-functions -fschedule-insns" - CXXFLAGS="$CXXFLAGS -O0 -finline-functions -fschedule-insns" - echo >&2 "Warning: Disabling almost all optimization due to a bug in (at least)" - echo >&2 " GCC 4.7.0 on Itanium, which otherwise would break the build." - echo >&2 " See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48496" - echo >&2 " for current status and further details." - echo >&2 " (And please report to e.g. sage-devel in case you feel this bug" - echo >&2 " should already be fixed in GCC $gcc_version.)" - esac -fi - -export CFLAGS CPPFLAGS CXXFLAGS LDFLAGS # Partially redundant, but safe. - -# Actually, these flags have been written to the Makefile during 'configure'. -# Only CC, CPP and CXX settings from the environment currently override the -# ones in the Makefile (which was generated from a patched 'makemakefile.py'). -# -leif 04/2012 - -case "$UNAME" in - SunOS) - if ! (ld --version 2>&1 | grep GNU >/dev/null); then - # Assume it's the Sun linker; change '-soname' to '-h': - # The following is only supported by the Makefile generated by our - # patched 'makemakefile.py': - export SONAME_FLAG=-h - fi;; - *) unset SONAME_FLAG # Leave the Makefile default; for safety. -esac - -unset SHARED_FLAG # Currently leave the Makefile default ('-shared'); for safety. - -############################################################################### -# Build the 'test' program (if it's not already built) and run it: -############################################################################### - -cd src/ - -# The methods for testing zn_poly are more complex than those of most other -# packages. A 'make check' does some quick tests. These are run from -# 'spkg-install' to check zn_poly is not obviously disfunctional. -# -# However, a more comprehensive set of tests can be run by first building a -# test program, then running that test program, which we do here. -# -# To quote from the file src/README: -# -# make check -# Runs some quick tests to make sure that everything appears to be working. -# -# make test -# Builds a test program. Running "test/test all" runs the whole zn_poly test -# suite, which tests zn_poly much more thoroughly than "make check". - -echo -echo "Now building zn_poly's extensive test suite (if not already built)..." -# $MAKE test CC="$CC" CXX="$CXX" # See comment above. We don't have to pass these. -$MAKE test # Make sure the test program is built. -if [ $? -ne 0 ] || [ ! -x test/test ]; then - echo >&2 "Error: zn_poly failed to build its 'test' program," - echo >&2 " so zn_poly's extensive test suite cannot be run." - exit 1 -fi - -echo -echo "Now running zn_poly's extensive test suite..." -test/test all # Run the extensive test suite. -if [ $? -ne 0 ]; then - echo >&2 "Error: zn_poly failed to pass its extensive test suite." - exit 1 -fi -echo "zn_poly has passed its extensive test suite." diff --git a/build/pkgs/zn_poly/spkg-check.in b/build/pkgs/zn_poly/spkg-check.in new file mode 100644 index 00000000000..299ac7a6b8a --- /dev/null +++ b/build/pkgs/zn_poly/spkg-check.in @@ -0,0 +1,96 @@ +############################################################################### +# +# zn_poly Sage check script +# +############################################################################### + +############################################################################### +# Set up environment variables: +############################################################################### + +if [ "$SAGE_DEBUG" = yes ]; then + echo >&2 "Warning: Setting SAGE_DEBUG to 'yes' completely disables optimization." + CFLAGS="$CFLAGS -O0 -g -fPIC" + CXXFLAGS="$CXXFLAGS -O0 -g -fPIC" +else + CFLAGS="-O3 -g $CFLAGS -fPIC" + CXXFLAGS="-O3 -g $CXXFLAGS -fPIC" +fi + +# Work around a bug in GCC 4.7.0 which breaks the build on Itanium CPUs with +# '-O3', '-O2' and '-O1' (cf. #12765, #12751, and the bug URL below.) +if [ "`uname -m`" = ia64 ] && [ "`testcc.sh $CC`" = GCC ]; then + gcc_version=`$CC -dumpversion` + case "$gcc_version" in + 4.7.*) + CFLAGS="$CFLAGS -O0 -finline-functions -fschedule-insns" + CXXFLAGS="$CXXFLAGS -O0 -finline-functions -fschedule-insns" + echo >&2 "Warning: Disabling almost all optimization due to a bug in (at least)" + echo >&2 " GCC 4.7.0 on Itanium, which otherwise would break the build." + echo >&2 " See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48496" + echo >&2 " for current status and further details." + echo >&2 " (And please report to e.g. sage-devel in case you feel this bug" + echo >&2 " should already be fixed in GCC $gcc_version.)" + esac +fi + +export CFLAGS CPPFLAGS CXXFLAGS LDFLAGS # Partially redundant, but safe. + +# Actually, these flags have been written to the Makefile during 'configure'. +# Only CC, CPP and CXX settings from the environment currently override the +# ones in the Makefile (which was generated from a patched 'makemakefile.py'). +# -leif 04/2012 + +case "$UNAME" in + SunOS) + if ! (ld --version 2>&1 | grep GNU >/dev/null); then + # Assume it's the Sun linker; change '-soname' to '-h': + # The following is only supported by the Makefile generated by our + # patched 'makemakefile.py': + export SONAME_FLAG=-h + fi;; + *) unset SONAME_FLAG # Leave the Makefile default; for safety. +esac + +unset SHARED_FLAG # Currently leave the Makefile default ('-shared'); for safety. + +############################################################################### +# Build the 'test' program (if it's not already built) and run it: +############################################################################### + +cd src/ + +# The methods for testing zn_poly are more complex than those of most other +# packages. A 'make check' does some quick tests. These are run from +# 'spkg-install' to check zn_poly is not obviously disfunctional. +# +# However, a more comprehensive set of tests can be run by first building a +# test program, then running that test program, which we do here. +# +# To quote from the file src/README: +# +# make check +# Runs some quick tests to make sure that everything appears to be working. +# +# make test +# Builds a test program. Running "test/test all" runs the whole zn_poly test +# suite, which tests zn_poly much more thoroughly than "make check". + +echo +echo "Now building zn_poly's extensive test suite (if not already built)..." +# $MAKE test CC="$CC" CXX="$CXX" # See comment above. We don't have to pass these. +$MAKE test # Make sure the test program is built. +if [ $? -ne 0 ] || [ ! -x test/test ]; then + echo >&2 "Error: zn_poly failed to build its 'test' program," + echo >&2 " so zn_poly's extensive test suite cannot be run." + exit 1 +fi + +echo +echo "Now running zn_poly's extensive test suite..." +test/test all # Run the extensive test suite. +if [ $? -ne 0 ]; then + echo >&2 "Error: zn_poly failed to pass its extensive test suite." + exit 1 +fi +echo "zn_poly has passed its extensive test suite." diff --git a/build/pkgs/zn_poly/spkg-configure.m4 b/build/pkgs/zn_poly/spkg-configure.m4 new file mode 100644 index 00000000000..ba3fd95a96a --- /dev/null +++ b/build/pkgs/zn_poly/spkg-configure.m4 @@ -0,0 +1,8 @@ +SAGE_SPKG_CONFIGURE([zn_poly], [ + SAGE_SPKG_DEPCHECK([gmp mpir], [ + AC_CHECK_HEADER([zn_poly/zn_poly.h], [ + AC_SEARCH_LIBS([zn_mod_init], [zn_poly], [ + ], [sage_spkg_install_zn_poly=yes]) + ], [sage_spkg_install_zn_poly=yes]) + ]) +]) diff --git a/build/pkgs/zn_poly/spkg-install b/build/pkgs/zn_poly/spkg-install.in similarity index 100% rename from build/pkgs/zn_poly/spkg-install rename to build/pkgs/zn_poly/spkg-install.in diff --git a/build/pkgs/zope_interface/SPKG.rst b/build/pkgs/zope_interface/SPKG.rst new file mode 100644 index 00000000000..19310888c32 --- /dev/null +++ b/build/pkgs/zope_interface/SPKG.rst @@ -0,0 +1,16 @@ +zope.interface +============== + +Description +----------- + +This package is intended to be independently reusable in any Python +project. It is maintained by the Zope Toolkit project. + +This package provides an implementation of "object interfaces" for +Python. Interfaces are a mechanism for labeling objects as conforming to +a given API or contract. So, this package can be considered as +implementation of the Design By Contract methodology support in Python. + +For detailed documentation, please see +http://docs.zope.org/zope.interface diff --git a/build/pkgs/zope_interface/SPKG.txt b/build/pkgs/zope_interface/SPKG.txt deleted file mode 100644 index ca529666746..00000000000 --- a/build/pkgs/zope_interface/SPKG.txt +++ /dev/null @@ -1,13 +0,0 @@ -= zope.interface = - -== Description == - -This package is intended to be independently reusable in any Python project. -It is maintained by the Zope Toolkit project. - -This package provides an implementation of "object interfaces" for Python. -Interfaces are a mechanism for labeling objects as conforming to a given API -or contract. So, this package can be considered as implementation of the -Design By Contract methodology support in Python. - -For detailed documentation, please see http://docs.zope.org/zope.interface diff --git a/build/pkgs/zope_interface/checksums.ini b/build/pkgs/zope_interface/checksums.ini deleted file mode 100644 index 2c713d14909..00000000000 --- a/build/pkgs/zope_interface/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=zope.interface-VERSION.tar.gz -sha1=e4dd98256b168e7888abbf6798789c775f5eae35 -md5=a3b24f9d079bae5e13dd7a88aa512112 -cksum=1864918591 diff --git a/build/pkgs/zope_interface/dependencies b/build/pkgs/zope_interface/dependencies index d5dab729e18..15df0c4d6d8 100644 --- a/build/pkgs/zope_interface/dependencies +++ b/build/pkgs/zope_interface/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | pip +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/zope_interface/package-version.txt b/build/pkgs/zope_interface/package-version.txt deleted file mode 100644 index 6016e8addc4..00000000000 --- a/build/pkgs/zope_interface/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -4.6.0 diff --git a/build/pkgs/zope_interface/spkg-install b/build/pkgs/zope_interface/spkg-install old mode 100644 new mode 100755 index deba1bb42bb..d37ec11bc29 --- a/build/pkgs/zope_interface/spkg-install +++ b/build/pkgs/zope_interface/spkg-install @@ -1 +1,2 @@ -cd src && sdh_pip_install . +#! /bin/sh +echo Installation of zope_interface is disabled, as a preparation for removal diff --git a/build/pkgs/zope_interface/spkg-install.in b/build/pkgs/zope_interface/spkg-install.in new file mode 100644 index 00000000000..9e34dd1f6d2 --- /dev/null +++ b/build/pkgs/zope_interface/spkg-install.in @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +echo Installation of zope_interface is disabled, as a preparation for removal diff --git a/build/sage_bootstrap/app.py b/build/sage_bootstrap/app.py index 20f5043d201..a3191b05e2b 100644 --- a/build/sage_bootstrap/app.py +++ b/build/sage_bootstrap/app.py @@ -14,6 +14,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** + import os import logging log = logging.getLogger() @@ -22,6 +23,9 @@ from sage_bootstrap.tarball import Tarball from sage_bootstrap.updater import ChecksumUpdater, PackageUpdater from sage_bootstrap.creator import PackageCreator +from sage_bootstrap.pypi import PyPiVersion, PyPiNotFound +from sage_bootstrap.fileserver import FileServer +from sage_bootstrap.expand_class import PackageClass class Application(object): @@ -39,7 +43,7 @@ def config(self): from sage_bootstrap.config import Configuration print(Configuration()) - def list(self): + def list_cls(self, package_class): """ Print a list of all available packages @@ -52,8 +56,9 @@ def list(self): zn_poly """ log.debug('Listing packages') - for pkg in Package.all(): - print(pkg.name) + pc = PackageClass(package_class) + for pkg_name in pc.names: + print(pkg_name) def name(self, tarball_filename): """ @@ -108,12 +113,38 @@ def update(self, package_name, new_version, url=None): """ log.debug('Updating %s to %s', package_name, new_version) update = PackageUpdater(package_name, new_version) - if url is not None: + if url is not None or update.package.tarball_upstream_url: log.debug('Downloading %s', url) update.download_upstream(url) update.fix_checksum() - def download(self, package_name): + def update_latest(self, package_name): + """ + Update a package to the latest version. This modifies the Sage sources. + """ + try: + pypi = PyPiVersion(package_name) + except PyPiNotFound: + log.debug('%s is not a pypi package', package_name) + return + else: + pypi.update() + + def update_latest_all(self): + log.debug('Attempting to update all packages') + exclude = [ + 'atlas', 'flint', 'bzip2', 'ecm', 'freetype', 'gap', 'glpk', 'graphs', + 'iconv', 'patch', 'r', 'configure', 'bliss', 'readline', 'decorator', + 'igraph', 'rw', 'planarity', 'gambit', + ] + pc = PackageClass(':standard:') + for package_name in pc.names: + if package_name in exclude: + log.debug('skipping %s because of pypi name collision', package_name) + continue + self.update_latest(package_name) + + def download(self, package_name, allow_upstream=False): """ Download a package @@ -123,9 +154,34 @@ def download(self, package_name): """ log.debug('Downloading %s', package_name) package = Package(package_name) - package.tarball.download() + package.tarball.download(allow_upstream=allow_upstream) print(package.tarball.upstream_fqn) + def download_cls(self, package_name_or_class): + pc = PackageClass(package_name_or_class) + pc.apply(self.download) + + def upload(self, package_name): + """ + Upload a package to the Sage mirror network + + $ sage --package upload pari + Uploading /home/vbraun/Code/sage.git/upstream/pari-2.8-2044-g89b0f1e.tar.gz + """ + package = Package(package_name) + if not os.path.exists(package.tarball.upstream_fqn): + log.debug('Skipping %s because there is no local tarbal', package_name) + return + log.info('Uploading %s', package.tarball.upstream_fqn) + fs = FileServer() + fs.upload(package) + + def upload_cls(self, package_name_or_class): + pc = PackageClass(package_name_or_class) + pc.apply(self.upload) + fs = FileServer() + fs.publish() + def fix_all_checksums(self): """ Fix the checksum of a package @@ -137,10 +193,10 @@ def fix_all_checksums(self): log.debug('Ignoring {0} because tarball is not cached'.format(pkg.tarball_filename)) continue if pkg.tarball.checksum_verifies(): - log.debug('Checksum of {0} unchanged'.format(pkg.tarball_filename)) + log.debug('Checksum of {0} (tarball {1}) unchanged'.format(pkg.name, pkg.tarball_filename)) continue update = ChecksumUpdater(pkg.name) - print('Updating checksum of {0}'.format(pkg.tarball_filename)) + print('Updating checksum of {0} (tarball {1})'.format(pkg.name, pkg.tarball_filename)) update.fix_checksum() def fix_checksum(self, package_name): @@ -154,12 +210,12 @@ def fix_checksum(self, package_name): update = ChecksumUpdater(package_name) pkg = update.package if pkg.tarball.checksum_verifies(): - print('Checksum of {0} unchanged'.format(pkg.tarball_filename)) + print('Checksum of {0} (tarball {1}) unchanged'.format(package_name, pkg.tarball_filename)) else: - print('Updating checksum of {0}'.format(pkg.tarball_filename)) + print('Updating checksum of {0} (tarball {1})'.format(package_name, pkg.tarball_filename)) update.fix_checksum() - def create(self, package_name, version, tarball, pkg_type): + def create(self, package_name, version, tarball, pkg_type, upstream_url): log.debug('Creating %s: %s, %s, %s', package_name, version, tarball, pkg_type) creator = PackageCreator(package_name) if version: @@ -167,7 +223,11 @@ def create(self, package_name, version, tarball, pkg_type): if pkg_type: creator.set_type(pkg_type) if tarball: - creator.set_tarball(tarball) - update = ChecksumUpdater(package_name) + creator.set_tarball(tarball, upstream_url) + if upstream_url and version: + update = PackageUpdater(package_name, version) + update.download_upstream() + else: + update = ChecksumUpdater(package_name) update.fix_checksum() diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py index 07925fdec11..117d02c79a3 100644 --- a/build/sage_bootstrap/cmdline.py +++ b/build/sage_bootstrap/cmdline.py @@ -69,6 +69,13 @@ autotools [...] zn_poly + + $ sage --package list :standard: | sort + arb + atlas + backports_ssl_match_hostname + [...] + zn_poly """ @@ -115,6 +122,16 @@ """ +epilog_update_latest = \ +""" +Update a package to the latest version. This modifies the Sage sources. + +EXAMPLE: + + $ sage --package update-latest ipython +""" + + epilog_download = \ """ Download the tarball for a package and print the filename to stdout @@ -127,6 +144,17 @@ """ +epilog_upload = \ +""" +Upload the tarball to the Sage mirror network (requires ssh key authentication) + +EXAMPLE: + + $ sage --package upload pari + Uploading /home/vbraun/Code/sage.git/upstream/pari-2.8-2044-g89b0f1e.tar.gz +""" + + epilog_fix_checksum = \ """ Fix the checksum of a package @@ -134,7 +162,7 @@ EXAMPLE: $ sage --package fix-checksum pari - Updating checksum of pari-2.8-2044-g89b0f1e.tar.gz + Updating checksum of pari (tarball pari-2.8-2044-g89b0f1e.tar.gz) """ epilog_create = \ @@ -170,6 +198,10 @@ def make_parser(): 'list', epilog=epilog_list, formatter_class=argparse.RawDescriptionHelpFormatter, help='Print a list of all available packages') + parser_list.add_argument( + 'package_class', + type=str, default=':all:', nargs='?', + help='Package class like :all: (default) or :standard:') parser_name = subparsers.add_parser( 'name', epilog=epilog_name, @@ -202,12 +234,29 @@ def make_parser(): parser_update.add_argument( '--url', type=str, default=None, help='Download URL') + parser_update_latest = subparsers.add_parser( + 'update-latest', epilog=epilog_update_latest, + formatter_class=argparse.RawDescriptionHelpFormatter, + help='Update a package to the latest version. This modifies the Sage sources.') + parser_update_latest.add_argument( + 'package_name', type=str, help='Package name (:all: for all packages)') + parser_download = subparsers.add_parser( 'download', epilog=epilog_download, formatter_class=argparse.RawDescriptionHelpFormatter, help='Download tarball') parser_download.add_argument( - 'package_name', type=str, help='Package name') + 'package_name', type=str, help='Package name or :type:') + parser_download.add_argument( + '--allow-upstream', action="store_true", + help='Whether to fall back to downloading from the upstream URL') + + parser_upload = subparsers.add_parser( + 'upload', epilog=epilog_upload, + formatter_class=argparse.RawDescriptionHelpFormatter, + help='Upload tarball to Sage mirrors') + parser_upload.add_argument( + 'package_name', type=str, help='Package name or :type:') parser_fix_checksum = subparsers.add_parser( 'fix-checksum', epilog=epilog_fix_checksum, @@ -230,6 +279,8 @@ def make_parser(): '--tarball', type=str, default=None, help='Tarball filename pattern, e.g. Foo-VERSION.tar.bz2') parser_create.add_argument( '--type', type=str, default=None, help='Package type') + parser_create.add_argument( + '--url', type=str, default=None, help='Download URL pattern, e.g. http://example.org/Foo-VERSION.tar.bz2') return parser @@ -249,7 +300,7 @@ def run(): if args.subcommand == 'config': app.config() elif args.subcommand == 'list': - app.list() + app.list_cls(args.package_class) elif args.subcommand == 'name': app.name(args.tarball_filename) elif args.subcommand == 'tarball': @@ -258,10 +309,17 @@ def run(): app.apropos(args.incorrect_name) elif args.subcommand == 'update': app.update(args.package_name, args.new_version, url=args.url) + elif args.subcommand == 'update-latest': + if args.package_name == ':all:': + app.update_latest_all() + else: + app.update_latest(args.package_name) elif args.subcommand == 'download': - app.download(args.package_name) + app.download_cls(args.package_name, args.allow_upstream) elif args.subcommand == 'create': - app.create(args.package_name, args.version, args.tarball, args.type) + app.create(args.package_name, args.version, args.tarball, args.type, args.url) + elif args.subcommand == 'upload': + app.upload_cls(args.package_name) elif args.subcommand == 'fix-checksum': if args.package_name is None: app.fix_all_checksums() diff --git a/build/sage_bootstrap/creator.py b/build/sage_bootstrap/creator.py index b07feb719a6..ec89a5de769 100644 --- a/build/sage_bootstrap/creator.py +++ b/build/sage_bootstrap/creator.py @@ -48,12 +48,15 @@ def set_type(self, pkg_type): f.write(pkg_type) f.write('\n') - def set_tarball(self, tarball): + def set_tarball(self, tarball, upstream_url): """ Write the tarball name pattern to ``checksums.ini`` """ with open(os.path.join(self.path, 'checksums.ini'), 'w+') as f: f.write('tarball={0}'.format(tarball)) f.write('\n') + if upstream_url: + f.write('upstream_url={0}'.format(upstream_url)) + f.write('\n') diff --git a/build/sage_bootstrap/download/app.py b/build/sage_bootstrap/download/app.py index 69acdb78e2d..fc1e302ab1d 100644 --- a/build/sage_bootstrap/download/app.py +++ b/build/sage_bootstrap/download/app.py @@ -36,9 +36,9 @@ def print_fastest_mirror(self): def download_url(self, url, destination): Download(url, destination, progress=not self.quiet, ignore_errors=False).run() - def download_tarball(self, tarball_filename, destination=None): + def download_tarball(self, tarball_filename, destination=None, allow_upstream=False): tarball = Tarball(tarball_filename) - tarball.download() + tarball.download(allow_upstream=allow_upstream) if destination is not None: tarball.save_as(destination) diff --git a/build/sage_bootstrap/download/cmdline.py b/build/sage_bootstrap/download/cmdline.py index c26e9d08e35..85062ae7d67 100644 --- a/build/sage_bootstrap/download/cmdline.py +++ b/build/sage_bootstrap/download/cmdline.py @@ -59,6 +59,10 @@ def make_parser(): '--timeout', type=float, default=None, help='Timeout for network operations') + parser.add_argument( + '--allow-upstream', action="store_true", + help='Whether to fall back to downloading from the upstream URL') + parser.add_argument( 'url_or_tarball', type=str, nargs='?', default=None, help="""A http:// url or a tarball filename. In the latter case, the @@ -71,6 +75,10 @@ def make_parser(): will be downloaded and the content written to stdout and a tarball will be saved under {SAGE_DISTFILES}""".format(SAGE_DISTFILES=SAGE_DISTFILES)) + parser.add_argument( + '--no-check-certificate', action='store_true', + help='Do not check SSL certificates for https connections') + return parser @@ -84,6 +92,12 @@ def run(): level = getattr(logging, args.log.upper()) log.setLevel(level=level) log.debug('Commandline arguments: %s', args) + if args.no_check_certificate: + try: + import ssl + ssl._create_default_https_context = ssl._create_unverified_context + except ImportError: + pass app = Application(timeout=args.timeout, quiet=args.quiet) if (not args.print_fastest_mirror) and (args.url_or_tarball is None): parser.print_help() @@ -95,7 +109,7 @@ def run(): elif is_url(args.url_or_tarball): app.download_url(args.url_or_tarball, args.destination) else: - app.download_tarball(args.url_or_tarball, args.destination) + app.download_tarball(args.url_or_tarball, args.destination, args.allow_upstream) def format_error(message): diff --git a/build/sage_bootstrap/expand_class.py b/build/sage_bootstrap/expand_class.py new file mode 100644 index 00000000000..a666ec7ae93 --- /dev/null +++ b/build/sage_bootstrap/expand_class.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +""" +Utility to specify classes of packages like `:all:` +""" + + +#***************************************************************************** +# Copyright (C) 2016 Volker Braun +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + +import os +import sys +import logging +log = logging.getLogger() + +from sage_bootstrap.package import Package + + +class PackageClass(object): + + def __init__(self, package_name_or_class): + if package_name_or_class == ':all:': + self._init_all() + elif package_name_or_class == ':standard:': + self._init_standard() + elif package_name_or_class == ':optional:': + self._init_optional() + elif package_name_or_class == ':experimental:': + self._init_experimental() + elif package_name_or_class == ':huge:': + self._init_huge() + else: + if package_name_or_class.startswith(':'): + raise ValueError('Package name cannot start with ":", got %s', package_name_or_class) + if package_name_or_class.endswith(':'): + raise ValueError('Package name cannot end with ":", got %s', package_name_or_class) + self.names = [package_name_or_class] + + def _init_all(self): + self.names = [pkg.name for pkg in Package.all()] + + def _init_standard(self): + self.names = [pkg.name for pkg in Package.all() if pkg.type == 'standard'] + + def _init_optional(self): + self.names = [pkg.name for pkg in Package.all() if pkg.type == 'optional'] + + def _init_experimental(self): + self.names = [pkg.name for pkg in Package.all() if pkg.type == 'experimental'] + + def _init_huge(self): + self.names = [pkg.name for pkg in Package.all() if pkg.type == 'huge'] + + def apply(self, function, *args, **kwds): + for package_name in self.names: + function(package_name, *args, **kwds) diff --git a/build/sage_bootstrap/fileserver.py b/build/sage_bootstrap/fileserver.py new file mode 100644 index 00000000000..df6c20ee35e --- /dev/null +++ b/build/sage_bootstrap/fileserver.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +""" +Interface to the Sage fileserver +""" + + +#***************************************************************************** +# Copyright (C) 2016 Volker Braun +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + + +import os +import subprocess + + + +class FileServer(object): + + def __init__(self): + self.user = 'sagemath' + self.hostname = 'fileserver.sagemath.org' + + def upstream_directory(self, package): + """ + Return the directory where the tarball resides on the server + """ + return os.path.join( + '/', 'data', 'files', 'spkg', 'upstream', package.name, + ) + + def upload(self, package): + """ + Upload the current tarball of package + """ + subprocess.check_call([ + 'ssh', 'sagemath@fileserver.sagemath.org', + 'mkdir -p {0} && touch {0}/index.html'.format(self.upstream_directory(package)) + ]) + subprocess.check_call([ + 'rsync', '-av', '--checksum', '-e', 'ssh -l sagemath', + package.tarball.upstream_fqn, + 'fileserver.sagemath.org:{0}'.format(self.upstream_directory(package)) + ]) + + def publish(self): + """ + Publish the files + """ + subprocess.check_call([ + 'ssh', 'sagemath@fileserver.sagemath.org', './publish-files.sh' + ]) diff --git a/build/sage_bootstrap/package.py b/build/sage_bootstrap/package.py index 08ed0b4d550..bbd04fab36a 100644 --- a/build/sage_bootstrap/package.py +++ b/build/sage_bootstrap/package.py @@ -45,6 +45,7 @@ def __init__(self, package_name): self.__tarball = None self._init_checksum() self._init_version() + self._init_type() def __repr__(self): return 'Package {0}'.format(self.name) @@ -147,6 +148,33 @@ def tarball_filename(self): """ return self.tarball_pattern.replace('VERSION', self.version) + @property + def tarball_upstream_url_pattern(self): + """ + Return the tarball upstream URL pattern + + OUTPUT: + + String. The tarball upstream URL, but with the placeholder + ``VERSION``. + """ + return self.__tarball_upstream_url_pattern + + @property + def tarball_upstream_url(self): + """ + Return the tarball upstream URL or ``None`` if none is recorded + + OUTPUT: + + String. The URL. + """ + pattern = self.tarball_upstream_url_pattern + if pattern: + return pattern.replace('VERSION', self.version) + else: + return None + @property def tarball_package(self): """ @@ -190,6 +218,13 @@ def patchlevel(self): """ return self.__patchlevel + @property + def type(self): + """ + Return the package type + """ + return self.__type + def __eq__(self, other): return self.tarball == other.tarball @@ -200,10 +235,15 @@ def all(cls): """ base = os.path.join(SAGE_ROOT, 'build', 'pkgs') for subdir in os.listdir(base): - path = os.path.join(base, subdir) + path = os.path.join(base, subdir) if not os.path.isfile(os.path.join(path, "checksums.ini")): + log.debug('%s has no checksums.ini', subdir) continue - yield cls(subdir) + try: + yield cls(subdir) + except BaseException: + log.error('Failed to open %s', subdir) + raise @property def path(self): @@ -217,7 +257,7 @@ def _init_checksum(self): Load the checksums from the appropriate ``checksums.ini`` file """ checksums_ini = os.path.join(self.path, 'checksums.ini') - assignment = re.compile('(?P[a-zA-Z0-9]*)=(?P.*)') + assignment = re.compile('(?P[a-zA-Z0-9_]*)=(?P.*)') result = dict() with open(checksums_ini, 'rt') as f: for line in f.readlines(): @@ -230,6 +270,7 @@ def _init_checksum(self): self.__sha1 = result.get('sha1', None) self.__cksum = result.get('cksum', None) self.__tarball_pattern = result['tarball'] + self.__tarball_upstream_url_pattern = result.get('upstream_url', None) # Name of the directory containing the checksums.ini file self.__tarball_package_name = os.path.realpath(checksums_ini).split(os.sep)[-2] @@ -246,4 +287,10 @@ def _init_version(self): self.__version = match.group('version') self.__patchlevel = int(match.group('patchlevel')) - + def _init_type(self): + with open(os.path.join(self.path, 'type')) as f: + package_type = f.read().strip() + assert package_type in [ + 'base', 'standard', 'optional', 'experimental', 'script', 'pip' + ] + self.__type = package_type diff --git a/build/sage_bootstrap/pypi.py b/build/sage_bootstrap/pypi.py new file mode 100644 index 00000000000..7b17df3a58e --- /dev/null +++ b/build/sage_bootstrap/pypi.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +""" +PyPi Version Information +""" + + +#***************************************************************************** +# Copyright (C) 2016 Volker Braun +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +import logging +log = logging.getLogger() + +import json + +from sage_bootstrap.package import Package +from sage_bootstrap.updater import PackageUpdater +from sage_bootstrap.compat import urllib + + +class PyPiNotFound(Exception): + pass + + +class PyPiError(Exception): + pass + + +class PyPiVersion(object): + + def __init__(self, package_name): + self.name = package_name + self.json = self._get_json() + + def _get_json(self): + response = urllib.urlopen(self.json_url) + if response.getcode() != 200: + raise PyPiNotFound('%s not on pypi', self.name) + data = response.read() + text = data.decode('utf-8') + return json.loads(text) + + @property + def json_url(self): + return 'https://pypi.python.org/pypi/{0}/json'.format(self.name) + + @property + def version(self): + """ + Return the current version + """ + return self.json['info']['version'] + + @property + def url(self): + """ + Return the source url + """ + for download in self.json['urls']: + if download['python_version'] == 'source': + return download['url'] + raise PyPiError('No source url for %s found', self.name) + + def update(self): + package = Package(self.name) + if package.version == self.version: + log.info('%s is already at the latest version', self.name) + return + log.info('Updating %s: %s -> %s', self.name, package.version, self.version) + update = PackageUpdater(self.name, self.version) + update.download_upstream(self.url) + update.fix_checksum() diff --git a/build/sage_bootstrap/tarball.py b/build/sage_bootstrap/tarball.py index 63bcc15a6bd..ad03f1035ad 100644 --- a/build/sage_bootstrap/tarball.py +++ b/build/sage_bootstrap/tarball.py @@ -48,7 +48,7 @@ def __init__(self, tarball_name, package=None): INPUT: - - ``name`` - string. The full filename (``foo-1.3.tar.bz2``) + - ``tarball_name`` - string. The full filename (``foo-1.3.tar.bz2``) of a tarball on the Sage mirror network. """ self.__filename = tarball_name @@ -132,9 +132,13 @@ def checksum_verifies(self): sha1 = self._compute_sha1() return sha1 == self.package.sha1 - def download(self): + def download(self, allow_upstream=False): """ Download the tarball to the upstream directory. + + If allow_upstream is False and the package cannot be found + on the sage mirrors, fall back to downloading it from + the upstream URL if the package has one. """ destination = self.upstream_fqn if os.path.isfile(destination): @@ -159,7 +163,16 @@ def download(self): except IOError: log.debug('File not on mirror') if not successful_download: - raise FileNotMirroredError('tarball does not exist on mirror network') + url = self.package.tarball_upstream_url + if allow_upstream and url: + log.info('Attempting to download from {}'.format(url)) + try: + Download(url, destination).run() + successful_download = True + except IOError: + raise FileNotMirroredError('tarball does not exist on mirror network and neither at the upstream URL') + else: + raise FileNotMirroredError('tarball does not exist on mirror network') if not self.checksum_verifies(): raise ChecksumError('checksum does not match') diff --git a/build/sage_bootstrap/updater.py b/build/sage_bootstrap/updater.py index 496b579cd37..3fb46dc7960 100644 --- a/build/sage_bootstrap/updater.py +++ b/build/sage_bootstrap/updater.py @@ -36,18 +36,21 @@ def package(self): def fix_checksum(self): checksums_ini = os.path.join(self.package.path, 'checksums.ini') + s = self.checksums_ini() with open(checksums_ini, 'w') as f: - f.write(self.checksums_ini()) + f.write(s) def checksums_ini(self): tarball = self.package.tarball result = [ 'tarball=' + self.package.tarball_pattern, 'sha1=' + tarball._compute_sha1(), - 'md5=' + tarball._compute_md5(), - 'cksum=' + tarball._compute_cksum(), - '' # newline at end + 'md5=' + tarball._compute_md5(), + 'cksum=' + tarball._compute_cksum() ] + if self.package.tarball_upstream_url_pattern: + result.append('upstream_url=' + self.package.tarball_upstream_url_pattern) + result.append('') # newline at end return '\n'.join(result) @@ -63,8 +66,12 @@ def _update_version(self, new_version): with open(package_version_txt, 'w') as f: f.write(new_version.strip() + '\n') - def download_upstream(self, download_url): + def download_upstream(self, download_url=None): tarball = self.package.tarball + if download_url is None: + download_url = self.package.tarball_upstream_url + if download_url is None: + raise ValueError("package has no default upstream_url pattern, download_url needed") print('Downloading tarball to {0}'.format(tarball.upstream_fqn)) Download(download_url, tarball.upstream_fqn).run() diff --git a/build/test/test_download_cmdline.py b/build/test/test_download_cmdline.py index 444961d4118..4326b7f5a31 100644 --- a/build/test/test_download_cmdline.py +++ b/build/test/test_download_cmdline.py @@ -34,6 +34,8 @@ class SageDownloadFileTestCase(unittest.TestCase): + maxDiff = None + def test_print_mirror_list_no_network(self): """ Subsequent runs of sage-download-file diff --git a/build/test/test_package.py b/build/test/test_package.py index a9eb3d13d8b..d15700a7c92 100644 --- a/build/test/test_package.py +++ b/build/test/test_package.py @@ -20,6 +20,8 @@ class PackageTestCase(unittest.TestCase): + maxDiff = None + def test_package(self): pkg = Package('pari') self.assertTrue(pkg.name, 'pari') diff --git a/build/test/test_package_cmdline.py b/build/test/test_package_cmdline.py index 69011314e1f..739fbe76d30 100644 --- a/build/test/test_package_cmdline.py +++ b/build/test/test_package_cmdline.py @@ -140,7 +140,7 @@ def test_fix_checksum(self): # Prints to stdout self.assertEqual( stdout.rstrip(), - 'Checksum of {0} unchanged'.format(pkg.tarball_filename)) + 'Checksum of {0} (tarball {1}) unchanged'.format(pkg.name, pkg.tarball_filename)) def test_create(self): tmp = tempfile.mkdtemp() diff --git a/build/tox.ini b/build/tox.ini index a44694b17c8..93c79bf7892 100644 --- a/build/tox.ini +++ b/build/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26, py27, py33, py34 +envlist = py26, py27, py33, py34, py35, py36, py37, py38 skip_missing_interpreters=true [testenv] @@ -20,3 +20,15 @@ commands=python3.3 -m unittest discover [testenv:py34] commands=python3.4 -m unittest discover + +[testenv:py35] +commands=python3.5 -m unittest discover + +[testenv:py36] +commands=python3.6 -m unittest discover + +[testenv:py37] +commands=python3.7 -m unittest discover + +[testenv:py38] +commands=python3.8 -m unittest discover diff --git a/configure.ac b/configure.ac index 07c88a3229b..d15c3f12d62 100644 --- a/configure.ac +++ b/configure.ac @@ -28,31 +28,6 @@ AC_PREREQ([2.69]) AC_DEFUN([SAGE_VERSION], m4_esyscmd_s([. src/bin/sage-version.sh && echo $SAGE_VERSION])) AC_INIT([Sage], SAGE_VERSION, [sage-devel@googlegroups.com]) -# The following needs to be immediately after calling AC_INIT -#------------------------------------------------------------ -# We need to run this configure script with bash -if test -z "$BASH_VERSION" -then - CONFIG_SHELL=`command -v bash` - export CONFIG_SHELL - if $CONFIG_SHELL -c "exit 0" - then - exec $CONFIG_SHELL $0 "$@" - else - AC_MSG_NOTICE([The 'bash' shell is needed to build AC_PACKAGE_NAME]) - AC_MSG_NOTICE([All modern systems will have the 'bash' shell installed somewhere]) - if test -d /opt/OpenSource/bin - then - AC_MSG_NOTICE([On HP-UX you may try adding /opt/OpenSource/bin to your path]) - fi - if test -d /opt/pware/bin - then - AC_MSG_NOTICE([On AIX you may try adding /opt/pware/bin to your path]) - fi - AC_MSG_ERROR(['bash' not found]) - fi -fi - AC_COPYRIGHT([GPL version 3]) AC_CONFIG_SRCDIR([configure.ac]) @@ -87,6 +62,7 @@ AC_CONFIG_MACRO_DIR([m4]) # Assume current directory is SAGE_ROOT. SAGE_ROOT=`pwd -P` +AC_SUBST(SAGE_ROOT) SAGE_LOCAL="$prefix" # The following line is necessary because the code below @@ -301,6 +277,14 @@ if test -z "$MAKE"; then [AC_MSG_ERROR([[found GNU make in $ac_cv_path_MAKE, but it's not the first "make" program in your PATH]])]) fi +# Check for system python +AC_MSG_CHECKING([for Python]) +if SAGE_SYSTEM_PYTHON=$(build/bin/sage-system-python -c "import sys; print(sys.executable)"); then + AC_MSG_RESULT([$SAGE_SYSTEM_PYTHON]) +else + AC_MSG_ERROR([You do not have a suitable version of Python installed]) +fi + # Check for Latex, the use of which is less important in Sage than # it used to be, as it was at one time required to build any documentation # but this is no longer so. @@ -325,10 +309,14 @@ AX_PROG_PERL_VERSION([5.8.0],[],[ # Check C/C++/Fortran compilers ############################################################################### +SAGE_CHECK_CONDA_COMPILERS + AC_PROG_CC() AC_PROG_CPP() AC_PROG_CXX() -AC_PROG_FC() +# Search for gfortran-9 first. #30067 +# This is a temporary workaround until we support gfortran 10. +AC_PROG_FC([gfortran-9 gfortran g95 xlf95 f95 fort ifort ifc efc pgfortran pgf95 lf95 ftn nagfor xlf90 f90 pgf90 pghpf epcf90 g77 xlf f77 frt pgf77 cf77 fort77 fl32 af77]) AC_SUBST(CC) AC_SUBST(FC) @@ -390,22 +378,18 @@ SAGE_CHECK_OSX_SUPPORTED() ############################################################################### # Python version -AC_MSG_CHECKING([Python version to install]) AC_ARG_WITH([python], -[AS_HELP_STRING([--with-python=2], - [build and use Python 2])] [AS_HELP_STRING([--with-python=3], [build and use Python 3 (default)])]) case "$with_python" in - 2*) SAGE_PYTHON_VERSION=2;; - 3*) SAGE_PYTHON_VERSION=3;; + 3) SAGE_PYTHON_VERSION=3;; + 3*) AC_MSG_WARN([the only allowed value for --with-python is 3; specific Python 3.x versions cannot be requested using --with-python. For compatibility reasons, this is only a warning. Use './configure PYTHON3=/path/to/python3' to use a specific Python 3 binary for the Sage venv.]) + SAGE_PYTHON_VERSION=3;; "") SAGE_PYTHON_VERSION=3;; *) - AC_MSG_ERROR([allowed values for --with-python are 2 and 3]);; + AC_MSG_ERROR([the only allowed value for --with-python is 3. Support for Python 2 has been removed in Sage 9.2.]);; esac - -AC_MSG_RESULT([$SAGE_PYTHON_VERSION]) AC_SUBST(SAGE_PYTHON_VERSION) # $(TOOLCHAIN) variable containing prerequisites for the build @@ -415,39 +399,27 @@ if test "$SAGE_INSTALL_CCACHE" = yes ; then fi AC_SUBST([SAGE_TOOLCHAIN]) -SAGE_SPKG_COLLECT() - -# We need sage-env-config to exist before running this. -# TODO: fix this in Trac #21524 -touch "$SAGE_SRC/bin/sage-env-config" - -SAGE_SCRIPTS='\ -' -for file in "$SAGE_SRC/bin/"*; do - # Skip files with ".in" extension - ext=${file##*.} - if test "$ext" != in; then - SAGE_SCRIPTS+=" \$(SAGE_LOCAL)${file#$SAGE_SRC} \\"$'\n' - fi -done - -AC_SUBST([SAGE_SCRIPTS]) - -SAGE_EXTCODE='\ -' -for file in `find "$SAGE_SRC"/ext -type f`; do - SAGE_EXTCODE+=" \$(SAGE_EXTCODE)${file#$SAGE_SRC/ext} \\"$'\n' -done +AC_ARG_ENABLE([experimental-packages], + [AS_HELP_STRING([--enable-experimental-packages], + [allow installing experimental packages (default: no = ask for user confirmation for each package)])]) +AC_ARG_ENABLE([download-from-upstream-url], + [AS_HELP_STRING([--enable-download-from-upstream-url], + [allow downloading packages from their upstream URL if they cannot be found on the Sage mirrors])]) -AC_SUBST([SAGE_EXTCODE]) - -SAGE_MAKE_DEPS="$SAGE_ROOT/build/make/deps" -AC_SUBST_FILE([SAGE_MAKE_DEPS]) +SAGE_SPKG_OPTIONS="" +AS_IF([test "x$enable_experimental_packages" = "xyes"], [ + AS_VAR_APPEND([SAGE_SPKG_OPTIONS], [" -y"]) +]) +AS_IF([test "x$enable_download_from_upstream_url" = "xyes"], [ + AS_VAR_APPEND([SAGE_SPKG_OPTIONS], [" -o"]) +]) +AC_SUBST([SAGE_SPKG_OPTIONS]) +SAGE_SPKG_COLLECT() dnl AC_CONFIG_HEADERS([config.h]) -AC_CONFIG_FILES([build/make/Makefile-auto build/make/Makefile src/Makefile]) -AC_CONFIG_FILES([src/bin/sage-env-config]) +AC_CONFIG_FILES([build/make/Makefile-auto build/make/Makefile]) +AC_CONFIG_FILES([src/bin/sage-env-config build/bin/sage-build-env-config]) AC_CONFIG_FILES([build/pkgs/sage_conf/src/sage_conf.py build/pkgs/sage_conf/src/setup.cfg]) @@ -457,6 +429,7 @@ AC_CONFIG_COMMANDS(mkdirs, for d in "$SAGE_LOGS" "$SAGE_LOCAL" \ "$SAGE_LOCAL/bin" "$SAGE_LOCAL/etc" \ "$SAGE_LOCAL/include" "$SAGE_LOCAL/lib" \ + "$SAGE_LOCAL/lib/pkgconfig" \ "$SAGE_SHARE" "$SAGE_INST"; do AC_MSG_NOTICE([creating directory $d]) mkdir -p "$d" || AC_MSG_ERROR(could not create $d) @@ -481,4 +454,6 @@ AC_CONFIG_COMMANDS(mkdirs, AC_OUTPUT() +SAGE_SYSTEM_PACKAGE_NOTICE() + dnl vim:syntax=m4 diff --git a/docker/README.md b/docker/README.md index a39e0021050..e77008fc886 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,4 +1,4 @@ -[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://github.com/sagemath/sage/COPYING.txt) [![Maintained](https://img.shields.io/maintenance/yes/2020.svg)](https://github.com/sagemath/sage/commits/master) +[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://github.com/sagemath/sage/COPYING.txt) # Supported tags @@ -6,7 +6,7 @@ * `x.x` — all stable releases of Sage are tagged with their version number. * `x.x.{beta,rc}x` - betas and release candidates of Sage as [tagged in our git repository](https://github.com/sagemath/sage/tags). * `develop` — the current development version of Sage which gets merged into the `master` branch when a new version of Sage is released [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sagemath/sage/develop.svg)](https://github.com/sagemath/sage/commits/develop) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/develop/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/develop) -* all the above tags can be complemented by `-py3` for Python 3 based images +* `-py3` - until Sage 9.1, we provided Python 2 builds (with no suffix) and Python 3 builds (with the `-py3` suffix). From Sage 9.2.beta0 on, all images we provide are based on Python 3 and the `-py3` suffix survives only for historical reasons: with or without it, you get Python 3. # What is SageMath diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 00000000000..bd753b34d7d --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,53 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/m4/ax_check_root.m4 b/m4/ax_check_root.m4 index 2ac1f669229..a8632264769 100644 --- a/m4/ax_check_root.m4 +++ b/m4/ax_check_root.m4 @@ -48,7 +48,7 @@ AC_DEFUN([AX_CHECK_ROOT],[ AC_MSG_CHECKING([for root user]) uid=`id -u` -if test "x$uid" == "x0"; then +if test "x$uid" = "x0"; then AC_MSG_RESULT([yes]) $1 else diff --git a/m4/ppl.m4 b/m4/ppl.m4 new file mode 100644 index 00000000000..05d5223a404 --- /dev/null +++ b/m4/ppl.m4 @@ -0,0 +1,304 @@ +dnl A function to test for the existence and usability of particular +dnl versions of the PPL, defining macros containing the required paths. +dnl Copyright (C) 1997 Owen Taylor +dnl Copyright (C) 2001-2010 Roberto Bagnara +dnl Copyright (C) 2010-2016 BUGSENG srl (http://bugseng.com) +dnl +dnl This file is part of the Parma Polyhedra Library (PPL). +dnl +dnl The PPL is free software; you can redistribute it and/or modify it +dnl under the terms of the GNU General Public License as published by the +dnl Free Software Foundation; either version 3 of the License, or (at your +dnl option) any later version. +dnl +dnl The PPL is distributed in the hope that it will be useful, but WITHOUT +dnl ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +dnl FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +dnl for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software Foundation, +dnl Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA. +dnl +dnl For the most up-to-date information see the Parma Polyhedra Library +dnl site: http://bugseng.com/products/ppl/ . + +dnl AM_PATH_PPL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for PPL, and define PPL_CPPFLAGS, PPL_LDFLAGS, ... what else? + +AC_DEFUN([AM_PATH_PPL], +[ +dnl Get the required information from the ppl-config program. +AC_ARG_WITH(ppl-prefix, + AS_HELP_STRING([--with-ppl-prefix=PREFIX], + [prefix used to configure the PPL]), + ppl_prefix="$withval", + ppl_prefix="") +AC_ARG_WITH(ppl-exec-prefix, + AS_HELP_STRING([--with-ppl-exec-prefix=PREFIX], + [exec-prefix used to configure the PPL]), + ppl_exec_prefix="$withval", + ppl_exec_prefix="") +AC_ARG_ENABLE(ppl-test, + AS_HELP_STRING([--disable-ppltest], + [do not try to compile and run a test PPL program]), + , + enable_ppltest=yes) + +if test "x$ppl_exec_prefix" != x +then + ppl_config_args="$ppl_config_args --exec-prefix=$ppl_exec_prefix" + if test "x${PPL_CONFIG+set}" != xset + then + PPL_CONFIG="$ppl_exec_prefix/bin/ppl-config" + fi +fi +if test "x$ppl_prefix" != x +then + ppl_config_args="$ppl_config_args --prefix=$ppl_prefix" + if test "x${PPL_CONFIG+set}" != xset + then + PPL_CONFIG="$ppl_prefix/bin/ppl-config" + fi +fi + +AC_PATH_PROG(PPL_CONFIG, ppl-config, no) +min_ppl_version=ifelse([$1], ,0.0,$1) +if test \( "x$min_ppl_version" = "x0.0" \) -o \( "x$min_ppl_version" = "x0.0.0" \) +then + AC_MSG_CHECKING([for the Parma Polyhedra Library]) +else + AC_MSG_CHECKING([for the Parma Polyhedra Library, version >= $min_ppl_version]) +fi +no_ppl="" +if test $PPL_CONFIG = no +then + no_ppl=yes +else + PPL_CPPFLAGS=`$PPL_CONFIG $ppl_config_args --cppflags` + PPL_LDFLAGS="" + PPL_LIBS="" + for flag in $($PPL_CONFIG $ppl_config_args --ldflags); do + dnl Check if each "ldflag" starts with -l or not. The ones that do + dnl start with -l are libs and belong in PPL_LIBS, the others belong + dnl in PPL_LDFLAGS. The LDFLAGS and LIBS variables get appended at + dnl different locations in the link command, so the distinction is + dnl not academic. + if test "x${flag#-l}" = "x$flag"; then + dnl this flag doesn't start with -l + PPL_LDFLAGS="$PPL_LDFLAGS $flag" + else + PPL_LIBS="$PPL_LIBS $flag" + fi + done + ppl_config_version="`$PPL_CONFIG $ppl_config_args --version`" + + if test "x$enable_ppltest" = xyes + then + ac_save_CPPFLAGS="$CPPFLAGS" + ac_save_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $PPL_CPPFLAGS" + LIBS="$LIBS $PPL_LIBS" + +dnl Now check if the installed PPL is sufficiently new. +dnl (Also sanity checks the results of ppl-config to some extent.) + + AC_LANG_PUSH(C++) + + rm -f conf.ppltest + AC_TRY_RUN([ +#include +#include +#include +#include + +namespace PPL = Parma_Polyhedra_Library; + +using std::cout; +using std::endl; + +int +main() { + system("touch conf.ppltest"); + + unsigned min_ppl_major, min_ppl_minor, min_ppl_revision, min_ppl_beta; + int n = sscanf("$min_ppl_version", + "%u.%u.%upre%u%*c", + &min_ppl_major, &min_ppl_minor, + &min_ppl_revision, &min_ppl_beta); + bool min_ppl_version_ok = true; + if (n == 4) { + if (min_ppl_beta == 0) + min_ppl_version_ok = false; + } + else if (n == 3) { + n = sscanf("$min_ppl_version", + "%u.%u.%u%*c", + &min_ppl_major, &min_ppl_minor, &min_ppl_revision); + if (n != 3) + min_ppl_version_ok = false; + else + min_ppl_beta = 0; + } + else if (n == 2) { + n = sscanf("$min_ppl_version", + "%u.%upre%u%*c", + &min_ppl_major, &min_ppl_minor, &min_ppl_beta); + if (n == 3) { + if (min_ppl_beta == 0) + min_ppl_version_ok = false; + else + min_ppl_revision = 0; + } + else if (n == 2) { + n = sscanf("$min_ppl_version", + "%u.%u%*c", + &min_ppl_major, &min_ppl_minor); + if (n != 2) + min_ppl_version_ok = false; + else { + min_ppl_revision = 0; + min_ppl_beta = 0; + } + } + else + min_ppl_version_ok = false; + } + else + min_ppl_version_ok = false; + + if (!min_ppl_version_ok) { + cout << "illegal version string '$min_ppl_version'" + << endl; + return 1; + } + + if (strcmp("$ppl_config_version", PPL::version()) != 0) { + cout << "\n*** 'ppl-config --version' returned $ppl_config_version, " + "but PPL version " + << PPL::version() + << "\n*** was found! If ppl-config was correct, then it is best" + "\n*** to remove the old version of PPL." + " You may also be able to fix the error" + "\n*** by modifying your LD_LIBRARY_PATH enviroment variable," + " or by editing" + "\n*** /etc/ld.so.conf." + " Make sure you have run ldconfig if that is" + "\n*** required on your system." + "\n*** If ppl-config was wrong, set the environment variable" + " PPL_CONFIG" + "\n*** to point to the correct copy of ppl-config," + " and remove the file config.cache" + "\n*** before re-running configure." + << endl; + return 1; + } + else if (strcmp(PPL_VERSION, PPL::version()) != 0) { + cout << "\n*** PPL header file (version " PPL_VERSION ") does not match" + << "\n*** library (version " << PPL::version() << ")" + << endl; + return 1; + } + else if (PPL_VERSION_MAJOR < min_ppl_major + || (PPL_VERSION_MAJOR == min_ppl_major + && PPL_VERSION_MINOR < min_ppl_minor) + || (PPL_VERSION_MAJOR == min_ppl_major + && PPL_VERSION_MINOR == min_ppl_minor + && PPL_VERSION_REVISION < min_ppl_revision) + || (PPL_VERSION_MAJOR == min_ppl_major + && PPL_VERSION_MINOR == min_ppl_minor + && PPL_VERSION_REVISION == min_ppl_revision + && PPL_VERSION_BETA < min_ppl_beta)) { + cout << "\n*** An old version of PPL (" PPL_VERSION ") was found." + "\n*** You need at least PPL version $min_ppl_version." + " The latest version of" + "\n*** PPL is always available from ftp://ftp.cs.unipr.it/ppl/ ." + "\n***" + "\n*** If you have already installed a sufficiently new version," + " this error" + "\n*** probably means that the wrong copy of the ppl-config" + " program is" + "\n*** being found. The easiest way to fix this is to remove" + " the old version" + "\n*** of PPL, but you can also set the PPL_CONFIG environment" + " variable to point" + "\n*** to the correct copy of ppl-config. (In this case," + " you will have to" + "\n*** modify your LD_LIBRARY_PATH enviroment" + " variable or edit /etc/ld.so.conf" + "\n*** so that the correct libraries are found at run-time.)" + << endl; + return 1; + } + return 0; +} +],, no_ppl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + + AC_LANG_POP + + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + fi +fi + +if test "x$no_ppl" = x +then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) +else + AC_MSG_RESULT(no) + if test x"$PPL_CONFIG" = xno + then + echo "*** The ppl-config script installed by PPL could not be found." + echo "*** If the PPL was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the PPL_CONFIG environment variable to the" + echo "*** full path to ppl-config." + else + if test -f conf.ppltest + then + : + else + echo "*** Could not run PPL test program, checking why..." + CPPFLAGS="$CPPFLAGS $PPL_CPPFLAGS" + LIBS="$LIBS $PPL_LIBS" + AC_TRY_LINK([ +#include +using namespace Parma_Polyhedra_Library; +], +[ + return version_major() || version_minor() + || version_revision() || version_beta(); +], +[ + echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding the PPL or finding the" + echo "*** wrong version of the PPL. If it is not finding the PPL, you will" + echo "*** need to set your LD_LIBRARY_PATH environment variable, or edit" + echo "*** /etc/ld.so.conf to point to the installed location. Also, make" + echo "*** sure you have run ldconfig if that is required on your system." + echo "***" + echo "*** If you have an old version installed, it is best to remove it," + echo "*** although you may also be able to get things to work by modifying" + echo "*** LD_LIBRARY_PATH." +], +[ + echo "*** The test program failed to compile or link. See the file" + echo "*** config.log for the exact error that occured. This usually means" + echo "*** the PPL was incorrectly installed or that someone moved the PPL" + echo "*** since it was installed. In both cases you should reinstall" + echo "*** the library." +]) + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + PPL_CPPFLAGS="" + PPL_LDFLAGS="" + PPL_LIBS="" + ifelse([$3], , :, [$3]) +fi +AC_SUBST(PPL_CPPFLAGS) +AC_SUBST(PPL_LDFLAGS) +AC_SUBST(PPL_LIBS) +rm -f conf.ppltest +]) diff --git a/m4/sage_check_conda_compilers.m4 b/m4/sage_check_conda_compilers.m4 new file mode 100644 index 00000000000..05cdf8773f2 --- /dev/null +++ b/m4/sage_check_conda_compilers.m4 @@ -0,0 +1,25 @@ +AC_DEFUN([SAGE_CHECK_CONDA_COMPILERS], [ + AC_MSG_CHECKING([whether a conda environment is active]) + AS_IF([test "x$CONDA_PREFIX" != x], [have_conda_active=yes], [have_conda_active=no]) + AC_MSG_RESULT($have_conda_active) + AS_IF([test $have_conda_active = yes], [ + dnl A conda environment is active. + dnl #27699: Conda compiler packages must be installed + need_pkgs="c-compiler cxx-compiler fortran-compiler" + AS_IF([test -z "$CC" -o -z "$CXX" -o -z "$FC" ], [ + AC_MSG_ERROR([A conda environment ($CONDA_DEFAULT_ENV) is active, but +at least one of the environment variables CC, CXX, FC is not set, which indicates +that the conda environment is missing the following conda packages required +for building Sage: + $need_pkgs +For building Sage, either: +- activate a conda environment that has these packages, using: + conda activate ENVIRONMENT +- or install these conda packages, using + conda install $need_pkgs +- or deactivate conda by + conda deactivate + (this command may need to be repeated).]) + ]) + ]) +]) diff --git a/m4/sage_spkg_collect.m4 b/m4/sage_spkg_collect.m4 index 0e1f94f23c9..66d81cf58aa 100644 --- a/m4/sage_spkg_collect.m4 +++ b/m4/sage_spkg_collect.m4 @@ -28,8 +28,8 @@ # - SAGE_OPTIONAL_PACKAGES - lists the names of packages with the # "optional" type that should be installed. # -# - SAGE_SDIST_PACKAGES - lists the names of all packages that should be -# included in the source distribution. +# - SAGE_SDIST_PACKAGES - lists the names of all packages whose sources +# need to be downloaded to be included in the source distribution. # # - SAGE_PACKAGE_VERSIONS - this template variable defines multiple # Makefile variables in the format "vers_" the value @@ -92,7 +92,7 @@ newest_version() { if test -f "$SAGE_ROOT/build/pkgs/$SPKG/package-version.txt" ; then cat "$SAGE_ROOT/build/pkgs/$SPKG/package-version.txt" else - echo "$SPKG" + echo none fi } @@ -100,34 +100,33 @@ newest_version() { # not required on this platform or that can be taken from the underlying system # installation. Note that this contains packages that are not actually going to # be installed by most users because they are optional/experimental. -SAGE_BUILT_PACKAGES='\ -' +SAGE_BUILT_PACKAGES='' + # The complement of SAGE_BUILT_PACKAGES, i.e., packages that are not required # on this platform or packages where we found a suitable package on the # underlying system. -SAGE_DUMMY_PACKAGES='\ -' +SAGE_DUMMY_PACKAGES='' + # Standard packages -SAGE_STANDARD_PACKAGES='\ -' +SAGE_STANDARD_PACKAGES='' + # List of currently installed and to-be-installed optional packages - filled in SAGE_SPKG_ENABLE #SAGE_OPTIONAL_INSTALLED_PACKAGES # List of optional packages to be uninstalled - filled in SAGE_SPKG_ENABLE #SAGE_OPTIONAL_CLEANED_PACKAGES # List of all packages that should be downloaded -SAGE_SDIST_PACKAGES='\ -' +SAGE_SDIST_PACKAGES='' + # Generate package version and dependency lists SAGE_PACKAGE_VERSIONS="" SAGE_PACKAGE_DEPENDENCIES="" # Lists of packages categorized according to their build rules -SAGE_NORMAL_PACKAGES='\ -' -SAGE_PIP_PACKAGES='\ -' -SAGE_SCRIPT_PACKAGES='\ -' +SAGE_NORMAL_PACKAGES='' +SAGE_PIP_PACKAGES='' +SAGE_SCRIPT_PACKAGES='' + +SAGE_NEED_SYSTEM_PACKAGES="" # for each package in pkgs/, add them to the SAGE_PACKAGE_VERSIONS and # SAGE_PACKAGE_DEPENDENCIES lists, and to one or more of the above variables @@ -157,7 +156,7 @@ for DIR in $SAGE_ROOT/build/pkgs/*; do message="came preinstalled with the SageMath tarball" ;; standard) - SAGE_STANDARD_PACKAGES+=" $SPKG_NAME \\"$'\n' + SAGE_STANDARD_PACKAGES="${SAGE_STANDARD_PACKAGES} \\$(printf '\n ')${SPKG_NAME}" in_sdist=yes message="will be installed as an SPKG" ;; @@ -169,20 +168,39 @@ for DIR in $SAGE_ROOT/build/pkgs/*; do message="experimental, use \"$srcdir/configure --enable-$SPKG_NAME\" to install" uninstall_message=", use \"$srcdir/configure --disable-$SPKG_NAME\" to uninstall" ;; - script) - message="use \"$srcdir/configure --enable-$SPKG_NAME\" to install as an SPKG" - ;; - pip) - message="use \"$srcdir/configure --enable-$SPKG_NAME\" to install as an SPKG" - ;; *) - AC_MSG_ERROR([The content of "$SPKG_TYPE_FILE" must be 'base', 'standard', 'optional', 'experimental', 'script', or 'pip']) + AC_MSG_ERROR([The content of "$SPKG_TYPE_FILE" must be 'base', 'standard', 'optional', or 'experimental']) + ;; + esac + + case "$SPKG_TYPE" in + optional|experimental) + stampfile="" + for f in "$SAGE_SPKG_INST/$SPKG_NAME"-*; do + AS_IF([test -r "$f"], [ + AS_IF([test -n "$stampfile"], [ + AC_MSG_ERROR(m4_normalize([ + multiple installation records for $SPKG_NAME: + m4_newline($(ls -l "$SAGE_SPKG_INST/$SPKG_NAME"-*)) + m4_newline([only one should exist, so please delete some or all + of these files and re-run \"$srcdir/configure\"]) + ])) + ]) + stampfile=yes + ]) + done ;; esac - SAGE_PACKAGE_VERSIONS+="vers_$SPKG_NAME = $SPKG_VERSION"$'\n' + # Trac #29629: Temporary solution for Sage 9.1: Do not advertise installing pip packages + # using ./configure --enable-SPKG + if test -f "$DIR/requirements.txt"; then + message="$SPKG_TYPE pip package; use \"./sage -i $SPKG_NAME\" to install" + uninstall_message="$SPKG_TYPE pip package (installed)" + fi + + SAGE_PACKAGE_VERSIONS="${SAGE_PACKAGE_VERSIONS}$(printf '\nvers_')${SPKG_NAME} = ${SPKG_VERSION}" - if test "$SPKG_NAME" != "$SPKG_VERSION"; then AS_VAR_PUSHDEF([sage_spkg_install], [sage_spkg_install_${SPKG_NAME}])dnl AS_VAR_PUSHDEF([sage_require], [sage_require_${SPKG_NAME}])dnl AS_VAR_PUSHDEF([sage_use_system], [sage_use_system_${SPKG_NAME}])dnl @@ -192,17 +210,19 @@ for DIR in $SAGE_ROOT/build/pkgs/*; do # "./sage -i SPKG_NAME" will still install the package. AS_VAR_IF([sage_spkg_install], [no], [ dnl We will use the system package (or not required for this platform.) - SAGE_DUMMY_PACKAGES+=" $SPKG_NAME \\"$'\n' + SAGE_DUMMY_PACKAGES="${SAGE_DUMMY_PACKAGES} \\$(printf '\n ')${SPKG_NAME}" AS_VAR_IF([sage_require], [yes], [ message="using system package; SPKG will not be installed" ], [ message="not required on your platform; SPKG will not be installed" ]) ], [ dnl We won't use the system package. - SAGE_BUILT_PACKAGES+=" $SPKG_NAME \\"$'\n' + SAGE_BUILT_PACKAGES="${SAGE_BUILT_PACKAGES} \\$(printf '\n ')${SPKG_NAME}" AS_VAR_SET_IF([sage_use_system], [ AS_VAR_COPY([reason], [sage_use_system]) AS_CASE([$reason], - [yes], [ message="no suitable system package; $message" ], + [yes], [ message="no suitable system package; $message" + AS_VAR_APPEND([SAGE_NEED_SYSTEM_PACKAGES], [" $SPKG_NAME"]) + ], [installed], [ message="already installed as an SPKG$uninstall_message" ], [ message="$reason; $message" ]) ], [ @@ -216,54 +236,76 @@ for DIR in $SAGE_ROOT/build/pkgs/*; do AS_VAR_POPDEF([sage_use_system])dnl AS_VAR_POPDEF([sage_require])dnl AS_VAR_POPDEF([sage_spkg_install])dnl - fi # Packages that should be included in the source distribution # This includes all standard packages and two special cases case "$SPKG_NAME" in - mpir|python2) + mpir) in_sdist=yes ;; esac + # Determine package source + # + if test -f "$DIR/requirements.txt"; then + SPKG_SOURCE=pip + # Since pip packages are downloaded and installed by pip, we don't + # include them in the source tarball. At the time of this writing, + # all pip packages are optional. + in_sdist=no + elif test ! -f "$DIR/checksums.ini"; then + SPKG_SOURCE=script + # We assume that either (a) the sources for an optional script + # package will be downloaded by the script, or (b) that a + # standard script package's sources are already a part of the + # sage repository (and thus the release tarball). As a result, + # we don't need to download the sources, which is what + # "in_sdist" really means. At the time of this writing, the + # only standard script packages are sage_conf and sagelib. + # The source of sage_conf is included under build/pkgs/sage_conf/src, + # and the source of sagelib is provided by symlinks in + # build/pkgs/sagelib/src. + in_sdist=no + else + SPKG_SOURCE=normal + fi + if test "$in_sdist" = yes; then - SAGE_SDIST_PACKAGES+=" $SPKG_NAME \\"$'\n' + SAGE_SDIST_PACKAGES="${SAGE_SDIST_PACKAGES} \\$(printf '\n ')${SPKG_NAME}" fi # Determine package dependencies - DEP_FILE="$SAGE_ROOT/build/pkgs/$SPKG_NAME/dependencies" + # + DEP_FILE="$DIR/dependencies" if test -f "$DEP_FILE"; then # - the # symbol is treated as comment which is removed DEPS=`sed 's/^ *//; s/ *#.*//; q' $DEP_FILE` else - case "$SPKG_TYPE" in - optional) - DEPS=' | $(STANDARD_PACKAGES)' # default for optional packages - ;; - script) - DEPS=' | $(STANDARD_PACKAGES)' # default for script-only packages - ;; + ORDER_ONLY_DEPS="" + case "$SPKG_SOURCE" in pip) - DEPS=' | pip' - ;; - *) - DEPS="" + ORDER_ONLY_DEPS='pip' ;; esac + if test -n "$ORDER_ONLY_DEPS"; then + DEPS="| $ORDER_ONLY_DEPS" + else + DEPS="" + fi fi - SAGE_PACKAGE_DEPENDENCIES+="deps_$SPKG_NAME = $DEPS"$'\n' + SAGE_PACKAGE_DEPENDENCIES="${SAGE_PACKAGE_DEPENDENCIES}$(printf '\ndeps_')${SPKG_NAME} = ${DEPS}" # Determine package build rules - case "$SPKG_TYPE" in + case "$SPKG_SOURCE" in pip) - SAGE_PIP_PACKAGES+=" $SPKG_NAME \\"$'\n' + SAGE_PIP_PACKAGES="${SAGE_PIP_PACKAGES} \\$(printf '\n ')${SPKG_NAME}" ;; script) - SAGE_SCRIPT_PACKAGES+=" $SPKG_NAME \\"$'\n' + SAGE_SCRIPT_PACKAGES="${SAGE_SCRIPT_PACKAGES} \\$(printf '\n ')${SPKG_NAME}" ;; - *) - SAGE_NORMAL_PACKAGES+=" $SPKG_NAME \\"$'\n' + normal) + SAGE_NORMAL_PACKAGES="${SAGE_NORMAL_PACKAGES} \\$(printf '\n ')${SPKG_NAME}" ;; esac done @@ -280,3 +322,25 @@ AC_SUBST([SAGE_OPTIONAL_INSTALLED_PACKAGES]) AC_SUBST([SAGE_OPTIONAL_CLEANED_PACKAGES]) AC_SUBST([SAGE_SDIST_PACKAGES]) ]) + +AC_DEFUN([SAGE_SYSTEM_PACKAGE_NOTICE], [ + AS_IF([test -n "$SAGE_NEED_SYSTEM_PACKAGES"], [ + AC_MSG_NOTICE([notice: the following SPKGs did not find equivalent system packages:$SAGE_NEED_SYSTEM_PACKAGES]) + AC_MSG_CHECKING([for the package system in use]) + SYSTEM=$(build/bin/sage-guess-package-system 2>& AS_MESSAGE_FD) + AC_MSG_RESULT([$SYSTEM]) + AS_IF([test $SYSTEM != unknown], [ + SYSTEM_PACKAGES=$(build/bin/sage-get-system-packages $SYSTEM $SAGE_NEED_SYSTEM_PACKAGES) + AS_IF([test -n "$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([After installation, re-run configure using:]) + AC_MSG_NOTICE([ \$ ./config.status --recheck && ./config.status]) + ], [ + AC_MSG_NOTICE([No equivalent system packages for $SYSTEM are known to Sage]) + ]) + ]) + ]) +]) diff --git a/m4/sage_spkg_configure.m4 b/m4/sage_spkg_configure.m4 index 53fd1bd9d62..d8b64f831fb 100644 --- a/m4/sage_spkg_configure.m4 +++ b/m4/sage_spkg_configure.m4 @@ -159,7 +159,7 @@ AC_DEFUN([SAGE_SPKG_CONFIGURE], [ AC_DEFUN([SAGE_SPKG_DEPCHECK], [ m4_foreach_w([DEP], $1, [ AC_REQUIRE([SAGE_SPKG_CONFIGURE_]m4_toupper(DEP))]) - AC_MSG_CHECKING([whether any of $1 is installed or will be installed as SPKG]) + AC_MSG_CHECKING([whether any of $1 is installed as or will be installed as SPKG]) AS_IF([test x = y m4_foreach_w([DEP], $1, [ -o [x$sage_spkg_install_]DEP = xyes])], [ AC_MSG_RESULT([yes; install SPKG_NAME as well]) [sage_spkg_install_]SPKG_NAME=yes], [ diff --git a/m4/sage_spkg_enable.m4 b/m4/sage_spkg_enable.m4 index 7d4e23008e6..c349aab5f9a 100644 --- a/m4/sage_spkg_enable.m4 +++ b/m4/sage_spkg_enable.m4 @@ -10,13 +10,20 @@ AS_HELP_STRING([--disable-]SPKG_NAME, AS_VAR_SET(want_spkg, [$enableval]), AS_VAR_SET(want_spkg, [if_installed]) ) - AS_IF([test -r "$SAGE_SPKG_INST/]SPKG_NAME["-*], - [AS_VAR_SET([is_installed], [yes])], - [AS_VAR_SET([is_installed], [no])]) + + AS_VAR_SET([is_installed], [no]) + for f in "$SAGE_SPKG_INST/SPKG_NAME"-*; do + AS_IF([test -r "$f"], + [AS_VAR_SET([is_installed], [yes])]) + done + AS_IF([test "$want_spkg" = if_installed], [AS_VAR_SET([want_spkg], $is_installed)]) - AS_VAR_SET([spkg_line], [" ]SPKG_NAME[ \\"$'\n']) + + spkg_line=" \\$(printf '\n ')SPKG_NAME" AS_CASE([$is_installed-$want_spkg], [*-yes], [AS_VAR_APPEND(SAGE_OPTIONAL_INSTALLED_PACKAGES, "$spkg_line")], [yes-no], [AS_VAR_APPEND(SAGE_OPTIONAL_CLEANED_PACKAGES, "$spkg_line")]) + m4_popdef([SPKG_TYPE]) + m4_popdef([SPKG_NAME]) ]) diff --git a/sage b/sage index 837f3e6866f..3fad670c839 100755 --- a/sage +++ b/sage @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # # Sage: a free open-source mathematics software system # @@ -25,7 +25,11 @@ # Resolve all symbolic links in a filename. This more or less behaves # like "readlink -f" except that it does not convert the filename to an # absolute path (a relative path remains relative), nor does it treat -# "." or ".." specially. See Trac ticket #5852. +# "." or ".." specially. +# +# WARNING: this function is copy/pasted from src/bin/sage-env, and +# deserves to be factored out at some point in the future. In the +# meantime however the two should be kept synchronized. resolvelinks() { # $in is what still needs to be converted (normally has no starting slash) in="$1" @@ -35,12 +39,10 @@ resolvelinks() { # Move stuff from $in to $out while [ -n "$in" ]; do # Normalize $in by replacing consecutive slashes by one slash - while { in_single_slash=${in//\/\//\/}; [ "$in" != "$in_single_slash" ]; }; do - in=$in_single_slash - done + in=$(echo "${in}" | sed 's://*:/:g') # If $in starts with a slash, remove it and set $out to the root - in_without_slash=${in/#\//} + in_without_slash=${in#/} if [ "$in" != "$in_without_slash" ]; then in=$in_without_slash out="/" @@ -62,7 +64,7 @@ resolvelinks() { out="$out$f" # If the new $in starts with a slash, move it to $out - in_without_slash=${in/#\//} + in_without_slash=${in#/} if [ "$in" != "$in_without_slash" ]; then in=$in_without_slash out="$out/" @@ -94,7 +96,8 @@ resolvelinks() { fi # In $in, replace $f by $f_resolved (leave $out alone) - in=${in/#"$f"/"$f_resolved"} + in="${in#${f}}" + in="${f_resolved}${in}" done # Return $out diff --git a/src/Makefile.in b/src/Makefile.in deleted file mode 100644 index b296775710e..00000000000 --- a/src/Makefile.in +++ /dev/null @@ -1,50 +0,0 @@ -# @configure_input@ - -abs_builddir = @abs_builddir@ -srcdir = @srcdir@ -abs_top_srcdir = @abs_top_srcdir@ - -.PHONY: all sage clean - -all: sage - -## All sagelib-building is done by setup.py. -## This is so that sagelib can be installed by standard Python procedures, -## such as "./setup.py install" or "pip install ." -## BECAUSE OF THIS, DON'T ADD ADDITIONAL STEPS TO THIS MAKEFILE. - -## We poison all environment variables that have paths to the sage source and build directories, -## but keep the directories below SAGE_LOCAL intact. -## In this way we make sure that all of the sagelib build's source paths are communicated through -## the current directory (for the source tree). -## Building takes places in the build/ subdirectory. -## -## As a special exception, we feed SAGE_PKGS. This is needed by src/sage_setup/optional_extension.py -## via src/sage/misc/package.py. Hoping that #20382 will make this unnecessary. -## -## TODO: Do also something about the SAGE_LOCAL stuff: -## - some of it (our install) should be communicated only through --install-base; -## - others (installed packages that we pull in) through some configuration mechanism. -## -## TODO: Currently providing --install-base=$(SAGE_LOCAL) leads to this error: -## [sagelib-7.4.beta4] running install -## [sagelib-7.4.beta4] error: install-base or install-platbase supplied, but installation scheme is incomplete -sage: - cd $(srcdir) && export \ - SAGE_ROOT=/doesnotexist \ - SAGE_SRC=/doesnotexist \ - SAGE_SRC_ROOT=/doesnotexist \ - SAGE_DOC_SRC=/doesnotexist \ - SAGE_BUILD_DIR=/doesnotexist \ - SAGE_PKGS=$(abs_top_srcdir)/build/pkgs \ - && sage-python -u setup.py --no-user-cfg build install - if [ "$$UNAME" = "CYGWIN" ]; then \ - sage-rebase.sh "$$SAGE_LOCAL" 2>/dev/null; \ - fi - -clean: - @echo "Deleting Sage library build artifacts..." - rm -rf c_lib .cython_version # from old sage versions - rm -rf build - find . -name '*.pyc' | xargs rm -f - rm -rf sage/ext/interpreters diff --git a/src/bin/sage b/src/bin/sage index e6191249547..ce325f5d039 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -24,30 +24,21 @@ usage() { echo echo "Optional arguments:" echo " file.[sage|py|spyx] -- run given .sage, .py or .spyx file" - echo " -advanced -- list all command line options" + echo " --advanced -- list all command line options" echo " -c -- Evaluates cmd as sage code" - echo " -experimental -- list all experimental packages that can be installed" - echo " -gap [...] -- run Sage's Gap with given arguments" - echo " -gap3 [...] -- run Sage's Gap3 with given arguments" - echo " -gp [...] -- run Sage's PARI/GP calculator with given arguments" - echo " -h, -? -- print this help message" - echo " -i [packages] -- install the given Sage packages" - echo " -pip [...] -- invoke pip, the Python package manager" - echo " -inotebook [...] -- start the *insecure* Sage notebook (deprecated)" - echo " -maxima [...] -- run Sage's Maxima with given arguments" - echo " -mwrank [...] -- run Sage's mwrank with given arguments" + echo " --gap [...] -- run Sage's Gap with given arguments" + echo " --gp [...] -- run Sage's PARI/GP calculator with given arguments" + echo " -h -- print this help message" + echo " --pip [...] -- invoke pip, the Python package manager" + echo " --maxima [...] -- run Sage's Maxima with given arguments" + echo " --mwrank [...] -- run Sage's mwrank with given arguments" echo " --notebook=[...] -- start the Sage notebook (valid options are" - echo " 'default', 'sagenb', 'jupyter', and 'export')" + echo " 'default', 'jupyter', and 'export')" echo " Current default is 'export' from sagenb to jupyter" echo " -n, --notebook -- shortcut for --notebook=default" - echo " -optional -- list all optional packages that can be installed" - echo " -python [...] -- run the Python interpreter" - echo " -python2 [...] -- run the Python 2 interpreter" - echo " -python3 [...] -- run the Python 3 interpreter" + echo " --python [...], --python3 [...] -- run the Python 3 interpreter" echo " -R [...] -- run Sage's R with given arguments" - echo " -singular [...] -- run Sage's singular with given arguments" - echo " -sqlite3 [...] -- run Sage's sqlite3 with given arguments" - echo " -root -- print the Sage root directory" + echo " --singular [...] -- run Sage's singular with given arguments" echo " --nodotsage -- run Sage without using the user's .sage directory:" echo " create and use a temporary .sage directory instead" echo " -t [options] <--all|files|dir>" @@ -56,235 +47,16 @@ usage() { echo " --long - include lines with the phrase 'long time'" echo " --verbose - print debugging output during the test" echo " --optional - controls which optional tests are run" - echo " --sagenb - test all sagenb files" echo " --help - show all testing options" - echo " -upgrade [version] -- download, build and install the given version. Here," - echo " 'version' is a git branch or tag name. Useful values" - echo " are 'master' (the current development version, this" - echo " is the default) or a version number like '5.13'." - echo " -v, -version -- display Sage version information" - exit 0 -} - -usage_advanced() { - sage_version -v - #### 1.......................26..................................................78 - #### |.....................--.|...................................................| - echo - echo "Running Sage:" - echo " file.[sage|py|spyx] -- run given .sage, .py or .spyx file" - echo " -advanced -- print this advanced help message" - echo " -c -- Evaluates cmd as sage code" - echo " -h, -? -- print short help message" - echo " -min [...] -- do not populate global namespace (must be first" - echo " option)" - echo " -preparse -- preparse file.sage and produce corresponding file.sage.py" - echo " -q -- quiet; start with no banner" - echo " -root -- print the Sage root directory" - echo " -gthread, -qthread, -q4thread, -wthread, -pylab" - echo " -- pass the option through to ipython" - echo " -v, -version -- display Sage version information" - echo " -dumpversion -- print Sage version" - echo " -git-branch -- print the current git branch" - - echo - #### 1.......................26..................................................78 - #### |.....................--.|...................................................| - echo "Running the notebook:" - echo " --notebook=[...] -- start the Sage notebook (valid options are" - echo " 'default', 'sagenb', 'jupyter' and 'export')." - echo " Current default is 'export' sagenb to jupyter." - echo " See the output of sage --notebook --help" - echo " for more details and examples of how to pass" - echo " optional arguments" - echo " -bn, -build-and-notebook [...] -- build the Sage library then start" - echo " the Sage notebook" - echo " -inotebook [...] -- start the *insecure* Sage notebook (deprecated)" - echo " -n, -notebook [...] -- start the default Sage notebook (options are the" - echo " same as for the notebook command in Sage). See the" - echo " output of sage -n -h for more details" - - echo - #### 1.......................26..................................................78 - #### |.....................--.|...................................................| - echo "Running external programs:" - echo " -cleaner -- run the Sage cleaner" - echo " -cython [...] -- run Cython with given arguments" - echo " -ecl [...] -- run Common Lisp" - echo " -gap [...] -- run Sage's Gap with given arguments" - echo " -gap3 [...] -- run Sage's Gap3 with given arguments" - echo " -gdb -- run Sage under the control of gdb" - echo " -gp [...] -- run Sage's PARI/GP calculator with given arguments" - echo " -ipython [...] -- run Sage's IPython using the default environment (not" - echo " Sage), passing additional options to IPython" - echo " -ipython3 [...] -- same as above, but using Python 3" - echo " -jupyter [...] -- run Sage's Jupyter with given arguments" - echo " -kash [...] -- run Sage's Kash with given arguments" - command -v kash &>/dev/null || \ - echo " (not installed currently, run sage -i kash)" - echo " -lisp [...] -- run Lisp interpreter included with Sage" - echo " -M2 [...] -- run Sage's Macaulay2 with given arguments" - command -v M2 &>/dev/null || \ - echo " (not installed currently, run sage -i macaulay2)" - echo " -maxima [...] -- run Sage's Maxima with given arguments" - echo " -mwrank [...] -- run Sage's mwrank with given arguments" - echo " -polymake [...] -- run Sage's Polymake with given arguments" - command -v polymake &>/dev/null || \ - echo " (not installed currently, run sage -i polymake)" - echo " -python [...] -- run the Python interpreter" - echo " -python2 [...] -- run the Python 2 interpreter" - echo " -python3 [...] -- run the Python 3 interpreter" - echo " -R [...] -- run Sage's R with given arguments" - echo " -scons [...] -- run Sage's scons" - echo " -sh [...] -- run \$SHELL ($SHELL) with Sage environment variables" - echo " -singular [...] -- run Sage's singular with given arguments" - echo " -sqlite3 [...] -- run Sage's sqlite3 with given arguments" - echo " -twistd [...] -- run Twisted server" - - echo - #### 1.......................26..................................................78 - #### |.....................--.|...................................................| - echo "Installing packages and upgrading:" - echo " -package [args] -- call the new package manager with given arguments." - echo " Run without arguments for package-specific help." - echo " -experimental -- list all experimental packages that can be installed" - echo " -f [opts] [packages]-- shortcut for -i -f: force build of the given Sage" - echo " packages" - echo " -i [opts] [packages]-- install the given Sage packages. Options:" - echo " -c -- run the packages' test suites" - echo " -d -- only download, do not install packages" - echo " -f -- force build: install the packages even" - echo " if they are already installed" - echo " -s -- do not delete the temporary build directories" - echo " after a successful build" - echo " -y -- reply yes to prompts about experimental" - echo " and old-style packages; warning: there" - echo " is no guarantee that these packages will" - echo " build correctly; use at your own risk" - echo " -n -- reply no to prompts about experimental" - echo " and old-style packages" - echo " -p [opts] [packages]-- install the given Sage packages, without dependency" - echo " checking and with support for old-style spkgs." - echo " Options are -c, -d and -s with the same meaning as" - echo " for the -i command" - echo " -info [packages] -- print the SPKG.txt of the given packages" - echo " --location -- if needed, fix paths to make Sage relocatable" - echo " -optional -- list all optional packages that can be installed" - echo " -standard -- list all standard packages that can be installed" - echo " -installed -- list all installed packages" - echo " -upgrade [version] -- download, build and install the given version. Here," - echo " 'version' is a git branch or tag name. Useful values" - echo " are 'master' (the current development version, this" - echo " is the default) or a version number like '5.13'." - echo " -pip [...] -- invoke pip, the Python package manager" - - echo - #### 1.......................26..................................................78 - #### |.....................--.|...................................................| - echo "Building and testing the Sage library:" - echo " -b -- build Sage library." - echo " -ba -- same as -b and rebuild all Cython code" - echo " -ba-force -- same as -ba, but don't query before rebuilding" - echo " -br -- build and run Sage" - echo " -bt [...] -- build and test, options like -t below" - echo " -btp [...] -- build and test parallel, options like -tp below" - echo " -btnew [...] -- build and test modified files, options like -tnew" - echo " -fixdoctests [output_file] [--long]" - echo " -- replace failing doctests with the actual output. With" - echo " optional output_file: redirect there. With the --long" - echo " option: include #long time tests." - echo " -startuptime [module] -- display how long each component of Sage takes to" - echo " start up; optionally specify a module to get more" - echo " details about that particular module" - echo " -t [options] <--all|files|dir>" - echo " -- test examples in .py, .pyx, .sage, .tex or .rst files" - echo " selected options:" - echo " --long - include lines with the phrase 'long time'" - echo " --verbose - print debugging output during the test" - echo " --all - test all files" - echo " --sagenb - test all sagenb files" - echo " --optional - controls which optional tests are run" - echo " --new - only test files modified since last commit" - echo " --initial - only show the first failure per block" - echo " --debug - drop into PDB after an unexpected error" - echo " --failed - only test files that failed last test" - echo " --warn-long [timeout] - warning if doctest is slow" - echo " --only-errors - only output failures, not successes" - echo " --gc=GC - control garbarge collection (ALWAYS:" - echo " collect garbage before every test; NEVER:" - echo " disable gc; DEFAULT: Python default)" - echo " --help - show all testing options" - echo " -tp [...] -- like -t above, but tests in parallel using N threads" - echo " with 0 interpreted as a sensible default" - echo " -testall [options] -- test all source files, docs, and examples. options" - echo " like -t" - - echo - #### 1.......................26..................................................78 - #### |.....................--.|...................................................| - echo "Documentation:" - echo " -coverage -- give info about doctest coverage of files" - echo " -coverageall -- give summary info about doctest coverage of all" - echo " files in the Sage library" - echo " -docbuild [lang/] -- Build the Sage documentation" - echo " -search_src -- search through all the Sage library code for string" - echo " -search_doc -- search through the Sage documentation for string" - echo " -grep -- same as -search_src" - echo " -grepdoc -- same as -search_doc" - - echo - #### 1.......................26..................................................78 - #### |.....................--.|...................................................| - echo "File conversion:" - echo " -rst2ipynb [...] -- Generates Jupyter notebook (.ipynb) from standalone" - echo " reStructuredText source." - command -v rst2ipynb &>/dev/null || \ - echo " (not installed currently, run sage -i rst2ipynb)" - echo " -ipynb2rst [...] -- Generates a reStructuredText source file from" - echo " a Jupyter notebook (.ipynb)." - echo " -rst2txt [...] -- Generates Sage worksheet text file from standalone" - echo " reStructuredText source." - echo " -rst2sws [...] -- Generates Sage worksheet (.sws) from standalone" - echo " reStructuredText source." - echo " -sws2rst -- Generates a reStructuredText source file from" - echo " a Sage worksheet (.sws) document." - - echo - #### 1.......................26..................................................78 - #### |.....................--.|...................................................| - echo "Making Sage packages or distributions:" - echo " -sdist -- build a source distribution of Sage" - echo " -pkg -- create Sage package dir.spkg from a given directory" - echo " -pkg_nc -- as -pkg, but do not compress the package" - echo " -fix-pkg-checksums -- fix the checksums from build/pkgs directories from " - echo " the packages located in upstream/" - - echo - #### 1.......................26..................................................78 - #### |.....................--.|...................................................| - echo "Valgrind memory debugging:" - echo " -cachegrind -- run Sage using Valgrind's cachegrind tool. The log" - echo " files are named sage-cachegrind.PID can be found in" - echo " $DOT_SAGE" - echo " -callgrind -- run Sage using Valgrind's callgrind tool. The log" - echo " files are named sage-callgrind.PID can be found in" - echo " $DOT_SAGE" - echo " -massif -- run Sage using Valgrind's massif tool. The log" - echo " files are named sage-massif.PID can be found in" - echo " $DOT_SAGE" - echo " -memcheck -- run Sage using Valgrind's memcheck tool. The log" - echo " files are named sage-memcheck.PID can be found in" - echo " $DOT_SAGE" - echo " -omega -- run Sage using Valgrind's omega tool. The log" - echo " files are named sage-omega.PID can be found in" - echo " $DOT_SAGE" - echo " -valgrind -- this is an alias for -memcheck" - echo - echo "You can also use -- before a long option, e.g., 'sage --optional'." - echo + echo " -v, --version -- display Sage version information" + if [ -n "$SAGE_ROOT" ]; then + exec "$SAGE_ROOT/build/bin/sage-site" "-h" + fi exit 0 } +# 'usage_advanced', which prints a longer help message, is defined +# below, after sourcing sage-env. ##################################################################### # Special options to be processed without sage-env @@ -358,6 +130,11 @@ if [ "$1" = '-i' ]; then echo >&2 "Error: 'sage -i $OPT ' is no longer supported, use 'sage --info ' instead." exit 2;; -f) FORCE_INSTALL=yes;; + # Setting SAGE_CHECK here duplicates what we do in sage-spkg + # but we need it in "make" already when there are (order-only) + # dependencies on packages providing test infrastructure + -c) INSTALL_OPTIONS="$INSTALL_OPTIONS $OPT"; export SAGE_CHECK=yes;; + -w) INSTALL_OPTIONS="$INSTALL_OPTIONS $OPT"; export SAGE_CHECK=warn;; -*) INSTALL_OPTIONS="$INSTALL_OPTIONS $OPT";; *) PACKAGES="$PACKAGES $OPT";; esac @@ -379,23 +156,21 @@ if [ "$1" = '-i' ]; then # Now install the packages for PKG in $PACKAGES; do echo - # First check that $PKG is actually a Makefile target + # Check that $PKG is actually a Makefile target # See https://trac.sagemath.org/ticket/25078 if ! echo "$ALL_TARGETS" | grep "^${PKG}$" >/dev/null; then echo >&2 "Error: package '$PKG' not found" - echo >&2 "Note: if it is an old-style package, use -p instead of -i to install it" + echo >&2 "Note: if it is an old-style package, installing these is no longer supported" exit 1 fi $MAKE SAGE_SPKG="sage-spkg $INSTALL_OPTIONS" "$PKG" done - echo "New packages may have been installed." echo "Re-running configure and make in case any dependent packages need updating." touch "$SAGE_ROOT/configure" && $MAKE all-build exit 0 fi - ##################################################################### # Report information about the Sage environment ##################################################################### @@ -415,22 +190,14 @@ if [ "$1" = '-root' -o "$1" = '--root' ]; then exit 0 fi -if [ $# -gt 0 ]; then - if [ "$1" = '-h' -o "$1" = '-?' -o "$1" = '-help' -o "$1" = '--help' ]; then - usage - fi - if [ "$1" = "-advanced" -o "$1" = "--advanced" ]; then - usage_advanced - fi -fi - ##################################################################### # Source sage-env ($0 is the name of this "sage" script, so we can just # append -env to that). We redirect stdout to stderr, which is safer # for scripts. ##################################################################### -if [ -f "$0-env" ]; then +if [ -f "$0-env-config" ] && [ -f "$0-env" ]; then + . "$0-env-config" >&2 . "$0-env" >&2 if [ $? -ne 0 ]; then echo >&2 "Error setting environment variables by sourcing '$0-env';" @@ -443,6 +210,11 @@ if [ -z "$DOT_SAGE" ]; then export DOT_SAGE="$HOME/.sage" fi + +##################################################################### +# Helper functions +##################################################################### + # Prepare for running Sage, either interactively or non-interactively. sage_setup() { # Check that we're not in a source tarball which hasn't been built yet (#13561). @@ -489,6 +261,207 @@ interactive_sage() { exec sage-ipython "$@" -i } +##################################################################### +# sage --advanced +##################################################################### + +usage_advanced() { + sage_version -v + #### 1.......................26..................................................78 + #### |.....................--.|...................................................| + echo + echo "Running Sage, the most common options:" + echo + echo " file.[sage|py|spyx] -- run given .sage, .py or .spyx file" + echo " -h, -?, --help -- print a short help message" + echo " -v, --version -- print the Sage version" + echo " --advanced -- print this list of Sage options" + echo " -c cmd -- evaluate cmd as sage code. For example," + echo " \"sage -c 'print(factor(35))'\" will" + echo " print \"5 * 7\"." + echo + echo "Running Sage, other options:" + echo + echo " --dumpversion -- print brief Sage version" + echo " --preparse file.sage -- preparse \"file.sage\", and produce" + echo " the corresponding Python file" + echo " \"file.sage.py\"" + echo " -q -- quiet; start with no banner" + echo " --min -- do not populate global namespace" + echo " (must be first option)" + echo " --nodotsage -- run Sage without using the user's" + echo " .sage directory: create and use a temporary" + echo " .sage directory instead." + echo " --gthread, --qthread, --q4thread, --wthread, --pylab" + echo " -- pass the option through to IPython" + if [ -n "$SAGE_SRC" -a -d "$SAGE_SRC" ]; then + echo " --grep [options] " + echo " -- regular expression search through the Sage" + echo " library for \"string\". Any options will" + echo " get passed to the \"grep\" command." + echo " --grepdoc [options] " + echo " -- regular expression search through the" + echo " Sage documentation for \"string\"." + echo " --search_src ... -- same as --grep" + echo " --search_doc ... -- same as --grepdoc" + fi + echo + echo "Running external programs:" + echo + echo " --cython [...] -- run Cython with the given arguments" + echo " --ecl [...], --lisp [...] -- run Sage's copy of ECL (Embeddable" + echo " Common Lisp) with the given arguments" + echo " --gap [...] -- run Sage's Gap with the given arguments" + echo " --gap3 [...] -- run Sage's Gap3 with the given arguments" + command -v gap3 &>/dev/null || \ + echo " (not installed currently, run sage -i gap3)" + echo " --gdb -- run Sage under the control of gdb" + echo " --gdb-ipython -- run Sage's IPython under the control of gdb" + echo " --git [...] -- run Sage's Git with the given arguments" + echo " --gp [...] -- run Sage's PARI/GP calculator with the" + echo " given arguments" + echo " --ipython [...], --ipython3 [...]" + echo " -- run Sage's IPython using the default" + echo " environment (not Sage), passing additional" + echo " additional options to IPython" + echo " --jupyter [...] -- run Sage's Jupyter with given arguments" + echo " --kash [...] -- run Sage's Kash with the given arguments" + command -v kash &>/dev/null || \ + echo " (not installed currently, run sage -i kash)" + echo " --M2 [...] -- run Sage's Macaulay2 with the given arguments" + command -v M2 &>/dev/null || \ + echo " (not installed currently, run sage -i macaulay2)" + echo " --maxima [...] -- run Sage's Maxima with the given arguments" + echo " --mwrank [...] -- run Sage's mwrank with the given arguments" + echo " --pip [...] -- invoke pip, the Python package manager" + echo " --polymake [...] -- run Sage's Polymake with given arguments" + command -v polymake &>/dev/null || \ + echo " (not installed currently, run sage -i polymake)" + echo " --python [...], --python3 [...]" + echo " -- run the Python 3 interpreter" + echo " -R [...] -- run Sage's R with the given arguments" + echo " --singular [...] -- run Sage's singular with the given arguments" + echo " --sqlite3 [...] -- run Sage's sqlite3 with given arguments" + echo + echo "Running the notebook:" + echo + echo " -n [...], --notebook=[...]" + echo " -- start the notebook; valid options" + echo " include \"default\", \"jupyter\", \"export\"." + echo " Current default is \"jupyter\"." + echo " Run \"sage --notebook --help\" for more details." + echo + #### 1.......................26..................................................78 + #### |.....................--.|...................................................| + echo "Testing files:" + echo + echo " -t [options] -- test examples in .py, .pyx, .sage" + echo " or .tex files. Options:" + echo " --long -- include lines with the phrase 'long time'" + echo " --verbose -- print debugging output during the test" + echo " --all -- test all files" + echo " --optional -- also test all examples labeled \"# optional\"" + echo " --only-optional[=tags]" + echo " -- if no 'tags' are specified, only run" + echo " blocks of tests containing a line labeled" + echo " \"# optional\". If a comma-separated" + echo " list of tags is specified, only run block" + echo " containing a line labeled \"# optional tag\"" + echo " for any of the tags given, and in these blocks" + echo " only run the lines which are unlabeled or" + echo " labeled \"# optional\" or labeled" + echo " \"# optional tag\" for any of the tags given." + echo " --randorder[=seed] -- randomize order of tests" + echo " --random-seed[=seed] -- random seed for fuzzing doctests" + echo " --new -- only test files modified since last commit" + echo " --initial -- only show the first failure per block" + echo " --debug -- drop into PDB after an unexpected error" + echo " --failed -- only test files that failed last test" + echo " --warn-long [timeout] -- warning if doctest is slow" + echo " --only-errors -- only output failures, not successes" + echo " --gc=GC -- control garbarge collection (ALWAYS:" + echo " collect garbage before every test; NEVER:" + echo " disable gc; DEFAULT: Python default)" + echo " --short[=secs] -- run as many doctests as possible in about 300" + echo " seconds (or the number of seconds given.) This runs" + echo " the tests for each module from the top of the file" + echo " and skips tests once it exceeds the budget" + echo " allocated for that file." + echo " --help -- show all testing options" + echo " --tnew [...] -- equivalent to -t --new" + echo " -tp [...] -- like -t above, but tests in parallel using" + echo " N threads, with 0 interpreted as min(8, cpu_count())" + echo " --testall [options] -- equivalent to -t --all" + echo + echo " --coverage -- give information about doctest coverage of files" + echo " --coverageall -- give summary info about doctest coverage of" + echo " all files in the Sage library" + echo " --startuptime [module] -- display how long each component of Sage takes to" + echo " start up; optionally specify a module to get more" + echo " details about that particular module" + echo + echo "Some developer utilities:" + echo + echo " --sh [...] -- run a shell with Sage environment variables" + echo " as they are set in the runtime of Sage" + echo " --cleaner -- run the Sage cleaner. This cleans up after Sage," + echo " removing temporary directories and spawned processes." + echo " (This gets run by Sage automatically, so it is usually" + echo " not necessary to run it separately.)" + #### 1.......................26..................................................78 + #### |.....................--.|...................................................| + echo "File conversion:" + echo + echo " --rst2ipynb [...] -- Generates Jupyter notebook (.ipynb) from standalone" + echo " reStructuredText source." + command -v rst2ipynb &>/dev/null || \ + echo " (not installed currently, run sage -i rst2ipynb)" + echo " --ipynb2rst [...] -- Generates a reStructuredText source file from" + echo " a Jupyter notebook (.ipynb)." + echo " --rst2txt [...] -- Generates Sage worksheet text file from standalone" + echo " reStructuredText source." + echo + #### 1.......................26..................................................78 + #### |.....................--.|...................................................| + echo "Valgrind memory debugging:" + echo + echo " --cachegrind -- run Sage using Valgrind's cachegrind tool. The log" + echo " files are named sage-cachegrind.PID can be found in" + echo " \$DOT_SAGE" + echo " --callgrind -- run Sage using Valgrind's callgrind tool. The log" + echo " files are named sage-callgrind.PID can be found in" + echo " \$DOT_SAGE" + echo " --massif -- run Sage using Valgrind's massif tool. The log" + echo " files are named sage-massif.PID can be found in" + echo " \$DOT_SAGE" + echo " --memcheck -- run Sage using Valgrind's memcheck tool. The log" + echo " files are named sage-memcheck.PID can be found in" + echo " \$DOT_SAGE" + echo " --omega -- run Sage using Valgrind's omega tool. The log" + echo " files are named sage-omega.PID can be found in" + echo " \$DOT_SAGE" + echo " --valgrind -- this is an alias for --memcheck" + if [ -n "$SAGE_ROOT" ]; then + exec "$SAGE_ROOT/build/bin/sage-site" "--advanced" + fi + echo + exit 0 +} + +if [ $# -gt 0 ]; then + if [ "$1" = '-h' -o "$1" = '-?' -o "$1" = '-help' -o "$1" = '--help' ]; then + usage + fi + if [ "$1" = "-advanced" -o "$1" = "--advanced" ]; then + usage_advanced + fi +fi + + +##################################################################### +# Running Sage +##################################################################### + if [ "$1" = '-min' -o "$1" = '--min' ]; then shift export SAGE_IMPORTALL=no @@ -503,151 +476,141 @@ if [ $# -eq 0 ]; then interactive_sage fi -if [ "$1" = '-cleaner' -o "$1" = '--cleaner' ]; then - exec sage-cleaner -fi - ##################################################################### -# Run Sage's versions of the standard Algebra/Geometry etc. software +# Other basic options ##################################################################### -if [ "$1" = '-axiom' -o "$1" = '--axiom' ]; then - shift - exec axiom "$@" -fi - -if [ "$1" = '-gap' -o "$1" = '--gap' ]; then +if [ "$1" = '-c' ]; then shift - exec gap "$@" + sage_setup + unset TERM # See Trac #12263 + exec sage-eval "$@" fi -if [ "$1" = '-gap3' -o "$1" = '--gap3' ]; then +if [ "$1" = '-preparse' -o "$1" = "--preparse" ]; then shift - exec gap3 "$@" + exec sage-preparse "$@" fi -if [ "$1" = '-gp' -o "$1" = '--gp' ]; then - shift - exec gp "$@" +if [ "$1" = '-cleaner' -o "$1" = '--cleaner' ]; then + exec sage-cleaner fi -if [ "$1" = '-polymake' -o "$1" = '--polymake' ]; then - shift - exec polymake "$@" -fi +##################################################################### +# Run Sage's versions of Python, pip, IPython, Jupyter. +##################################################################### -if [ "$1" = '-singular' -o "$1" = '--singular' ]; then +if [ "$1" = '-pip' -o "$1" = '--pip' ]; then shift - exec Singular "$@" + exec sage-python -m pip "$@" fi -if [ "$1" = '-sqlite3' -o "$1" = '--sqlite3' ]; then +if [ "$1" = '--pip3' ]; then shift - exec sqlite3 "$@" + exec "$SAGE_LOCAL"/bin/python3 -m pip "$@" fi -if [ "$1" = '-sws2rst' -o "$1" = '--sws2rst' ]; then +if [ "$1" = '-python' -o "$1" = '--python' ]; then shift - exec sage-sws2rst "$@" + if [ "$SAGE_PYTHON3" = 'yes' ]; then + exec "$SAGE_LOCAL"/bin/python3 "$@" + else + exec "$SAGE_LOCAL"/bin/python2 "$@" + fi fi -if [ "$1" = '-twistd' -o "$1" = '--twistd' ]; then +if [ "$1" = '-python3' -o "$1" = '--python3' ]; then shift - exec twistd "$@" + exec "$SAGE_LOCAL"/bin/python3 "$@" fi -if [ "$1" = '-ecl' -o "$1" = '--ecl' ]; then +if [ "$1" = '-ipython' -o "$1" = '--ipython' ]; then shift - exec ecl "$@" + exec "$SAGE_LOCAL"/bin/ipython "$@" fi -if [ "$1" = '-lisp' -o "$1" = '--lisp' ]; then +if [ "$1" = '-ipython3' -o "$1" = '--ipython3' ]; then shift - exec ecl "$@" + exec "$SAGE_LOCAL"/bin/ipython3 "$@" fi -if [ "$1" = '-kash' -o "$1" = '--kash' ]; then +if [ "$1" = '-jupyter' -o "$1" = '--jupyter' ]; then shift - exec kash "$@" + exec "$SAGE_LOCAL"/bin/jupyter "$@" fi -if [ "$1" = '-fixdoctests' -o "$1" = '--fixdoctests' ]; then - shift - exec sage-fixdoctests "$@" -fi +##################################################################### +# Run Sage's versions of its component packages +##################################################################### -if [ "$1" = '-maxima' -o "$1" = '--maxima' ]; then +if [ "$1" = "-cython" -o "$1" = '--cython' -o "$1" = '-pyrex' -o "$1" = "--pyrex" ]; then shift - exec maxima "$@" + exec cython "$@" fi -if [ "$1" = '-mwrank' -o "$1" = '--mwrank' ]; then +if [ "$1" = '-gap' -o "$1" = '--gap' ]; then shift - exec mwrank "$@" + exec gap "$@" fi -if [ "$1" = '-M2' -o "$1" = '--M2' ]; then +if [ "$1" = '-gap3' -o "$1" = '--gap3' ]; then shift - exec M2 "$@" + exec gap3 "$@" fi -if [ "$1" = '-scons' -o "$1" = '--scons' ]; then +if [ "$1" = '-gp' -o "$1" = '--gp' ]; then shift - exec scons "$@" + exec gp "$@" fi -if [ "$1" = '-pip' -o "$1" = '--pip' ]; then +if [ "$1" = '-polymake' -o "$1" = '--polymake' ]; then shift - exec sage-python -m pip "$@" + exec polymake "$@" fi -if [ "$1" = '--pip3' ]; then +if [ "$1" = '-singular' -o "$1" = '--singular' ]; then shift - exec python3 -m pip "$@" + exec Singular "$@" fi -if [ "$1" = '-fix-pkg-checksums' -o "$1" = '--fix-pkg-checksums' ]; then +if [ "$1" = '-sqlite3' -o "$1" = '--sqlite3' ]; then shift - exec sage-fix-pkg-checksums "$@" + exec sqlite3 "$@" fi -if [ "$1" = '-python' -o "$1" = '--python' ]; then +if [ "$1" = '-ecl' -o "$1" = '--ecl' ]; then shift - if [ "$SAGE_PYTHON3" = 'yes' ]; then - exec python3 "$@" - else - exec python2 "$@" - fi + exec ecl "$@" fi -if [ "$1" = '-python2' -o "$1" = '--python2' ]; then +if [ "$1" = '-lisp' -o "$1" = '--lisp' ]; then shift - exec python2 "$@" + exec ecl "$@" fi -if [ "$1" = '-python3' -o "$1" = '--python3' ]; then +if [ "$1" = '-kash' -o "$1" = '--kash' ]; then shift - exec python3 "$@" + exec kash "$@" fi -if [ "$1" = '-R' -o "$1" = '--R' ]; then +if [ "$1" = '-maxima' -o "$1" = '--maxima' ]; then shift - exec R "$@" + exec maxima "$@" fi -if [ "$1" = '-ipython' -o "$1" = '--ipython' ]; then +if [ "$1" = '-mwrank' -o "$1" = '--mwrank' ]; then shift - exec ipython "$@" + exec mwrank "$@" fi -if [ "$1" = '-ipython3' -o "$1" = '--ipython3' ]; then +if [ "$1" = '-M2' -o "$1" = '--M2' ]; then shift - exec ipython3 "$@" + exec M2 "$@" fi -if [ "$1" = '-jupyter' -o "$1" = '--jupyter' ]; then +if [ "$1" = '-R' -o "$1" = '--R' ]; then shift - exec jupyter "$@" + exec R "$@" fi if [ "$1" = '-git' -o "$1" = '--git' ]; then @@ -655,18 +618,30 @@ if [ "$1" = '-git' -o "$1" = '--git' ]; then exec git "$@" fi -if [ "$1" = '-git-branch' -o "$1" = '--git-branch' ]; then - shift - exec git --git-dir="$SAGE_ROOT"/.git rev-parse --abbrev-ref HEAD -fi +##################################################################### +# sage --sh and sage --buildsh +##################################################################### -if [ "$1" = '-sh' -o "$1" = '--sh' ]; then +if [ "$1" = '-sh' -o "$1" = '--sh' -o "$1" = '-buildsh' -o "$1" = '--buildsh' ]; then # AUTHORS: # - Carl Witty and William Stein: initial version # - Craig Citro: add options for not loading profile # - Martin Albrecht: fix zshell prompt (#11866) # - John Palmieri: shorten the prompts, and don't print messages if # there are more arguments to 'sage -sh' (#11790) + if [ -z "$SAGE_SHPROMPT_PREFIX" ]; then + SAGE_SHPROMPT_PREFIX=sage-sh + fi + if [ "$1" = '-buildsh' -o "$1" = '--buildsh' ]; then + if [ ! -r "$SAGE_ROOT"/build/bin/sage-build-env-config ]; then + echo "error: '$SAGE_ROOT' does not contain build/bin/sage-build-env-config. Run configure first." + exit 1 + fi + . "$SAGE_ROOT"/build/bin/sage-build-env-config || (echo "error: Error sourcing $SAGE_ROOT/build/bin/sage-build-env-config"; exit 1) + export SAGE_SHPROMPT_PREFIX=sage-buildsh + # We export it so that recursive invocation of 'sage-sh' from a sage-buildsh shows the sage-buildsh prompt; + # this makes sense because all environment variables set in build/bin/sage-build-env-config are exported. + fi shift # If $SHELL is unset, default to bash if [ -z "$SHELL" ]; then @@ -693,9 +668,9 @@ if [ "$1" = '-sh' -o "$1" = '--sh' ]; then bash) SHELL_OPTS="--norc" if [ "$color_prompt" = yes ]; then - PS1="\[$(tput rev)\](sage-sh)\[$(tput sgr0)\] \u@\h:\W\$ " + PS1="\[$(tput rev)\]($SAGE_SHPROMPT_PREFIX)\[$(tput sgr0)\] \u@\h:\W\$ " else - PS1="(sage-sh) \u@\h:\w\$ " + PS1="($SAGE_SHPROMPT_PREFIX) \u@\h:\w\$ " fi export PS1 ;; @@ -708,9 +683,9 @@ if [ "$1" = '-sh' -o "$1" = '--sh' ]; then ksh) SHELL_OPTS="-p" if [ "$color_prompt" = yes ] ; then - PS1="$(tput rev)(sage-sh)$(tput sgr0) $USER@`hostname -s`:\${PWD##*/}$ " + PS1="$(tput rev)($SAGE_SHPROMPT_PREFIX)$(tput sgr0) $USER@`hostname -s`:\${PWD##*/}$ " else - PS1="(sage-sh) $USER@`hostname -s`:\${PWD##*/}$ " + PS1="($SAGE_SHPROMPT_PREFIX) $USER@`hostname -s`:\${PWD##*/}$ " fi export PS1 ;; @@ -719,9 +694,9 @@ if [ "$1" = '-sh' -o "$1" = '--sh' ]; then # bash, but this is not guaranteed), so we don't set # SHELL_OPTS. if [ "$color_prompt" = yes ] ; then - PS1="$(tput rev)(sage-sh)$(tput sgr0) $USER@`hostname -s`:\${PWD##*/}$ " + PS1="$(tput rev)($SAGE_SHPROMPT_PREFIX)$(tput sgr0) $USER@`hostname -s`:\${PWD##*/}$ " else - PS1="(sage-sh) $USER@`hostname -s`:\${PWD}$ " + PS1="($SAGE_SHPROMPT_PREFIX) $USER@`hostname -s`:\${PWD}$ " fi export PS1 ;; @@ -732,7 +707,7 @@ if [ "$1" = '-sh' -o "$1" = '--sh' ]; then SHELL_OPTS="-f" ;; zsh) - PS1="%S(sage-sh)%s %n@%m:%~$ " + PS1="%S($SAGE_SHPROMPT_PREFIX)%s %n@%m:%~$ " # In zsh, the system /etc/zshenv is *always* run, # and this may change the path (like on OSX), so we'll # create a temporary .zshenv to reset the path @@ -744,7 +719,7 @@ EOF export PS1 ;; *) - export PS1='(sage-sh) $ ' + export PS1='($SAGE_SHPROMPT_PREFIX) $ ' ;; esac if [ -n "$oldPS1" ]; then @@ -778,20 +753,6 @@ EOF exit $status fi -##################################################################### -# Test coverage of a module? -##################################################################### - -if [ "$1" = "-coverage" -o "$1" = "--coverage" ]; then - shift - exec sage-coverage "$@" -fi - -if [ "$1" = "-coverageall" -o "$1" = "--coverageall" ]; then - shift - exec sage-coverageall "$@" -fi - ##################################################################### # File conversion ##################################################################### @@ -814,18 +775,16 @@ if [ "$1" = '-rst2txt' -o "$1" = '--rst2txt' ]; then exec sage-rst2txt "$@" fi -if [ "$1" = '-rst2sws' -o "$1" = '--rst2sws' ]; then - shift - exec sage-rst2sws "$@" -fi - ##################################################################### -# Run Sage's versions of the standard Algebra/Geometry etc. software +# The notebook, grep, building Sage, testing Sage ##################################################################### +# build_sage, sage -b, sage -br, etc. could be moved to +# build/bin/sage-site. See #29111. + build_sage() { maybe_sage_location - ( cd "$SAGE_SRC" && $MAKE ) || exit $? + ( cd "$SAGE_ROOT/build/make" && $MAKE sagelib-no-deps ) || exit $? } if [[ "$1" =~ ^--notebook=.* || "$1" =~ ^-n=.* || "$1" =~ ^-notebook=.* ]] ; then @@ -845,22 +804,20 @@ if [ "$1" = "-bn" -o "$1" = "--build-and-notebook" ]; then exec sage-notebook --notebook=default "$@" fi -if [ "$1" = "-inotebook" -o "$1" = '--inotebook' ]; then - shift - sage-cleaner &>/dev/null & - exec sage-notebook --notebook=sagenb secure=False "$@" -fi - -if [ "$1" = '-grep' -o "$1" = "--grep" -o "$1" = "-search_src" -o "$1" = "--search_src" ]; then - shift - sage-grep "$@" - exit 0 -fi +if [ -n "$SAGE_SRC" -a -d "$SAGE_SRC" ]; then + # Source inspection facilities, supported on sage-the-distribution and on distributions + # that package the Sage sources. + if [ "$1" = '-grep' -o "$1" = "--grep" -o "$1" = "-search_src" -o "$1" = "--search_src" ]; then + shift + sage-grep "$@" + exit 0 + fi -if [ "$1" = '-grepdoc' -o "$1" = "--grepdoc" -o "$1" = "-search_doc" -o "$1" = "--search_doc" ]; then - shift - sage-grepdoc "$@" - exit 0 + if [ "$1" = '-grepdoc' -o "$1" = "--grepdoc" -o "$1" = "-search_doc" -o "$1" = "--search_doc" ]; then + shift + sage-grepdoc "$@" + exit 0 + fi fi if [ "$1" = '-b' ]; then @@ -878,8 +835,17 @@ if [ "$1" = '-r' ]; then interactive_sage fi -if [ "$1" = '-ba' -o "$1" = '-ba-force' -o "$1" = '--ba-force' ]; then - ( cd "$SAGE_SRC" && make clean ) +if [ "$1" = '-ba-force' -o "$1" = '--ba-force' ]; then + echo + echo "WARNING: 'sage --ba-force' is deprecated; use 'sage -ba' instead." + echo + ( cd "$SAGE_ROOT/build/make" && make sagelib-clean ) + build_sage + exit $? +fi + +if [ "$1" = '-ba' ]; then + ( cd "$SAGE_ROOT/build/make" && make sagelib-clean ) build_sage exit $? fi @@ -916,13 +882,31 @@ if [ "$1" = '-testall' -o "$1" = "--testall" ]; then exec sage-runtests -a "$@" fi -if [ "$1" = '-c' ]; then +if [ "$1" = '-fixdoctests' -o "$1" = '--fixdoctests' ]; then shift - sage_setup - unset TERM # See Trac #12263 - exec sage-eval "$@" + exec sage-fixdoctests "$@" fi +if [ "$1" = "-coverage" -o "$1" = "--coverage" ]; then + shift + exec sage-coverage "$@" +fi + +if [ "$1" = "-coverageall" -o "$1" = "--coverageall" ]; then + shift + exec sage-coverageall "$@" +fi + +if [ "$1" = '-startuptime' -o "$1" = '--startuptime' ]; then + exec sage-startuptime.py "$@" +fi + +##################################################################### +# Creating and handling Sage distributions +##################################################################### + +# The following could be moved to build/bin/sage-site. See #29111. + if [ "$1" = '--location' ]; then maybe_sage_location exit 0 @@ -1006,38 +990,15 @@ if [ "$1" = '-info' -o "$1" = '--info' ]; then exit 0 fi -if [ "$1" = '-pkg' -o "$1" = '-spkg' -o "$1" = "--pkg" -o "$1" = "--spkg" ]; then - shift - exec sage-pkg "$@" -fi - -if [ "$1" = '-pkg_nc' -o "$1" = "--pkg_nc" ]; then - shift - exec sage-pkg -n "$@" -fi - if [ "$1" = '-sdist' -o "$1" = "--sdist" ]; then maybe_sage_location shift exec sage-sdist "$@" fi -if [ "$1" = '-rsyncdist' -o "$1" = "--rsyncdist" ]; then - if [ $# -ne 2 ]; then - echo >&2 "** MISSING VERSION NUMBER! **" - exit 2 - fi - maybe_sage_location - exec sage-rsyncdist $2 -fi - -if [ "$1" = "-docbuild" -o "$1" = "--docbuild" ]; then - # Redirect stdin from /dev/null. This helps with running TeX which - # tends to ask interactive questions if something goes wrong. These - # cause the build to hang. If stdin is /dev/null, TeX just aborts. - shift - exec sage-python -m sage_setup.docbuild "$@" &2 + return 3 +fi # Resolve all symbolic links in a filename. This more or less behaves # like "readlink -f" except that it does not convert the filename to an @@ -44,12 +53,10 @@ resolvelinks() { # Move stuff from $in to $out while [ -n "$in" ]; do # Normalize $in by replacing consecutive slashes by one slash - while { in_single_slash=${in//\/\//\/}; [ "$in" != "$in_single_slash" ]; }; do - in=$in_single_slash - done + in=$(echo "${in}" | sed 's://*:/:g') # If $in starts with a slash, remove it and set $out to the root - in_without_slash=${in/#\//} + in_without_slash=${in#/} if [ "$in" != "$in_without_slash" ]; then in=$in_without_slash out="/" @@ -71,7 +78,7 @@ resolvelinks() { out="$out$f" # If the new $in starts with a slash, move it to $out - in_without_slash=${in/#\//} + in_without_slash=${in#/} if [ "$in" != "$in_without_slash" ]; then in=$in_without_slash out="$out/" @@ -103,14 +110,14 @@ resolvelinks() { fi # In $in, replace $f by $f_resolved (leave $out alone) - in=${in/#"$f"/"$f_resolved"} + in="${in#${f}}" + in="${f_resolved}${in}" done # Return $out echo "$out" } - # New value for SAGE_ROOT: either SAGE_ROOT (if given) # or a guessed value based on pwd. if [ -n "$SAGE_ROOT" ]; then @@ -120,8 +127,8 @@ elif [ -f sage -a -d build ]; then elif [ -f ../../sage -a -d ../../build ]; then NEW_SAGE_ROOT="../.." else - # No idea what SAGE_ROOT should be... - echo >&2 "Error: You must set the SAGE_ROOT environment variable or run this" + # No idea what SAGE_ROOT should be... it should have been set by sage-env-config. + echo >&2 "Error: You must set the SAGE_ROOT or SAGE_SCRIPTS_DIR environment variables or run this" echo >&2 "script from the SAGE_ROOT or SAGE_ROOT/local/bin/ directory." return 1 fi @@ -186,13 +193,6 @@ elif [ ! -f "$SAGE_SCRIPTS_DIR/sage-env-config" ]; then return 1 fi -# Set environment variables (like SAGE_LOCAL) depending on ./configure -. "$SAGE_SCRIPTS_DIR/sage-env-config" -if [ $? -ne 0 ]; then - echo >&2 "Error: failed to source $SAGE_SCRIPTS_DIR/sage-env-config" - return 1 -fi - # The compilers are set in order of priority by # 1) environment variables # 2) compiler installed by sage @@ -227,6 +227,10 @@ if [ "$UNAME" = "Darwin" ]; then export OBJC OBJCXX fi +if [ "$SAGE_PYTHON_VERSION" = 3 ]; then + export SAGE_PYTHON3=yes +fi + # Set other Fortran-related compiler variables export F77="$FC" export F90="$FC" # Needed for SciPy @@ -283,15 +287,18 @@ fi # depending on SAGE_ROOT and SAGE_LOCAL which are already defined. export SAGE_ETC="$SAGE_LOCAL/etc" export SAGE_SHARE="$SAGE_LOCAL/share" -export SAGE_EXTCODE="$SAGE_SHARE/sage/ext" -export SAGE_PKGCONFIG="$SAGE_LOCAL/lib/pkgconfig" export SAGE_SPKG_INST="$SAGE_LOCAL/var/lib/sage/installed" -export SAGE_SPKG_SCRIPTS="$SAGE_LOCAL/var/lib/sage/scripts" export SAGE_LOGS="$SAGE_ROOT/logs/pkgs" export SAGE_SRC="$SAGE_ROOT/src" export SAGE_DOC_SRC="$SAGE_SRC/doc" export SAGE_DOC="$SAGE_SHARE/doc/sage" +if [ -n "$SAGE_PKG_CONFIG_PATH" ]; then + # set up external pkg-config to look into SAGE_LOCAL/lib/pkgconfig/ + # (Sage's pkgconf spkg takes care of this, if installed) + export PKG_CONFIG_PATH="$SAGE_PKG_CONFIG_PATH${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" +fi + if [ -z "${SAGE_ORIG_PATH_SET}" ]; then SAGE_ORIG_PATH=$PATH && export SAGE_ORIG_PATH SAGE_ORIG_PATH_SET=True && export SAGE_ORIG_PATH_SET @@ -313,6 +320,8 @@ export UNAME=`uname | sed 's/CYGWIN.*/CYGWIN/' ` # Mac OS X-specific setup if [ "$UNAME" = "Darwin" ]; then + # Avoid 'MACOSX_DEPLOYMENT_TARGET mismatch: now "10.9" but "10.15" during configure' + if [ -z "$PYTHON_FOR_VENV" ]; then # set MACOSX_DEPLOYMENT_TARGET -- if set incorrectly, this can # cause lots of random "Abort trap" issues on OSX. see trac # #7095 for an example. @@ -325,7 +334,7 @@ if [ "$UNAME" = "Darwin" ]; then MACOSX_DEPLOYMENT_TARGET=10.$[$MACOSX_VERSION-4] fi export MACOSX_DEPLOYMENT_TARGET MACOSX_VERSION - + fi # Work around problems on recent OS X crashing with an error message # "... may have been in progress in another thread when fork() was called" # when objective-C functions are called after fork(). See Trac #25921. @@ -335,13 +344,13 @@ fi # Compile-time path for libraries. This is the equivalent of # adding the gcc option -L $SAGE_LOCAL/lib. -[ -z "$LIBRARY_PATH" ] || LIBRARY_PATH="${LIBRARY_PATH}:" -export LIBRARY_PATH="${LIBRARY_PATH}$SAGE_LOCAL/lib" +[ -z "$LIBRARY_PATH" ] || LIBRARY_PATH=":${LIBRARY_PATH}" +export LIBRARY_PATH="$SAGE_LOCAL/lib${LIBRARY_PATH}" # Compile-time path for include files. This is the equivalent of # adding the gcc option -I $SAGE_LOCAL/include. -[ -z "$CPATH" ] || CPATH="${CPATH}:" -export CPATH="${CPATH}$SAGE_LOCAL/include" +[ -z "$CPATH" ] || CPATH=":${CPATH}" +export CPATH="$SAGE_LOCAL/include${CPATH}" SINGULARPATH="$SAGE_LOCAL/share/singular" && export SINGULARPATH SINGULAR_EXECUTABLE="$SAGE_LOCAL/bin/Singular" && export SINGULAR_EXECUTABLE @@ -412,6 +421,9 @@ if [ -n "$PYTHONHOME" ]; then fi LDFLAGS="-L$SAGE_LOCAL/lib -Wl,-rpath,$SAGE_LOCAL/lib $LDFLAGS" +if [ "$UNAME" = "Linux" ]; then + LDFLAGS="-Wl,-rpath-link,$SAGE_LOCAL/lib $LDFLAGS" +fi export LDFLAGS if [ -z "$IPYTHONDIR" ]; then @@ -449,7 +461,7 @@ unset R_PROFILE if [ -d "$SAGE_LOCAL/lib/R/share" ] ; then R_MAKEVARS_SITE="$SAGE_LOCAL/lib/R/share/Makevars.site" && export R_MAKEVARS_SITE if ! [ -f "$R_MAKEVARS_SITE" ] ; then - if ! [ -a "$R_MAKEVARS_SITE" ] ; then + if ! [ -e "$R_MAKEVARS_SITE" ] ; then echo "## Empty site-wide Makevars file for Sage's R" > "$R_MAKEVARS_SITE" else >&2 echo "Warning: $R_MAKEVARS_SITE exists and is not a file : trouble ahead..." @@ -458,7 +470,7 @@ if [ -d "$SAGE_LOCAL/lib/R/share" ] ; then fi if [ -d "$DOT_SAGE" ] ; then if ! [ -d "$DOT_SAGE/R" ] ; then - if ! [ -a "$DOT_SAGE/R" ] ; then + if ! [ -e "$DOT_SAGE/R" ] ; then mkdir -p "$DOT_SAGE/R" else >&2 echo "Warning: $DOT_SAGE/R exists and is not a directory : trouble ahead..." @@ -466,7 +478,7 @@ if [ -d "$DOT_SAGE" ] ; then fi R_MAKEVARS_USER="$DOT_SAGE/R/Makevars.user" && export R_MAKEVARS_USER if ! [ -f "$R_MAKEVARS_USER" ] ; then - if ! [ -a "$R_MAKEVARS_USER" ] ; then + if ! [ -e "$R_MAKEVARS_USER" ] ; then echo "## Empty user-specific Makevars file for Sage's R" > "$R_MAKEVARS_USER" else >&2 echo "Warning: $R_MAKEVARS_USER exists and is not a file : trouble ahead..." @@ -544,11 +556,6 @@ if [ -n "$CFLAGS" -a -z "$CXXFLAGS" ]; then export CXXFLAGS="$CFLAGS" fi -if [ "$UNAME" = "Darwin" ]; then - # Trac #21175 - export empty ARCHFLAGS for the benefit of Perl modules - ARCHFLAGS="" && export ARCHFLAGS -fi - if [ "$CP" = "" ]; then CP="cp" && export CP fi @@ -594,9 +601,10 @@ ECLDIR="$SAGE_LOCAL/lib/ecl/" && export ECLDIR # First, figure out the right values for SAGE_NUM_THREADS (default # number of threads) and SAGE_NUM_THREADS_PARALLEL (default number of # threads when parallel execution is asked explicitly). -sage_num_threads_array=(`sage-num-threads.py 2>/dev/null || echo 1 2 1`) -SAGE_NUM_THREADS=${sage_num_threads_array[0]} -SAGE_NUM_THREADS_PARALLEL=${sage_num_threads_array[1]} +sage_num_threads_array=$(sage-num-threads.py 2>/dev/null || echo 1 2 1) +sage_num_threads_array="${sage_num_threads_array% *}" # strip third item +SAGE_NUM_THREADS="${sage_num_threads_array% *}" # keep first item +SAGE_NUM_THREADS_PARALLEL="${sage_num_threads_array#* }" # keep second item export SAGE_NUM_THREADS export SAGE_NUM_THREADS_PARALLEL diff --git a/src/bin/sage-env-config.in b/src/bin/sage-env-config.in index 4e177c53395..7872a930dc9 100644 --- a/src/bin/sage-env-config.in +++ b/src/bin/sage-env-config.in @@ -1,113 +1,55 @@ -# -*- shell-script -*- +# -*- shell-script -*- @configure_input@ ########################################################################### # -# Set some environment variables for Sage. -# This file is generated from sage-env-config.in by configure. +# Set some environment variables that are needed at the runtime of Sage +# and by its child processes. # # NOTES: # - You must *source* this script instead of executing. # - Use "return" instead of "exit" to signal a failure. Since this # file is sourced, an "exit" here will actually exit src/bin/sage, # which is probably not intended. -# - All environment variables set here should be *exported*, otherwise -# they won't be available in child programs. +# - Environment variables that should be available in the Sage environment +# should be exported. +# - This file is only for setting immediate values. Any kind of conditionals +# or computed values are to be set by src/bin/sage-env after sourcing this +# file. +# - Environment variables that are only needed at the time of building +# SPKGs or sagelib should be set in build/bin/sage-build-env-config +# instead. +# - Configuration variables that are only needed by the Sage runtime, +# but not as environment variables, should instead be set in +# build/pkgs/sage_conf/src/sage_conf.py # ########################################################################## +export SAGE_ENV_CONFIG_SOURCED=1 + # SAGE_LOCAL is the installation prefix and can be customized by using # ./configure --prefix export SAGE_LOCAL="@prefix@" +# SAGE_ROOT is the location of the Sage source/build tree. +export SAGE_ROOT="@SAGE_ROOT@" + ####################################### -# Compilers set at configuration time +# Compilers set at configuration time. +# We do not export these variables; sage-env sets CC etc. based on them. ####################################### -export CONFIGURED_CC="@CC@" -export CONFIGURED_CXX="@CXX@" -export CONFIGURED_FC="@FC@" -export CONFIGURED_OBJC="@OBJC@" -export CONFIGURED_OBJCXX="@OBJCXX@" +CONFIGURED_CC="@CC@" +CONFIGURED_CXX="@CXX@" +CONFIGURED_FC="@FC@" +CONFIGURED_OBJC="@OBJC@" +CONFIGURED_OBJCXX="@OBJCXX@" ####################################### -# Other configuration +# Other configuration (exported) ####################################### export SAGE_PYTHON_VERSION=@SAGE_PYTHON_VERSION@ -if [ "$SAGE_PYTHON_VERSION" = 3 ]; then - export SAGE_PYTHON3=yes -fi - -# This is usually blank if the system GMP is used, or $SAGE_LOCAL otherwise -export SAGE_GMP_PREFIX="@SAGE_GMP_PREFIX@" -export SAGE_GMP_INCLUDE="@SAGE_GMP_INCLUDE@" -if [ -n "$SAGE_GMP_PREFIX" ]; then - # Many packages that depend on GMP accept a --with-gmp= flag to - # their ./configure scripts. When using the system's GMP this is not - # generally necessary, but when using the GMP package installed in - # SAGE_LOCAL it is useful to pass it. We define this variable to - # pass to these packages' ./configure scripts. When using the system - # GMP its value is just blank (for many of these packages passing - # --with-gmp without an argument is actually a bug) - export SAGE_CONFIGURE_GMP="--with-gmp=$SAGE_GMP_PREFIX" -fi - -# The MPFR case is very close to the GMP case above -# This is usually blank if the system MPFR is used, or $SAGE_LOCAL otherwise -export SAGE_MPFR_PREFIX="@SAGE_MPFR_PREFIX@" -if [ -n "$SAGE_MPFR_PREFIX" ]; then - # Some packages that depend on MPFR accept a --with-mpfr= flag to - # their ./configure scripts. Thus we deal with this just as with GMP above. - export SAGE_CONFIGURE_MPFR="--with-mpfr=$SAGE_MPFR_PREFIX" -fi - -# The MPC case is very close to the MPFR case above -# This is usually blank if the system MPC is used, or $SAGE_LOCAL otherwise -export SAGE_MPC_PREFIX="@SAGE_MPC_PREFIX@" -if [ -n "$SAGE_MPC_PREFIX" ]; then - # Some packages that depend on MPC accept a --with-mpc= flag to - # their ./configure scripts. Thus we deal with this just as with GMP above. - export SAGE_CONFIGURE_MPC="--with-mpc=$SAGE_MPC_PREFIX" -fi - -# Location of system crti.o, in case we build our own gcc -export SAGE_CRTI_DIR="@SAGE_CRTI_DIR@" - -# This is usually blank if the system NTL is used, or $SAGE_LOCAL otherwise -export SAGE_NTL_PREFIX="@SAGE_NTL_PREFIX@" -if [ -n "$SAGE_NTL_PREFIX" ]; then - # Many packages that depend on NTL accept a --with-ntl= flag to - # their ./configure scripts. When using the system's NTL this is not - # generally necessary, but when using the NTL package installed in - # SAGE_LOCAL it is useful to pass it. - export SAGE_CONFIGURE_NTL="--with-ntl=$SAGE_NTL_PREFIX" -fi +export PYTHON_FOR_VENV="@PYTHON_FOR_VENV@" -# The FLINT case is very close to the MPFR case above -# This is usually blank if the system FLINT is used, or $SAGE_LOCAL otherwise -export SAGE_FLINT_PREFIX="@SAGE_FLINT_PREFIX@" -if [ -n "$SAGE_FLINT_PREFIX" ]; then - # Some packages that depend on FLINT accept a --with-flint= flag to - # their ./configure scripts. Thus we deal with this just as with GMP above. - export SAGE_CONFIGURE_FLINT="--with-flint=$SAGE_FLINT_PREFIX" -fi - -# This is usually blank if the system PARI is used, or $SAGE_LOCAL otherwise -export SAGE_PARI_PREFIX="@SAGE_PARI_PREFIX@" -if [ -n "$SAGE_PARI_PREFIX" ]; then - # Some packages that depend on PARI accept a --with-pari= flag to - # their ./configure scripts. Thus we deal with this just as with GMP above. - export SAGE_CONFIGURE_PARI="--with-pari=$SAGE_PARI_PREFIX" -fi -export SAGE_PARI_CFG="@SAGE_PARI_CFG@" - -export SAGE_GLPK_PREFIX="@SAGE_GLPK_PREFIX@" -export SAGE_FREETYPE_PREFIX="@SAGE_FREETYPE_PREFIX@" -export SAGE_ARB_LIBRARY="@SAGE_ARB_LIBRARY@" - -export SAGE_PKG_CONFIG_PATH="@SAGE_PKG_CONFIG_PATH@" -if [ -n "$SAGE_PKG_CONFIG_PATH" ]; then - # set up external pkg-config to look into SAGE_LOCAL/lib/pkgconfig/ - # (Sage's pkgconf spkg takes care of this, if installed) - export PKG_CONFIG_PATH="$SAGE_PKG_CONFIG_PATH${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" -fi - -export SAGE_NAUTY_BINS_PREFIX="@SAGE_NAUTY_BINS_PREFIX@" +####################################### +# Other configuration (not exported, only used in sage-env) +####################################### +SAGE_PKG_CONFIG_PATH="@SAGE_PKG_CONFIG_PATH@" diff --git a/src/bin/sage-fix-pkg-checksums b/src/bin/sage-fix-pkg-checksums deleted file mode 100755 index 34a12b60cd8..00000000000 --- a/src/bin/sage-fix-pkg-checksums +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -echo "Deprecated #19984, use sage --package fix-checksum instead" - -# If any command-line arguments are present, treat them as files to be -# checksummed. If no arguments are present, all tarballs in -# $SAGE_ROOT/upstream are checksummed. -if [ $# -eq 0 ]; then - sage --package fix-checksum -else - for tarball in "$@" ; do - pkg=$(sage --package name $tarball) - sage --package fix-checksum $pkg - done -fi diff --git a/src/bin/sage-grep b/src/bin/sage-grep index d78cfce6a5d..8441fa83519 100755 --- a/src/bin/sage-grep +++ b/src/bin/sage-grep @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh cd "$SAGE_SRC" diff --git a/src/bin/sage-grepdoc b/src/bin/sage-grepdoc index 51cb3218ac7..ec0383178ed 100755 --- a/src/bin/sage-grepdoc +++ b/src/bin/sage-grepdoc @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh cd "$SAGE_DOC" diff --git a/src/bin/sage-inline-fortran b/src/bin/sage-inline-fortran index e0250cb6d9a..e417d5b9a3f 100755 --- a/src/bin/sage-inline-fortran +++ b/src/bin/sage-inline-fortran @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # Compile Fortran code within Sage, # see src/sage/misc/inline_fortran.py diff --git a/src/bin/sage-list-experimental b/src/bin/sage-list-experimental deleted file mode 100755 index b6a5760f939..00000000000 --- a/src/bin/sage-list-experimental +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -echo "DEPRECATION WARNING: this script is deprecated" >&2 -echo "do:" >&2 -echo " $ sage-list-packages experimental" >&2 -exec sage-list-packages experimental diff --git a/src/bin/sage-list-optional b/src/bin/sage-list-optional deleted file mode 100755 index 3fd911e4ea6..00000000000 --- a/src/bin/sage-list-optional +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -echo "DEPRECATION WARNING: this script is deprecated" >&2 -echo "do:" >&2 -echo " $ sage-list-packages optional" >&2 -exec sage-list-packages optional diff --git a/src/bin/sage-list-packages b/src/bin/sage-list-packages index 7a4c211d7ec..f2e9331ca2e 100755 --- a/src/bin/sage-list-packages +++ b/src/bin/sage-list-packages @@ -3,7 +3,7 @@ r""" Script to list the Sage packages This is script can be called with one argument which might be either -"all", "standard", "optional", "experimental" or "pip". It is mostly a +"all", "standard", "optional", or "experimental". It is mostly a script interface to sage_setup.packages.list_packages. """ @@ -23,10 +23,10 @@ from sage.misc.package import list_packages parser = argparse.ArgumentParser(description="List Sage's packages") parser.add_argument('category', choices=['all', 'standard', 'optional', - 'experimental', 'pip', 'installed'], + 'experimental', 'installed'], metavar="category", help="The type of packages. Can be 'all', 'standard', " - "'optional', 'experimental' or 'pip'.") + "'optional', or 'experimental'.") parser.add_argument('--installed-only', dest='installed_only', default=False, action='store_true', help='only display installed packages') @@ -83,7 +83,7 @@ else: if args['category'] == 'all': L = list(list_packages(local=True, ignore_URLError=True).values()) elif args['category'] == 'optional': - L = list(list_packages('optional', 'pip', local=args['local'], ignore_URLError=True).values()) + L = list(list_packages('optional', local=args['local'], ignore_URLError=True).values()) else: L = list(list_packages(args['category'], local=args['local'], ignore_URLError=True).values()) diff --git a/src/bin/sage-list-standard b/src/bin/sage-list-standard deleted file mode 100755 index a8bbd66af46..00000000000 --- a/src/bin/sage-list-standard +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -echo "DEPRECATION WARNING: this script is deprecated" >&2 -echo "do:" >&2 -echo " $ sage-list-packages standard" >&2 -sage-list-packages standard diff --git a/src/bin/sage-location b/src/bin/sage-location index bdb914683e3..64681300cea 100755 --- a/src/bin/sage-location +++ b/src/bin/sage-location @@ -1,4 +1,7 @@ -#!/usr/bin/env sage-system-python +#!/usr/bin/env sage-python + +# This script is executed by $SAGE_LOCAL/bin/sage +# before starting sage. from __future__ import print_function diff --git a/src/bin/sage-massif b/src/bin/sage-massif index 45ffd02307f..083831d9293 100755 --- a/src/bin/sage-massif +++ b/src/bin/sage-massif @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # We reuse the gdb pythonstartup script. PYTHONSTARTUP=`dirname $0`/sage-ipython diff --git a/src/bin/sage-notebook b/src/bin/sage-notebook index d54a6ba91a2..889341d8b00 100755 --- a/src/bin/sage-notebook +++ b/src/bin/sage-notebook @@ -13,66 +13,6 @@ logger = logging.getLogger() from sage.misc.banner import banner -class NotebookSageNB(object): - - def print_banner(self): - banner() - print('Please wait while the old SageNB Notebook server starts...') - - @classmethod - def print_help(cls): - cls([], help=True) - - def cmdline2argspec(self, cmdline_args): - """ - Convert command line arguments to Python argspec - - AKA the crappy copy of argparse. Only here for the legacy - notebook, do not use. - - INPUT: - - - ``cmdline_args`` -- list of string. - - OUTPUT: - - A python argspec: A pair consisting of a tuple and a dict. - """ - args = [] - kwds = dict() - for x in cmdline_args: - logger.info('Parsing %s', x) - if '=' in x: - key, value = x.split('=', 2) - logger.debug('keyword argument %s = %s', key, value) - try: - value = ast.literal_eval(value) - except Exception: - logger.debug('cannot evaluate, treat as string') - kwds[key] = value - else: - logger.debug('positional argument %s', x) - try: - value = ast.literal_eval(x) - except Exception: - value = x - logger.debug('cannot evaluate, treat as string') - args.append(value) - return tuple(args), kwds - - def __init__(self, argv, help=False): - self.print_banner() - self.args, self.kwds = self.cmdline2argspec(argv) - logger.info('notebook positional arguments = %s', self.args) - logger.info('notebook keyword arguments = %s', self.kwds) - from sagenb.notebook.notebook_object import notebook - if help: - from sage.misc.sageinspect import sage_getdoc - print(sage_getdoc(notebook)) - else: - notebook(*self.args, **self.kwds) - - class NotebookJupyter(object): PREREQUISITE_ERROR = textwrap.dedent(""" @@ -182,8 +122,7 @@ EXAMPLES: notebook_launcher = { - 'default': SageNBExport, # change this to change the default - 'sagenb': NotebookSageNB, + 'default': NotebookJupyter, # change this to change the default 'ipython': NotebookJupyter, 'jupyter': NotebookJupyter, 'jupyterlab': NotebookJupyterlab, @@ -249,10 +188,13 @@ if __name__ == '__main__': logger.info('Main parser got arguments %s', args) logger.info('Passing on to notebook implementation: %s', unknown) - if sys.version_info.major == 3 and args.notebook == "sagenb": - logger.critical('trying to use old notebook under Python 3') - print('old notebook not working under Python 3, use Jupyter notebook') - print('see https://wiki.sagemath.org/Python3-Switch') + if args.notebook == "sagenb": + logger.critical('cannot use the legacy notebook SageNB with Python 3') + print('The legacy notebook does not work under Python 3; ' + 'use the Jupyter notebook.') + print('See https://wiki.sagemath.org/Python3-Switch') + print('Use \"sage --notebook=export\" to export SageNB notebooks ' + 'to Jupyter') sys.exit(1) try: diff --git a/src/bin/sage-omega b/src/bin/sage-omega index dc2a7fc37a9..b0f0f981106 100755 --- a/src/bin/sage-omega +++ b/src/bin/sage-omega @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # We reuse the gdb pythonstartup script. PYTHONSTARTUP=`dirname $0`/sage-ipython diff --git a/src/bin/sage-open b/src/bin/sage-open index 5c829ea0c3e..2bdea59891a 100755 --- a/src/bin/sage-open +++ b/src/bin/sage-open @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # SAGE open command for OS X -- needed since # the SAGE libraries clash with some OS X libraries, diff --git a/src/bin/sage-pkg b/src/bin/sage-pkg deleted file mode 100755 index 57fc655b9de..00000000000 --- a/src/bin/sage-pkg +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env sage-system-python -from __future__ import print_function - -import os -import sys -from subprocess import check_call, CalledProcessError - - -def tar_file(dir, no_compress=False): - """ - Create tar file from ``dir``. If ``no_compress`` is True, don't - compress; otherwise, compress using bzip2. - - If on a Mac, set the environment variable ``COPYFILE_DISABLE`` to - True and use the subprocess module to call tar (to use the new - environment variable). Otherwise, use the tarfile module to - create the tar file. - """ - if sys.platform == 'darwin': - # workaround OS X issue -- see trac #2522 - os.environ['COPYFILE_DISABLE'] = 'true' - if no_compress: - cmd = "tar -cf %s.spkg %s" % (dir, dir) - else: - cmd = "tar -cf - %s | bzip2 > %s.spkg" % (dir, dir) - try: - check_call(cmd, shell=True) - except CalledProcessError: - print("Package creation failed.") - sys.exit(1) - else: - import tarfile - if no_compress: - mode = "w" - else: - mode = "w:bz2" - try: - tar = tarfile.open("%s.spkg" % dir, mode=mode) - tar.add(dir, exclude=lambda f: f == ".DS_Store") - tar.close() - except tarfile.TarError: - print("Package creation failed.") - sys.exit(1) - - -def main(): - import re - from optparse import OptionParser - parser = OptionParser() - parser.add_option("-n", "--no-compress", "--no_compress", "--nocompress", - dest="no_compress", action="store_true", - help="don't compress the spkg file") - (options, args) = parser.parse_args() - compress_txt = "uncompressed " if options.no_compress else "" - for path in args: - print("Creating {1}Sage package from {0}".format(path, - compress_txt)) - - if not os.path.isdir(path): - print("No directory {}".format(path)) - sys.exit(1) - dir = os.path.basename(path.rstrip("/")) - file = dir + ".spkg" - s = dir.split("-", 1) - try: - name = s[0] - version = s[1] - except IndexError: - name = dir - version = "" - - if not version: - print("Warning: no version number detected") - else: - m = re.match(r"[0-9]+[-.0-9a-zA-Z]*(\.p[0-9]+)?$", version) - if not m: - print("Warning: version number ({}) not of the expected form." - .format(version)) - print("""The version number should start with a number and contain only numbers, -letters, periods, and hyphens. It may optionally end with a string of -the form 'pNUM' where NUM is a non-negative integer. - -Proceeding anyway...""") - - tar_file(dir, no_compress=options.no_compress) - - size = os.path.getsize(file) - if size < 1024 * 1024: - size = "{}K".format(size / 1024) - else: - size = "{:.1f}M".format(size / (1024 * 1024.0)) - - if os.path.exists(os.path.join(dir, "SPKG.txt")): - spkg_txt = "Good" - else: - spkg_txt = "File is missing" - - print(""" -Created package {file}. - - NAME: {name} - VERSION: {version} - SIZE: {size} -SPKG.txt: {spkg} - -Please test this package using - - sage -f {file} - -immediately.""".format(file=file, name=name, version=version, size=size, - spkg=spkg_txt), end=' ') - - if options.no_compress: - print() - print() - else: - print(""" Also, note that you can use - - sage -pkg_nc {} - -to make an uncompressed version of the package (useful if the -package is full of compressed data). -""".format(dir)) - - -if __name__ == "__main__": - main() diff --git a/src/bin/sage-python b/src/bin/sage-python index 896386c8015..596a467e242 100755 --- a/src/bin/sage-python +++ b/src/bin/sage-python @@ -1,2 +1,2 @@ -#!/usr/bin/env bash +#!/bin/sh sage -python "$@" diff --git a/src/bin/sage-rst2sws b/src/bin/sage-rst2sws deleted file mode 100755 index 3cbde0e05d5..00000000000 --- a/src/bin/sage-rst2sws +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/env sage-python -r""" -sage-rst2sws -============ - -Translate a rst file into a Sage worksheet file (.sws). - -Usage:: - - sage --rst2sws [options] - -Print the help message:: - - sage --rst2sws -h - -EXAMPLES:: - - sage --rst2sws file.rst file.sws - -In reStructuredText, "an escaped character represents the character -itself, and is prevented from playing a role in any markup -interpretation. The backslash is removed from the output" [1]_ [2]_. So, -if the backslashes of your reST file are not escaped, they will get lost -in the process. This is not practical for Sage since most of the -documentation and examples are Python raw strings where backslashes are -not escaped. Use the following command to escape all backslashes in the -input file:: - - sage --rst2sws --escape-backslashes input.rst output.sws - -AUTHOR: - - - Sebastien Labbe (2011, June 14th, at Sage Days 31): Initial - version - -REFERENCES: - -.. [1] Escaping Mechanism, reStructuredText Markup Specification, - http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#escaping-mechanism -.. [2] Escaping with backslashes, Quick reStructuredText Reference, - http://docutils.sourceforge.net/docs/user/rst/quickref.html#escaping -""" -############################################################################# -# Copyright (C) 2011 Sebastien Labbe -# Distributed under the terms of the GNU General Public License (GPL) -# The full text of the GPL is available at: -# http://www.gnu.org/licenses/ -############################################################################# -import sys -from optparse import OptionParser - -# Set the parser -usage = r""" - - sage -rst2sws [options] - -Generates Sage worksheet (.sws) from standalone reStructuredText source. - -Example: - - sage -rst2sws file.rst file.sws - -Remarks: - - About LaTeX: - - You can put LaTeX expression between backticks "`", dollar signs - "$" or double dollar signs "$$". Math role is not supported yet - (get involved!). - - Examples: `\\alpha`, $\\beta$ or $$\\gamma$$. - - About backslashes: - - In reStructuredText, backslashes must be escaped, otherwise they - are ignored. This is not quite practical for Sage's purposes - since backslashes are never escaped in the docstrings. If you - write `\alpha`, $\beta$ or $$\gamma$$, add the line - - .. escape-backslashes - - to the file and this script will consider all backslashes - escaped. Alternatively, you may use the available options.""" -parser = OptionParser(usage=usage) -parser.add_option("--escape-backslashes", - action="store_true", dest="escape_backslashes", - help="Escape all backslashes in the input file before execution. Default: disabled.") -parser.add_option("--no-escape-backslashes", - action="store_true", dest="no_escape_backslashes", - help="Disable escaping all backslashes in the input file before execution. This overrides the presence of the line '.. escape-backslashes' in the file, which can also be used to escape all backslashes in the file.") -(options, args) = parser.parse_args() - -# Parse arguments -if len(args) != 2: - parser.print_usage() - sys.exit(1) -input = args[0] -output = args[1] - -# Read the ReST input -try: - with open(input, 'r') as f: - rst = f.read() -except IOError as e: - print("IOError: {}".format(e)) - print("Unable to open source file for reading ('{}'). Exiting.".format(input)) - sys.exit(1) - -# docutils loses the single backslashes, so we precede them with escapes -# A better solution would be to escape baskslashes only in the math sections -import re -if options.escape_backslashes: - rst = rst.replace('\\','\\\\') -elif options.no_escape_backslashes: - pass -elif re.search(r'^\.\.[ \t]+escape-backslashes', rst, re.MULTILINE) is not None: - rst = rst.replace('\\','\\\\') - -# Do the translation rst -> html (using docutils) -from docutils.core import publish_parts -D = publish_parts(rst, writer_name='html') -title = D['title'] -html = D['whole'] - -# Do the translation html -> worksheet txt -from sagenb.notebook.docHTMLProcessor import docutilsHTMLProcessor -translator = docutilsHTMLProcessor() -worksheet_txt = translator.process_doc_html(html) - -# create a Notebook object -from sagenb.notebook.notebook import Notebook -from sage.misc.all import tmp_dir -nb = Notebook(tmp_dir()+'.sagenb') -nb.user_manager().create_default_users('password') - -# create a worksheet -W = nb.create_new_worksheet(title, 'admin') -W.edit_save(worksheet_txt) - -# export the worksheet as sws -nb.export_worksheet(W.filename(), output, title=title) - -# delete the Notebook object -nb.delete() diff --git a/src/bin/sage-rsyncdist b/src/bin/sage-rsyncdist deleted file mode 100755 index ed39a90ccb0..00000000000 --- a/src/bin/sage-rsyncdist +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env bash -# -# NOTE: This script is of little use after the git transition and -# will be deleted eventually. -# -# Create an rsyncable source distribution of Sage in dist/sage-rsync.tar.gz -# starting from a regular sdist tarball. -# -# This is mostly useful for regular automatic testing of Sage. -# -# All spkgs in spkg/standard are stored extracted: instead of a file -# spkg/standard/atlas-3.8.4.spkg, there is a directory -# spkg/standard/atlas/ (note the directory has no version number). -# In the tarball, there is no top-level directory like "sage-5.0", -# files like "Makefile" are stored directly at the top level. -# -# Running this script requires: -# * GNU tar -# * gzip with --rsyncable patch -# -# However, there are no special requirements for *building* from an -# rsyncable distribution. -# -# -# To build from an rsyncable tarball, do the following: -# mkdir sage-VERSION -# cd sage-VERSION -# tar xzf /path/to/sage-rsync.tar.gz -# ./rsyncpack.sh # to repack the directories into spkgs -# make # as usual -# -# -# AUTHOR: Jeroen Demeyer (2011-12-10): Trac ticket #12106 -# -#***************************************************************************** -# Copyright (C) 2011 Jeroen Demeyer -# -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - - -# Exit on error -set -e - - -# Check whether gzip supports --rsyncable, otherwise bail out immediately -if ! gzip --rsyncable /dev/null 2>/dev/null; then - echo >&2 "It seems your version of gzip does not support the --rsyncable option." - echo >&2 "In order to run sage --rsyncdist, you need a patched gzip." - echo >&2 "For more information about the patch, see" - echo >&2 "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=118118" - exit 1 -fi - - -# If $1 starts with "sage-", remove this prefix -SAGE_VERSION=`echo "$1" | sed 's/^sage-//'` -if [ -z "$SAGE_VERSION" ]; then - echo >&2 "Usage: $0 " - echo >&2 "Create an rsyncable source distribution of Sage" - exit 2 -fi - -# Run this script from SAGE_ROOT -[ -z "$SAGE_ROOT" ] || cd "$SAGE_ROOT" - -if [ ! -r "dist/sage-$SAGE_VERSION.tar" ]; then - echo >&2 "The sdist tarball dist/sage-$SAGE_VERSION.tar has not been created yet." - echo >&2 "You should call this script after running ./sage --sdist - exit 1 -fi - -# Extract existing sdist tarball -mkdir -p "dist/sage-rsync" -cd "dist/sage-rsync" -echo "Extracting sdist tarball sage-$SAGE_VERSION.tar" -tar -x --strip-components 1 -f "../sage-$SAGE_VERSION.tar" - -# Create a shell script to repack the spkgs. The repacked spkgs will -# not be compressed, but that's not a problem. It just means one -# should not make an sdist from an rsyncable Sage distribution. -exec 5>rsyncpack.sh -chmod 755 rsyncpack.sh -cat >&5 <&2 "Cannot determine base package name for $spkg" - exit 1 - fi - - echo "Extracting $spkg to directory $spkgname" - ( bzip2 -cd $spkg || gzip -cd $spkg || cat $spkg ) 2>/dev/null |\ - tar -x --delay-directory-restore - rm $spkg - mv "$spkgnamever" "$spkgname" - - echo "mv '$spkgname' '$spkgnamever' && tar c '$spkgnamever' >'$spkg' && rm -rf '$spkgnamever'" >&5 -done -exec 5<&- -cd ../.. - -# Put files in the tar file in *alphabetical* order, which is much -# better for rsync. Print directories with trailing slash for -# better sorting and skip '.' -echo "Packing tarball sage-rsync.tar.gz" -find . '!' -name . '(' -type d -printf '%P/\n' -or -printf '%P\n' ')' |\ -sort |\ -tar -c --no-recursion -T /dev/stdin |\ -gzip --best --rsyncable >../sage-rsync.tar.gz diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 3248f09b0dc..32d3fddc778 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -50,7 +50,6 @@ if __name__ == "__main__": 'skipped if no limit is set (default: 3300 MB)') parser.add_option("-a", "--all", action="store_true", default=False, help="test all files in the Sage library") parser.add_option("--logfile", metavar="FILE", help="log all output to FILE") - parser.add_option("--sagenb", action="store_true", default=False, help="test all files from the Sage notebook sources") parser.add_option("-l", "--long", action="store_true", default=False, help="include lines with the phrase 'long time'") parser.add_option("-s", "--short", dest="target_walltime", default=None, action="callback", @@ -71,8 +70,10 @@ if __name__ == "__main__": 'if "build" is listed, will also run tests specific to Sage\'s build/packaging system; ' 'if set to "all", then all tests will be run') parser.add_option("--randorder", type=int, metavar="SEED", help="randomize order of tests") + parser.add_option("--random-seed", dest="random_seed", type=int, metavar="SEED", help="random seed for fuzzing doctests") parser.add_option("--global-iterations", "--global_iterations", type=int, default=0, help="repeat the whole testing process this many times") parser.add_option("--file-iterations", "--file_iterations", type=int, default=0, help="repeat each file this many times, stopping on the first failure") + parser.add_option("--environment", type=str, default="sage.repl.ipython_kernel.all_jupyter", help="name of a module that provides the global environment for tests") parser.add_option("-i", "--initial", action="store_true", default=False, help="only show the first failure in each file") parser.add_option("--exitfirst", action="store_true", default=False, help="end the test run immediately after the first failure or unexpected exception") @@ -130,7 +131,7 @@ if __name__ == "__main__": options, args = parser.parse_args() - if not args and not (options.all or options.sagenb or options.new): + if not args and not (options.all or options.new): parser.print_help() sys.exit(2) diff --git a/src/bin/sage-sws2rst b/src/bin/sage-sws2rst deleted file mode 100755 index fb3eabeb966..00000000000 --- a/src/bin/sage-sws2rst +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/env sage-python -# -*- coding: utf-8 -*- -r""" -sage-sws2rst -============ - -Translate a Sage worksheet file (.sws) into an rst file. The result is -saved in the current working directory. - -Usage:: - - sage --sws2rst [-h] - -Print the help message:: - - sage --sws2rst -h - -EXAMPLES:: - - sage --sws2rst file.sws - -AUTHORS: - -- Pablo Angulo (January 2011): Initial version -- Karl-Dieter Crisman (June 2012): Documentation - and minor refinements -- Karl-Dieter Crisman (November 2014): Correct use of temporary files, - see :trac:`17308`. -""" - -#***************************************************************************** -# Copyright (C) 2011 Pablo Angulo -# Copyright (C) 2012-2014 Karl-Dieter Crisman -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -import sys -import tarfile -import os -import shutil -import codecs -import tempfile -from sagenb.misc.worksheet2rst import worksheet2rst - -from optparse import OptionParser - - -def process_sws(sws_file): - """ - Process the ``.sws`` file ``sws_file`` and create an ``.rst`` file - (and possible media files) in the current working directory. - """ - base_name = os.path.basename(os.path.splitext(sws_file)[0]) - base_name = base_name.replace(' ','_') - - tempdir = tempfile.mkdtemp() - try: - with tarfile.open(sws_file, mode='r:bz2') as sws_file: - sws_file.extractall(tempdir) - worksheet_dir = os.path.join(tempdir, 'sage_worksheet') - if not os.path.isdir(worksheet_dir): - raise RuntimeError("Worksheeet file %r does not contain a 'sage_worksheet' directory" % sws_file) - process_worksheet(worksheet_dir, base_name) - finally: - shutil.rmtree(tempdir) - -def process_worksheet(worksheet_dir, base_name): - """ - Process the extracted worksheet directory ``worksheet_dir`` and - create the ``.rst`` and media files with base name ``base_name``. - - Files are moved from ``worksheet_dir``, so make sure these are - temporary files! - """ - #Images - images_dir = base_name + '_media' - try: - os.mkdir(images_dir) - except OSError: - if not os.path.isdir(images_dir): - raise - - #"data" dir - data_path = os.path.join(worksheet_dir, 'data') - if os.path.isdir(data_path): - for image in os.listdir(data_path): - shutil.move(os.path.join(data_path, image), os.path.join(images_dir, image.replace(' ','_'))) - - #cells - cells_path = os.path.join(worksheet_dir, 'cells') - if os.path.isdir(cells_path): - for cell in os.listdir(cells_path): - cell_path = os.path.join(cells_path, cell) - for image in os.listdir(cell_path): - if os.path.isfile(os.path.join(cell_path, image)): - shutil.move(os.path.join(cell_path, image), - os.path.join(images_dir, 'cell_%s_%s'%(cell,image))) - # could be Jmol image directory - code for future - #elif os.path.isdir(os.path.join(cell_path, image)): - # if image == '.jmol_images': - # for jmolimg in os.listdir(os.path.join(cell_path, image)): - # shutil.move(os.path.join(cell_path, image, jmolimg), - # os.path.join(images_dir, 'cell_%s_%s'%(cell,jmolimg))) - - #read html file, parse it, write rst file - html_file = os.path.join(worksheet_dir, 'worksheet.html') - with codecs.open(html_file, mode='r', encoding='utf-8') as f: - html_text = f.read() - - rst_text = worksheet2rst(html_text, images_dir=images_dir) - rst_file = base_name + '.rst' - - with codecs.open(rst_file, mode='w', encoding='utf-8') as out_file: - out_file.write(rst_text) - - print("File at", rst_file) - print("Image directory at", images_dir) - - -# Set the parser -usage = r""" - - sage --sws2rst [options] ... - -Translate a Sage worksheet file (.sws) into an reStructuredText -(.rst) file. At least one sws file argument is required; all sws -files will be parsed and translated. Spaces in the names of the -worksheet will be converted to underscores. The resulting files will -be stored in the current working directory. - -Examples: - - sage --sws2rst file.sws - sage --sws2rst file1.sws file2.sws file3.sws - sage --sws2rst -h # this help message prints - sage --sws2rst --sphinxify # information about how to use - # Sphinx to compile your rst file -""" - -sphinxify_text = r""" - -Once you have made your rst file, what can you do with it? - -If this is a file which is likely to become part of the Sage -standard documentation, you will want to edit the appropriate -file in $SAGE_ROOT/src/doc to include your file, or -simply include your file as appropriate. - -However, you may simply want to make great-looking documentation -for some other purpose out of your worksheet. The following -steps are one way to do so. - - - Assume that the generated .rst file is ``My_Project.rst``. - - Make a folder somewhere convenient to compile in, say, ``MyProject``. - - Then move your .rst file into that folder, and cd into it. - - Now the key is to use Sage's shell to run Sphinx on it! Run ``sage --sh``. - - Then type ``sphinx-quickstart`` and follow the instructions in the - Sphinx tutorial [1]_. You will probably want to choose to render math - with MathJax [2]_, but you can accept the defaults for the other options. - - Finally, edit ``index.rst`` by adding ``My_Project`` in the table of - contents, as detailed in the Sphinx tutorial [3]_. - - If you now type ``make html`` you should get a beautiful-looking web page - in ``_build/html``. If you did not have a header at the top of your worksheet, - you may get an error, but you can ignore this. - -REFERENCES: - -.. [1] First Steps with Sphinx, - http://sphinx.pocoo.org/tutorial.html -.. [2] MathJax, - http://www.mathjax.org/ -.. [3] Defining Document Structure, First Steps with Sphinx, - http://sphinx.pocoo.org/tutorial.html#defining-document-structure""" - -parser = OptionParser(usage=usage) -parser.add_option("--sphinxify", - action="store_true", dest="sphinxify", - help="Print information about how to use Sphinx to compile your rst file, then exit.") -(options, args) = parser.parse_args() - -# Parse option -if options.sphinxify: - print(sphinxify_text) - sys.exit(0) - -# Parse arguments -if len(args) < 1: - parser.print_usage() - sys.exit(1) - -for file_name in args: - print("Processing", file_name) - process_sws(file_name) diff --git a/src/bin/sage-unzip b/src/bin/sage-unzip deleted file mode 100755 index 7f021d2bf9f..00000000000 --- a/src/bin/sage-unzip +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env sage-system-python -# -# This file is a replacement for the 'unzip' command. -# -# We obtain its main feature (extraction) through the 'zipfile' python module. - -import argparse - -parser = argparse.ArgumentParser(description='Extract the contents of a .zip archive') -parser.add_argument('filename', help="the .zip archive", type=argparse.FileType('r')) -parser.add_argument('-d',help='An optional directory to which to extract files. Set to "." by default.',action='store',default='.') - -args = parser.parse_args() -from zipfile import ZipFile -ZipFile(args.filename).extractall(args.d) diff --git a/src/bin/sage-update-version b/src/bin/sage-update-version index 9f91a807532..7cac9d7e07f 100755 --- a/src/bin/sage-update-version +++ b/src/bin/sage-update-version @@ -36,6 +36,8 @@ SAGE_VERSION=`echo "$1" | sed 's/^sage-//'` SAGE_RELEASE_DATE=`date -u +'%Y-%m-%d'` SAGE_VERSION_BANNER="SageMath version $SAGE_VERSION, Release Date: $SAGE_RELEASE_DATE" +echo $SAGE_VERSION > "$SAGE_ROOT/build/pkgs/sagelib/package-version.txt" + # Update Sage version file for Python in SAGE_SRC/sage cat < "$SAGE_SRC/sage/version.py" # Sage version information for Python scripts @@ -67,6 +69,7 @@ git commit -m "Updated SageMath version to $SAGE_VERSION" -- \ "$SAGE_SRC/bin/sage-version.sh" \ "$SAGE_ROOT/build/pkgs/configure/checksums.ini" \ "$SAGE_ROOT/build/pkgs/configure/package-version.txt" \ + "$SAGE_ROOT/build/pkgs/sagelib/package-version.txt" \ || die "Error committing to the repository." git tag -a "$SAGE_VERSION" -m "$SAGE_VERSION_BANNER" \ diff --git a/src/bin/sage-upgrade b/src/bin/sage-upgrade index 9ed4b88c765..957f9b523f7 100755 --- a/src/bin/sage-upgrade +++ b/src/bin/sage-upgrade @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # This script must be run from $SAGE_ROOT and without having sourced # sage-env. diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 9039ddf90e6..293dabd067f 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.beta4' -SAGE_RELEASE_DATE='2020-02-13' -SAGE_VERSION_BANNER='SageMath version 9.1.beta4, Release Date: 2020-02-13' +SAGE_VERSION='9.2.beta8' +SAGE_RELEASE_DATE='2020-08-10' +SAGE_VERSION_BANNER='SageMath version 9.2.beta8, Release Date: 2020-08-10' diff --git a/src/doc/.gitignore b/src/doc/.gitignore index 0b55f794176..dd2abbd9cf1 100644 --- a/src/doc/.gitignore +++ b/src/doc/.gitignore @@ -1,5 +1,6 @@ /en/reference/*/sage -/en/reference/*/sagenb /en/reference/sage -/en/reference/sagenb +/en/reference/spkg/*.rst /output +/en/installation/*.txt +/en/reference/repl/*.txt diff --git a/src/doc/Makefile b/src/doc/Makefile index 5160e35cc71..de8689b56ea 100644 --- a/src/doc/Makefile +++ b/src/doc/Makefile @@ -3,7 +3,5 @@ all: @echo "'sage -docbuild all html'. See 'sage -docbuild help' for more informations." clean: rm -rf en/reference/*/sage - rm -rf en/reference/*/sagenb rm -rf en/reference/sage - rm -rf en/reference/sagenb rm -f common/*.pyc diff --git a/src/doc/bootstrap b/src/doc/bootstrap new file mode 100755 index 00000000000..4577463c06b --- /dev/null +++ b/src/doc/bootstrap @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +######################################################################## +# Regenerate auto-generated files, using information in SAGE_ROOT/build/ +# +# This script is run by SAGE_ROOT/bootstrap as part of the bootstrapping phase +# (before configure, before creating source distributions). +######################################################################## + +set -e + +if [ -z "$SAGE_ROOT" ]; then + echo Please run the top-level bootstrap script of the Sage distribution. + exit 1 +fi + +cd "$SAGE_ROOT" + +STRIP_COMMENTS="sed s/#.*//;" +OUTPUT_DIR="src/doc/en/installation" +mkdir -p "$OUTPUT_DIR" + +shopt -s extglob + +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) + SYSTEM_PACKAGES_FILE=$PKG_SCRIPTS/distros/$SYSTEM.txt + if [ ! -f $PKG_SCRIPTS/type ]; then + echo >&2 "Warning: $PKG_SCRIPTS/type is missing." + continue + fi + PKG_TYPE=$(cat $PKG_SCRIPTS/type) + if [ -f $SYSTEM_PACKAGES_FILE -a -f $PKG_SCRIPTS/spkg-configure.m4 ]; then + PKG_SYSTEM_PACKAGES=$(echo $(${STRIP_COMMENTS} $SYSTEM_PACKAGES_FILE)) + if [ -n "PKG_SYSTEM_PACKAGES" ]; then + case "$PKG_TYPE" in + standard) + SYSTEM_PACKAGES+=" $PKG_SYSTEM_PACKAGES" + ;; + *) + OPTIONAL_SYSTEM_PACKAGES+=" $PKG_SYSTEM_PACKAGES" + ;; + esac + fi + fi + fi + done + echo >&2 $0:$LINENO: installing "$OUTPUT_DIR"/$SYSTEM.txt and "$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 + +OUTPUT_DIR="src/doc/en/reference/spkg" +mkdir -p "$OUTPUT_DIR" +echo >&2 $0:$LINENO: installing "$OUTPUT_DIR"/"*.rst" +OUTPUT_INDEX="$OUTPUT_DIR"/index.rst +cat > "$OUTPUT_INDEX" <> "$OUTPUT_INDEX" " $PKG_BASE" + fi + fi +done +cat >> "$OUTPUT_INDEX" <&2 $0:$LINENO: installing "$OUTPUT" +./sage -advanced > "$OUTPUT" diff --git a/src/doc/de/tutorial/interactive_shell.rst b/src/doc/de/tutorial/interactive_shell.rst index f07c36e20e6..bb9ac60b903 100644 --- a/src/doc/de/tutorial/interactive_shell.rst +++ b/src/doc/de/tutorial/interactive_shell.rst @@ -14,10 +14,10 @@ vornehmen. Nach dem Start von Sage sehen Sie etwa folgendes: :: - ---------------------------------------------------------------------- - | SAGE Version 4.5.2, Release Date: 2010-08-05 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: @@ -174,10 +174,10 @@ in einer zukünftigen Sitzung (indem Sie einfach die Log-Datei laden). :: was@form:~$ sage - ---------------------------------------------------------------------- - | SAGE Version 4.5.2, Release Date: 2010-08-05 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: logstart setup Activating auto-logging. Current session state plus future input saved. @@ -193,10 +193,10 @@ in einer zukünftigen Sitzung (indem Sie einfach die Log-Datei laden). sage: Exiting SAGE (CPU time 0m0.61s, Wall time 0m50.39s). was@form:~$ sage - ---------------------------------------------------------------------- - | SAGE Version 4.5.2, Release Date: 2010-08-05 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: load("setup") Loading log file one line at a time... @@ -868,115 +868,3 @@ Variable ``b`` wurde nicht überschrieben. sage: a 389 - - -.. _section-notebook: - -Die Notebook Umgebung -===================== - -Folgendes beschreibt das alte Sage Browser Notebook, auch "sagenb" genannt. - -SageMath wird demnächst die `Jupyter notebook -`_ -als Hauptnotebookoption verwenden. Der wichtigste -Unterschied hier liegt darin, dass die einzelnen Worksheet-Dateien -nicht auf einem Server wohnen, sondern werden wie in üblichen -Anwendungen gespeichert. - - -Altes SageNB Notebook ----------------------- - -Das Sage Browser Notebook wird mit - -.. skip - -:: - - sage: notebook() - -in der Sage Kommandozeile gestartet. Der Befehl startet das Sage -Notebook und ebenso Ihren Standardbrowser. Die Serverstatus-Dateien -liegen unter ``$HOME/.sage/sage\_notebook.sagenb``. - -Die andere Optionen enthalten z.B. - -.. skip - -:: - - sage: notebook("Verzeichnis") - -was einen neuen Notebook Server mit den Dateien aus dem angegebenen Verzeichnis -``Verzeichnis.sagenb`` -startet (anstelle des Standardverzeichnises ``$HOME/.sage/sage_notebook.sagenb``). -Das kann hilfreich sein, wenn Sie einige Worksheets für ein Projekt oder -verschiedene gleichzeitig laufende Notebook Server von einander trennen wollen. - -Wenn Sie das Notebook starten, werden zuerst die folgenden Dateien erzeugt -in ``$HOME/.sage/sage_notebook.sagenb``: - -:: - - conf.pickle - openid.pickle - twistedconf.tac - sagenb.pid - users.pickle - home/admin/ (Das Verzeichnis für den Hauptbenutzer) - home/guest/ (Ein Verzeichnis für Gäste) - home/pub/ (Ein Verzeichnis für veröffentlichte Worksheets) - -Nach dem Anlegen dieser Dateien, startet das notebook als Webserver. - -Ein "Notebook" ist eine Sammlung von Benutzerkonten, von dem jedes -verschiedene Worksheets enthalten kann. Wenn Sie ein neues Worksheet -erstellen, werden alle zugehörigen Daten unter -``home/benutzer/nummer`` gespeichert. In jedem solchen -Verzeichnis ist eine Klartextdatei namens ``worksheet.html`` - sollte -mit Ihren Worksheets oder Sage irgendetwas Unvorhergesehenes -passieren, enthält diese Datei alles was Sie benötigen um Ihre -Worksheets wiederherzustellen. Das Verzeichnis enthält: - -:: - - cells/ - worksheet.html - data/ - worksheet_conf.pickle - -Innerhalb von Sage können Sie mit ``notebook?`` mehr Informationen zum Start eines -Notebook-Servers erhalten. - -Das folgende Diagramm veranschaulicht die Architektur eines Sage Notebooks. - -:: - - ---------------------- - | | - | | - | firefox/safari | - | | - | javascript | - | programm | - | | - | | - ---------------------- - | ^ - | AJAX | - V | - ---------------------- - | | - | sage | SAGE Prozess 1 - | web | ------------> SAGE Prozess 2 (Python Prozesse) - | server | pexpect SAGE Prozess 3 - | | . - | | . - ---------------------- . - -Um Hilfe zu einem Sage-Befehl ``befehl`` im Notebook-Browser zu bekommen -geben Sie ``befehl?`` ein und drücken Sie ```` (nicht ````). - -Für Informationen zu Tastenbefehlen des Notebook-Browsers klicken Sie auf -den ``Help`` Link. diff --git a/src/doc/de/tutorial/introduction.rst b/src/doc/de/tutorial/introduction.rst index a1f7360ecb8..418d5e079fb 100644 --- a/src/doc/de/tutorial/introduction.rst +++ b/src/doc/de/tutorial/introduction.rst @@ -77,12 +77,8 @@ Hier geben wir nur ein paar Kommentare ab. weiter benutzt, müssen Sie diese Programme nicht separat installieren, da diese in der Sage-Distribution enthalten sind. Jedoch müssen Sie, um bestimmte Sage Zusatzfunktionen, zum - Beispiel Macaulay oder KASH, nutzen zu können, diese entsprechenden - optionalen Pakete installieren, oder zumindest die relevanten - Programme auf ihrem Computer schon installiert haben. Macaulay und - KASH sind Sage-Pakete (um eine Liste aller verfügbaren Sage-Pakete - zu sehen, geben Sie ``sage -optional`` ein, oder rufen Sie die - "Download" Seite auf der Sage Webseite auf). + Beispiel Macaulay oder KASH, nutzen zu können, die relevanten + Programme auf ihrem Computer schon installiert haben. #. Die vorkompilierte Binärversion von Sage (zu finden auf der Sage-Webseite) ist vielleicht einfacher und @@ -110,8 +106,8 @@ Wie man Sage benutzen kann Sie können Sage auf verschiedene Weise benutzen. -- **graphisches Notebook-Interface:** lesen Sie den Abschnitt - zum Notebook im Referenzhandbuch und :ref:`section-notebook` weiter unten, +- **graphisches Notebook-Interface:** rufen Sie `sage -n jupyter` auf; lesen Sie + `Jupyter documentation on-line `_, - **interaktive Kommandozeile:** lesen Sie :ref:`chapter-interactive_shell`, diff --git a/src/doc/de/tutorial/latex.rst b/src/doc/de/tutorial/latex.rst index a4b14924334..5133e699140 100644 --- a/src/doc/de/tutorial/latex.rst +++ b/src/doc/de/tutorial/latex.rst @@ -484,18 +484,3 @@ alle Berechnungs- oder LaTeX-Formatierungseigenschaften von Sage automatisch gen Als Beispiel hierfür kann in einer mathematischen Betrachtung die korrekte Reihenfolge von Fragen und Antworten beibehalten werden, indem sagetex dazu genutzt wird Sage die einen aus den anderen berechnen zu lassen. Siehe hierfür auch :ref:`sec-sagetex` - -tex2sws beginnt mit einem LaTeX-Dokument, aber definiert einige zusätzliche -Umgebungen für Sage Code. Wenn es richtig genutzt wird, ist das Ergebnis ein -Sage Arbeitsblatt mit korrekt von MathJax formatiertem Inhalt und dem dazugehörigen -Sage Code in den Eingabezellen. Ein Lehrbuch oder Artikel kann also mit Sage Code Blöcken -in LaTeX gesetzt werden und es kann "live" das ganze Dokument in ein Sage Arbeitsblatt überführt werden; -unter Beibehaltung der Sage Code Blöcke und mit schön formatiertem mathematischen Text. -Momentan in Arbeit, siehe `tex2sws @ BitBucket -`_ . - -sws2tex kehrt den Prozess um, indem es mit einem Sage Arbeitsblatt beginnt, und -es in ein legitimes LaTeX-Dokument zur weiteren Bearbeitung mit allen -LaTeX-Werkzeugen verwandelt. -Momentan in Arbeit, siehe `sws2tex @ BitBucket -`_ . diff --git a/src/doc/de/tutorial/tour_linalg.rst b/src/doc/de/tutorial/tour_linalg.rst index 7bd099a1957..4a090f259ba 100644 --- a/src/doc/de/tutorial/tour_linalg.rst +++ b/src/doc/de/tutorial/tour_linalg.rst @@ -255,4 +255,4 @@ Beachten Sie, dass Python zwischen Klein- und Großschreibung unterscheidet: sage: M = MatrixSpace(QQ, 10,10, Sparse=True) Traceback (most recent call last): ... - TypeError: __classcall__() got an unexpected keyword argument 'Sparse' + TypeError: __init__() got an unexpected keyword argument 'Sparse' diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 31fb975862b..a58175f9ad0 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -103,7 +103,7 @@ of the directory containing the Sage sources: SAGE_ROOT/ sage # the Sage launcher Makefile # top level Makefile - build/ # sage's build system + build/ # Sage's build system deps install ... @@ -112,12 +112,12 @@ of the directory containing the Sage sources: setup.py module_list.py ... - sage/ # sage library (formerly devel/sage-main/sage) - ext/ # extra sage resources (formerly devel/ext-main) - mac-app/ # would no longer have to awkwardly be in extcode - bin/ # the scripts in local/bin that are tracked - upstream/ # tarballs of upstream sources - local/ # installed binaries + sage/ # Sage library + ext_data/ # extra Sage resources (formerly src/ext) + mac-app/ # would no longer have to awkwardly be in extcode + bin/ # the scripts in local/bin that are tracked + upstream/ # tarballs of upstream sources + local/ # installed binaries Python Sage library code goes into ``src/`` and uses the following conventions. Directory names may be plural (e.g. ``rings``) and file @@ -155,11 +155,11 @@ Then in the file ``SAGE_ROOT/src/sage/all.py``, add a line :: from sage.measure_theory.all import * Non-Python Sage source code and supporting files should be placed in -appropriate subdirectories of ``SAGE_ROOT/src/ext/``. They will then be +appropriate subdirectories of ``SAGE_ROOT/src/sage/ext_data/``. They will then be automatically copied to the corresponding subdirectories of ``SAGE_ROOT/local/share/sage/ext/`` during the build process and can be accessed at runtime using ``SAGE_EXTCODE``. For example, if ``file`` is placed -in ``SAGE_ROOT/src/ext/directory/`` it can be accessed with :: +in ``SAGE_ROOT/src/sage/ext_data/directory/`` it can be accessed with :: from sage.env import SAGE_EXTCODE file = os.path.join(SAGE_EXTCODE, 'directory', 'file') @@ -347,7 +347,7 @@ information. You can use the existing functions of Sage as templates. :mod:`sage.some.related.module`. See :ref:`chapter-sage_manuals_links` for details on how to setup - link in Sage. + links in Sage. - An **ALGORITHM** block (optional). diff --git a/src/doc/en/developer/coding_in_python.rst b/src/doc/en/developer/coding_in_python.rst index d2850268b2c..7036ac8d69d 100644 --- a/src/doc/en/developer/coding_in_python.rst +++ b/src/doc/en/developer/coding_in_python.rst @@ -310,23 +310,25 @@ The __hash__ Special Method Here is the definition of ``__hash__`` from the Python reference manual: - Called by built-in function ``hash()`` and for operations on members of - hashed collections including set, frozenset, and dict. ``__hash__()`` - should return an integer. The only required property is that objects which - compare equal have the same hash value; it is advised to somehow mix - together (e.g. using exclusive or) the hash values for the components of - the object that also play a part in comparison of objects. If a class does - not define a - ``__cmp__()`` method it should not define a - ``__hash__()`` operation either; if it defines - ``__cmp__()`` or ``__eq__()`` but not - ``__hash__()``, its instances will not be usable as - dictionary keys. If a class defines mutable objects and implements - a ``__cmp__()`` or ``__eq__()`` method, it - should not implement ``__hash__()``, since the dictionary - implementation requires that a key's hash value is immutable (if - the object's hash value changes, it will be in the wrong hash - bucket). + Called by built-in function ``hash()`` and for operations on members + of hashed collections including ``set``, ``frozenset``, and + ``dict``. ``__hash__()`` should return an integer. The only required + property is that objects which compare equal have the same hash + value; it is advised to mix together the hash values of the + components of the object that also play a part in comparison of + objects by packing them into a tuple and hashing the tuple. + + If a class does not define an ``__eq__()`` method it should not define + a ``__hash__()`` operation either; if it defines ``__eq__()`` but not + ``__hash__()``, its instances will not be usable as items in hashable + collections. If a class defines mutable objects and implements an + ``__eq__()`` method, it should not implement ``__hash__()``, since the + implementation of hashable collections requires that a key’s hash + value is immutable (if the object’s hash value changes, it will be + in the wrong hash bucket). + +See https://docs.python.org/3/reference/datamodel.html#object.__hash__ for more +information on the subject. Notice the phrase, "The only required property is that objects which compare equal have the same hash value." This is an assumption made by diff --git a/src/doc/en/developer/doctesting.rst b/src/doc/en/developer/doctesting.rst index 1db4909e8b5..248ee49cd4d 100644 --- a/src/doc/en/developer/doctesting.rst +++ b/src/doc/en/developer/doctesting.rst @@ -106,6 +106,12 @@ Sage to test its own modules. Even if we have a system-wide Sage installation, using that version to doctest the modules of a local installation is a recipe for confusion. +If your system Python has the ``tox`` package, you can also run the Sage +doctester as follows:: + + [jdemeyer@sage sage-6.0]$ cd src + [jdemeyer@sage src]$ tox -- sage/games/sudoku.py + Troubleshooting =============== @@ -797,6 +803,31 @@ You can also pass in an explicit amount of time:: Finally, you can disable any warnings about long tests with ``--warn-long 0``. +Doctests may start from a random seed:: + + [kliem@sage sage-9.2]$ sage -t --warn-long 89.5 --random-seed=112986622569797306072457879734474628454 src/sage/doctest/tests/random_seed.rst + Running doctests with ID 2020-06-23-23-24-28-14a52269. + ... + Doctesting 1 file. + sage -t --warn-long 89.5 --random-seed=112986622569797306072457879734474628454 src/sage/doctest/tests/random_seed.rst + ********************************************************************** + File "src/sage/doctest/tests/random_seed.rst", line 3, in sage.doctest.tests.random_seed + Failed example: + randint(5, 10) + Expected: + 9 + Got: + 8 + ********************************************************************** + 1 item had failures: + 1 of 2 in sage.doctest.tests.random_seed + [1 test, 1 failure, 0.00 s] + ---------------------------------------------------------------------- + sage -t --warn-long 89.5 --random-seed=112986622569797306072457879734474628454 src/sage/doctest/tests/random_seed.rst # 1 doctest failed + ---------------------------------------------------------------------- + Total time for all tests: 0.0 seconds + cpu time: 0.0 seconds + cumulative wall time: 0.0 seconds .. _section-optional-doctest-flag: @@ -855,7 +886,7 @@ If you want Sage to detect external software or other capabilities (such as magma, latex, internet) automatically and run all of the relevant tests, then add ``external``:: - $ sage -t --optional=external src/sage/rings/real_mpfr.pyx + $ sage -t --optional=external src/sage/rings/real_mpfr.pyx Running doctests with ID 2016-03-16-14-10-21-af2ebb67. Using --optional=external External software to be detected: cplex,gurobi,internet,latex,macaulay2,magma,maple,mathematica,matlab,octave,scilab @@ -936,8 +967,6 @@ as well as testing the Sage notebook:: [304 tests, 69.0 s] ... -If you want to just run the notebook tests, use the ``--sagenb`` flag instead. - Debugging Tools --------------- diff --git a/src/doc/en/developer/git_trac.rst b/src/doc/en/developer/git_trac.rst index 5ea66e57615..f89b7012824 100644 --- a/src/doc/en/developer/git_trac.rst +++ b/src/doc/en/developer/git_trac.rst @@ -110,6 +110,11 @@ This is required if you authenticate to Trac with your GitHub account, as you do not have a Trac password. Logged in users can find their token under `the token tab in preferences on the trac site `_ . +.. NOTE:: + + The username to be entered here is NOT the GitHub username, but rather the trac username which is gh- + as given on the top right corner of the trac server. + If both a token and a username/password are configured, the token-based authentication takes precedence. diff --git a/src/doc/en/developer/index.rst b/src/doc/en/developer/index.rst index f0e027a4aa6..dfe9b6024c0 100644 --- a/src/doc/en/developer/index.rst +++ b/src/doc/en/developer/index.rst @@ -126,6 +126,14 @@ Running Sage's tests doctesting +Testing on multiple platforms +----------------------------- + +.. toctree:: + :maxdepth: 3 + + portability_testing + Contributing to Manuals and Tutorials ------------------------------------- @@ -151,15 +159,6 @@ Packaging Third-Party Code :maxdepth: 3 packaging - packaging_old_spkgs - -Sage Notebook Developer Guide -============================= - -.. toctree:: - :maxdepth: 3 - - sagenb/index Indices and tables diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst index a619a7716a4..e4852e843e8 100644 --- a/src/doc/en/developer/packaging.rst +++ b/src/doc/en/developer/packaging.rst @@ -60,6 +60,62 @@ optional and experimental ones: some problems, the package can still be accepted. +.. _section-package-source-types: + +Package source types +-------------------- + +Orthogonal to the division by package types, a package has exactly one of +the following source types: + +#. A ``normal`` package: + + - comes from the tarball named in the required file ``checksums.ini`` and + hosted on the Sage mirrors; + + - its version number is defined by the required file ``package-version.txt``; + + - Sage installs the package using build and install scripts + (see :ref:`section-spkg-install`); + + - Sage records the version number of the package installed using a file in + ``$SAGE_LOCAL/var/lib/sage/installed/`` and will re-run the installation + if ``package-version.txt`` changes. + +#. A ``pip`` package: + + - is obtained directly from https://pypi.org/; + + - the version to be installed is determined using the required file + ``requirements.txt`` -- in its simplest form, this file just + contains the name of the package (more details at + https://pip.pypa.io/en/stable/user_guide/#requirements-files); + + - Sage installs the package using the ``pip`` package manager; + + - Sage delegates the recording of installed package version numbers to it; + + - by policy, no ``standard`` package is allowed to be a ``pip`` package. + +#. A ``script`` package: + + - is not associated with a tarball; + + - the file ``package-version.txt`` is optional; + + - installing the package runs the build and install scripts + (see :ref:`section-spkg-install`); + + - Sage records the version number of the package installed using a file in + ``$SAGE_LOCAL/var/lib/sage/installed/`` and will re-run the installation + if ``package-version.txt`` changes. + +To summarize: the package source type is determined as follows: if +there is a file ``requirements.txt``, it is a ``pip`` package. If not, +then if there is a ``checksums.ini`` file, it is ``normal``; +otherwise, it is a ``script`` package. + + .. _section-directory-structure: Directory Structure @@ -99,8 +155,8 @@ a minimum the following files: |-- checksums.ini |-- dependencies |-- package-version.txt - |-- spkg-install - |-- SPKG.txt + |-- spkg-install.in + |-- SPKG.rst `-- type The following are some additional files which can be added: @@ -111,7 +167,7 @@ The following are some additional files which can be added: |-- patches | |-- bar.patch | `-- baz.patch - |-- spkg-check + |-- spkg-check.in `-- spkg-src We discuss the individual files in the following sections. @@ -127,33 +183,39 @@ See :ref:`section-package-types` for the meaning of these types. .. _section-spkg-install: -Build and install scripts -------------------------- - -The ``spkg-build`` and ``spkg-install`` files are ``bash`` scripts that -build and/or install the package. If no ``spkg-build`` exists, then the -``spkg-install`` is responsible for both steps, though separating them is -encouraged where possible. - -It is also possible to include similar scripts named ``spkg-preinst`` or -``spkg-postinst`` to run additional steps before or after the package has been -installed into ``$SAGE_LOCAL``. It is encouraged to put steps which modify -already installed files in a separate ``spkg-postinst`` script rather than -combinging them with ``spkg-install``. This is because since :trac:`24106`, -``spkg-install`` does not necessarily install packages directly to -``$SAGE_LOCAL``. However, by the time ``spkg-postinst`` is run, the -installation to ``$SAGE_LOCAL`` is complete. - -These scripts should *not* be prefixed with a shebang line (``#!...``) and -should not have the executable bit set in their permissions. These are -added automatically, along with some additional boilerplate, when the -package is installed. The ``spkg-build`` and ``spkg-install`` files in the -Sage source tree need only focus on the specific steps for building and -installing that package. +Build and install scripts of normal packages +-------------------------------------------- + +The ``spkg-build.in`` and ``spkg-install.in`` files are templates for +``bash`` scripts ``spkg-build`` and ``spkg-install``, which build +and/or install the package. + +The ``*.in`` script templates should *not* be prefixed with a shebang +line (``#!...``) and should not have the executable bit set in their +permissions. These are added automatically when generating the +scripts, along with some additional boilerplate, when the package is +installed. + +The ``spkg-build.in`` and ``spkg-install.in`` files in the Sage source +tree need only focus on the specific steps for building and installing +that package. If no ``spkg-build.in`` exists, then the +``spkg-install.in`` is responsible for both steps, though separating +them is encouraged where possible. + +It is also possible to include similar script templatess named +``spkg-preinst.in`` or ``spkg-postinst.in`` to run additional steps +before or after the package has been installed into +``$SAGE_LOCAL``. It is encouraged to put steps which modify already +installed files in a separate ``spkg-postinst.in`` script template +rather than combining them with ``spkg-install.in``. This is because +since :trac:`24106`, ``spkg-install`` does not necessarily install +packages directly to ``$SAGE_LOCAL``. However, by the time +``spkg-postinst`` is run, the installation to ``$SAGE_LOCAL`` is +complete. In the best case, the upstream project can simply be installed by the -usual configure / make / make install steps. In that case, the build -script would simply consist of: +usual configure / make / make install steps. In that case, the +``spkg-build.in`` script template would simply consist of: .. CODE-BLOCK:: bash @@ -164,7 +226,7 @@ script would simply consist of: See :ref:`section-sdh-helpers` for more on the helper functions ``sdh_configure``, ``sdh_make``, etc. -The install script would consist of: +The ``spkg-install.in`` script template would consist of: .. CODE-BLOCK:: bash @@ -188,6 +250,9 @@ something like the following to install it: .. note:: + Prior to Sage 9.1, the script templates were called ``spkg-build``, + ``spkg-install``, etc., without the extension ``.in``. + Prior to Sage 8.1 the shebang line was included, and the scripts were marked executable. However, this is no longer the case as of :trac:`23179`. Now the scripts in the source tree are deliberately @@ -196,7 +261,7 @@ something like the following to install it: Build/install scripts may still be written in Python, but the Python code should go in a separate file (e.g. ``spkg-install.py``), and can - then be executed from the real ``spkg-install`` like: + then be executed from the real ``spkg-install.in`` like: .. CODE-BLOCK:: text @@ -221,20 +286,30 @@ something like the following to install it: expect Sage's Python to already be built. Many packages currently do not separate the build and install steps and only -provide a ``spkg-install`` file that does both. The separation is useful in +provide a ``spkg-install.in`` file that does both. The separation is useful in particular for root-owned install hierarchies, where something like ``sudo`` must be used to install files. For this purpose Sage uses an environment variable ``$SAGE_SUDO``, the value of which may be provided by the developer at build time, which should to the appropriate system-specific ``sudo``-like command (if any). The following rules are then observed: -- If ``spkg-build`` exists, it is first called, followed by - ``$SAGE_SUDO spkg-install``. +- If ``spkg-build.in`` exists, the generated script ``spkg-build`` is first + called, followed by ``$SAGE_SUDO spkg-install``. - Otherwise, only ``spkg-install`` is called (without ``$SAGE_SUDO``). Such - packages should prefix all commands in ``spkg-install`` that write into + packages should prefix all commands in ``spkg-install.in`` that write into the installation hierarchy with ``$SAGE_SUDO``. +Install scripts of script packages +---------------------------------- + +A script package has a single install script named ``spkg-install``. +It needs to be an executable shell script; it is not subject to the templating +described in the previous section. + +Sage runs ``spkg-install`` from the directory ``$SAGE_ROOT/build/pkgs/`` +in the environment obtained by sourcing the files ``src/bin/sage-env`` and +``build/bin/sage-build-env-config``. .. _section-sdh-helpers: @@ -340,13 +415,14 @@ The following are also available, but rarely used. Self-Tests ---------- -The ``spkg-check`` file is an optional, but highly recommended, script to -run self-tests of the package. The format for the ``spkg-check`` is the -same as ``spkg-build`` and ``spkg-install``. It is run after building and -installing if the ``SAGE_CHECK`` environment variable is set, see the Sage -installation guide. Ideally, upstream has some sort of tests suite that can -be run with the standard ``make check`` target. In that case, the -``spkg-check`` script would simply contain: +The ``spkg-check.in`` file is an optional, but highly recommended, +script template to run self-tests of the package. The format for the +``spkg-check`` is the same as ``spkg-build`` and ``spkg-install``. It +is run after building and installing if the ``SAGE_CHECK`` environment +variable is set, see the Sage installation guide. Ideally, upstream +has some sort of tests suite that can be run with the standard ``make +check`` target. In that case, the ``spkg-check.in`` script template +would simply contain: .. CODE-BLOCK:: bash @@ -360,7 +436,7 @@ Python-based packages --------------------- The best way to install a Python-based package is to use pip, in which -case the ``spkg-install`` script might just consist of +case the ``spkg-install.in`` script template might just consist of .. CODE-BLOCK:: bash @@ -371,11 +447,11 @@ points to the correct ``pip`` for the Python used by Sage, and includes some default flags needed for correct installation into Sage. If pip will not work but a command like ``python setup.py install`` -will, then the ``spkg-install`` script should call ``sage-python23`` -rather than ``python``. This will ensure that the correct version of -Python is used to build and install the package. The same holds for -``spkg-check`` scripts; for example, the ``scipy`` ``spkg-check`` -file contains the line +will, then the ``spkg-install.in`` script template should call +``sage-python23`` rather than ``python``. This will ensure that the +correct version of Python is used to build and install the +package. The same holds for ``spkg-check.in`` script templates; for +example, the ``scipy`` ``spkg-check.in`` file contains the line .. CODE-BLOCK:: bash @@ -384,8 +460,8 @@ file contains the line .. _section-spkg-SPKG-txt: -The SPKG.txt File ------------------ +The SPKG.rst or SPKG.txt File +----------------------------- The ``SPKG.txt`` file should follow this pattern: @@ -422,6 +498,8 @@ with ``PACKAGE_NAME`` replaced by the package name. Legacy ``SPKG.txt`` files have an additional changelog section, but this information is now kept in the git repository. +It is now also possible to use an ``SPKG.rst`` file instead, with the same +sections. .. _section-dependencies: @@ -441,6 +519,17 @@ for ``eclib``: All lines of this file are ignored except the first. It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. +For Python packages, common dependencies include ``pip``, +``setuptools``, and ``future``. If your package depends on any of +these, use ``$(PYTHON_TOOLCHAIN)`` instead. For example, here is the +``dependencies`` file for ``configparser``:: + +.. CODE-BLOCK:: text + + $(PYTHON) | $(PYTHON_TOOLCHAIN) + +(See below for the meaning of the ``|``.) + If there are no dependencies, you can use .. CODE-BLOCK:: text @@ -794,7 +883,7 @@ License Information If you are patching a standard Sage spkg, then you should make sure that the license information for that package is up-to-date, both in its -``SPKG.txt`` file and in the file ``SAGE_ROOT/COPYING.txt``. For +``SPKG.rst`` or ``SPKG.txt`` file and in the file ``SAGE_ROOT/COPYING.txt``. For example, if you are producing an spkg which upgrades the vanilla source to a new version, check whether the license changed between versions. @@ -809,18 +898,8 @@ must meet the following requirements: Foundation maintains a long list of `licenses and comments about them `_. -- **Build Support**. The code must build on all the `fully supported - platforms - `_. - - A standard package should also work on all the platforms where Sage - is `expected to work - `_ and - on which Sage `almost works - `_ but - since we don't fully support these platforms and often lack the - resources to test on them, you are not expected to confirm your - packages works on those platforms. +- **Build Support**. The code must build on all the fully supported + platforms (Linux, macOS, Cygwin); see :ref:`chapter-portability_testing`. - **Quality**. The code should be "better" than any other available code (that passes the two above criteria), and the authors need to diff --git a/src/doc/en/developer/packaging_old_spkgs.rst b/src/doc/en/developer/packaging_old_spkgs.rst deleted file mode 100644 index 588994e2845..00000000000 --- a/src/doc/en/developer/packaging_old_spkgs.rst +++ /dev/null @@ -1,559 +0,0 @@ -.. highlight:: bash - -.. _chapter-old-spkg: - -========================= -Packaging Old-Style SPKGs -========================= - -This chapter explains old-style spkgs; It applies only to legacy -optional spkgs and experimental spkgs. - -.. WARNING:: - - Old-style packages are **deprecated**, it is strongly - suggested that you make a new-style package instead. - See :ref:`chapter-packaging` - for the modern way of packaging third-party software. - - -Creating an Old-Style SPKG -========================== - -If you are producing code to add new functionality to Sage, you might -consider turning it into a package (an "spkg") instead of a patch -file. If your code is very large (for instance) and should be offered -as an optional download, a package is the right choice. Similarly, if -your code depends on some other optional component of Sage, you should -produce a package. When in doubt, ask for advice on the ``sage-devel`` -mailing list. - -The abbreviation "spkg" stands for "Sage package". The directory -``SAGE_ROOT/spkg/standard`` contains spkg's. In a source install, -these are all Sage spkg files (actually ``.tar`` or ``.tar.bz2`` -files), which are the source code that defines Sage. In a binary -install, some of these may be small placeholder files to save space. - -Sage packages are distributed as ``.spkg`` files, which are -``.tar.bz2`` files (or ``tar`` files) but have the extension ``.spkg`` -to discourage confusion. Although Sage packages are packed using tar -and/or bzip2, note that ``.spkg`` files contain control information -(installation scripts and metadata) that are necessary for building -and installing them. When you compile Sage from a source distribution -(or when you run ``sage -p ``), the file -``SAGE_ROOT/build/bin/sage-spkg`` takes care of the unpacking, -compilation, and installation of Sage packages for you. You can type:: - - tar -jxvf mypackage-version.spkg - -to extract an spkg and see what is inside. If you want to create a -new Sage package, it is recommended that you start by examining some -existing spkg's. The URL -http://www.sagemath.org/download-packages.html lists spkg's available -for download. - - -Naming Your SPKG ----------------- - -Each Sage spkg has a name of the following form:: - - BASENAME-VERSION.spkg - -``BASENAME`` is the name of the package; it may contain lower-case -letters, numbers, and underscores, but no hyphens. ``VERSION`` is the -version number; it should start with a number and may contain numbers, -letters, dots, and hyphens; it may end in a string of the form "pNUM", -where "NUM" is a non-negative integer. If your spkg is a "vanilla" -(unmodified) version of some piece of software, say version 5.3 of -"my-python-package", then ``BASENAME`` would be "my_python_package" -- -note the change from hyphens to underscores, because ``BASENAME`` -should not contain any hyphens -- and ``VERSION`` would be "5.3". If -you need to modify the software to use it with Sage (as described -below and in the chapter :ref:`section-old-spkg-patching-overview`), -then ``VERSION`` would be "5.3.p0", the "p0" indicating a patch-level -of 0. If someone adds more patches, later, this would become "p1", -then "p2", etc. - -The string ``VERSION`` must be present. If you are using a piece -software with no obvious version number, use a date. To give your spkg -a name like this, create a directory called ``BASENAME-VERSION`` and -put your files in that directory -- the next section describes the -directory structure. - - -Directory Structure -------------------- - -Put your files in a directory with a name like ``mypackage-0.1``, as -described above. If you are porting -another software package, then the directory should contain a -subdirectory ``src/``, containing an unaltered copy of the package. -Every file not in ``src/`` should be under version control, i.e. checked -into an hg repository. - -More precisely, the directory should contain the following: - -- ``src/``: this directory contains vanilla upstream code, with a few - exceptions, e.g. when the spkg shipped with Sage is in effect - upstream, and development on that code base is happening in close - coordination with Sage. See John Cremona's eclib spkg, for - instance. The directory ``src/`` must not be under revision control. - -- ``.hg``, ``.hgignore``, and ``.hgtags``: Old-style spkgs use - Mercurial for its revision control system. The hidden directory - ``.hg`` is part of the standard Sage spkg layout. It contains the - Mercurial repository for all files not in the ``src/`` directory. - To create this Mercurial repository from scratch, you should do:: - - hg init - - The files ``.hgignore`` and ``.hgtags`` also belong to the Mercurial - repository. The file ``.hgtags`` is optional, and is frequently - omitted. You should make sure that the file ``.hgignore`` contains - "src/", since we are not tracking its content. Indeed, frequently - this file contains only a single line:: - - src/ - -- ``spkg-install``: this file contains the install script. See - :ref:`section-old-spkg-install` for more information and a template. - -- ``SPKG.txt``: this file describes the spkg in wiki format. Each new - revision needs an updated changelog entry or the spkg will get an - automatic "needs work" at review time. See - :ref:`section-old-spkg-SPKG-txt` for a template. - -- ``spkg-check``: this file runs the test suite. This is somewhat - optional since not all spkg's have test suites. If possible, do - create such a script since it helps isolate bugs in upstream - packages. - -- ``patches/``: this directory contains patches to source files in - ``src/``. See :ref:`section-old-spkg-patching-overview`. Patches - to files in ``src/`` should be applied in ``spkg-install``, and all - patches must be self-documenting, i.e. the header must contain what - they do, if they are platform specific, if they should be pushed - upstream, etc. To ensure that all patched versions of upstream - source files under ``src/`` are under revision control, the whole - directory ``patches/`` must be under revision control. - -**Never** apply patches to upstream source files under ``src/`` and -then package up an spkg. Such a mixture of upstream source with Sage -specific patched versions is a recipe for confusion. There must be a -**clean separation** between the source provided by the upstream -project and the patched versions that the Sage project generates based -on top of the upstream source. - -The only exception to this rule is for *removals* of unused -files or directories. Some packages contain parts which are not needed -for Sage. To save space, these may be removed directly from ``src/``. -But be sure to document this in the "Special Update/Build Instructions" -section in ``SPKG.txt``! - - -.. _section-old-spkg-install: - -The File spkg-install ---------------------- - -The script ``spkg-install`` is run during installation of the Sage -package. In this script, you may make the following assumptions: - -- The PATH has the locations of ``sage`` and ``python`` (from the Sage - installation) at the front. Thus the command:: - - python setup.py install - - will run the correct version of Python with everything set up - correctly. Also, running ``gap`` or ``Singular``, for example, will - run the correct version. - -- The environment variable ``SAGE_ROOT`` points to the root directory - of the Sage installation. - -- The environment variable ``SAGE_LOCAL`` points to the - ``SAGE_ROOT/local`` directory of the Sage installation. - -The ``spkg-install`` script should copy your files to the appropriate -place after doing any build that is necessary. Here is a template:: - - #!/usr/bin/env bash - - if [ -z "$SAGE_LOCAL" ]; then - echo >&2 "SAGE_LOCAL undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 - fi - - cd src - - # Apply patches. See SPKG.txt for information about what each patch - # does. - for patch in ../patches/*.patch; do - [ -r "$patch" ] || continue # Skip non-existing or non-readable patches - patch -p1 <"$patch" - if [ $? -ne 0 ]; then - echo >&2 "Error applying '$patch'" - exit 1 - fi - done - - ./configure --prefix="$SAGE_LOCAL" - if [ $? -ne 0 ]; then - echo >&2 "Error configuring PACKAGE_NAME." - exit 1 - fi - - $MAKE - if [ $? -ne 0 ]; then - echo >&2 "Error building PACKAGE_NAME." - exit 1 - fi - - $MAKE install - if [ $? -ne 0 ]; then - echo >&2 "Error installing PACKAGE_NAME." - exit 1 - fi - - if [ "$SAGE_SPKG_INSTALL_DOCS" = yes ] ; then - # Before trying to build the documentation, check if any - # needed programs are present. In the example below, we - # check for 'latex', but this will depend on the package. - # Some packages may need no extra tools installed, others - # may require some. We use 'command -v' for testing this, - # and not 'which' since 'which' is not portable, whereas - # 'command -v' is defined by POSIX. - - # if [ `command -v latex` ] ; then - # echo "Good, latex was found, so building the documentation" - # else - # echo "Sorry, can't build the documentation for PACKAGE_NAME as latex is not installed" - # exit 1 - # fi - - - # make the documentation in a package-specific way - # for example, we might have - # cd doc - # $MAKE html - - if [ $? -ne 0 ]; then - echo >&2 "Error building PACKAGE_NAME docs." - exit 1 - fi - mkdir -p "$SAGE_ROOT/local/share/doc/PACKAGE_NAME" - # assuming the docs are in doc/* - cp -R doc/* "$SAGE_ROOT/local/share/doc/PACKAGE_NAME" - fi - - -Note that the first line is ``#!/usr/bin/env bash``; this is important -for portability. Next, the script checks that ``SAGE_LOCAL`` is -defined to make sure that the Sage environment has been set. After -this, the script may simply run ``cd src`` and then call either -``python setup.py install`` or the autotools sequence -``./configure && make && make install``, or something else along these -lines. - -Sometimes, though, it can be more complicated. For example, you might need -to apply the patches from the ``patches`` directory in a particular order. Also, -you should first build (e.g. with ``python setup.py build``, exiting -if there is an error), before installing (e.g. with ``python setup.py -install``). In this way, you would not overwrite a working older -version with a non-working newer version of the spkg. - -When copying documentation to -``$SAGE_ROOT/local/share/doc/PACKAGE_NAME``, it may be necessary to -check that only the actual documentation files intended for the user -are copied. For example, if the documentation is built from ``.tex`` -files, you may just need to copy the resulting pdf files, rather than -copying the entire doc directory. When generating documentation using -Sphinx, copying the ``build/html`` directory generally will copy just -the actual output intended for the user. - - -.. _section-old-spkg-SPKG-txt: - -The File SPKG.txt ------------------ - -The old-style ``SPKG.txt`` file is the same as described in -:ref:`section-spkg-SPKG-txt`, but with a hand-maintained changelog -appended since the contents are not part of the Sage repository -tree. It should follow the following pattern: - -.. CODE-BLOCK:: text - - == Changelog == - - Provide a changelog of the spkg here, where the entries have this format: - - === mypackage-0.1.p0 (Mary Smith, 1 Jan 2012) === - - * Patch src/configure so it builds on Solaris. See Sage trac #137. - - === mypackage-0.1 (Leonhard Euler, 17 September 1783) === - - * Initial release. See Sage trac #007. - -When the directory (say, ``mypackage-0.1``) is ready, the command - -:: - - sage --pkg mypackage-0.1 - -will create the file ``mypackage-0.1.spkg``. As noted above, this -creates a compressed tar file. Running ``sage --pkg_nc mypackage-0.1`` -creates an uncompressed tar file. - -When your spkg is ready, you should post about it on ``sage-devel``. -If people there think it is a good idea, then post a link to the spkg -on the Sage trac server (see :ref:`chapter-sage-trac`) so it can be -refereed. Do not post the spkg itself to the trac server: you only -need to provide a link to your spkg. If your spkg gets a positive -review, it might be included into the core Sage library, or it might -become an optional download from the Sage website, so anybody can -automatically install it by typing ``sage -p mypackage-version.spkg``. - -.. NOTE:: - - For any spkg: - - - Make sure that the hg repository contains every file outside the - ``src`` directory, and that these are all up-to-date and committed - into the repository. - - - Include an ``spkg-check`` file if possible (see `trac ticket #299`_). - - .. _trac ticket #299: http://trac.sagemath.org/sage_trac/ticket/299 - -.. NOTE:: - - External Magma code goes in ``SAGE_ROOT/src/ext/magma/user``, so - if you want to redistribute Magma code with Sage as a package that - Magma-enabled users can use, that is where you would put it. You - would also want to have relevant Python code to make the Magma - code easily usable. - - -.. _section-old-spkg-avoiding-troubles: - -Avoiding Troubles -================= - -This section contains some guidelines on what an spkg must never do to -a Sage installation. You are encouraged to produce an spkg that is as -self-contained as possible. - -#. An spkg must not modify an existing source file in the Sage - library. -#. Do not allow an spkg to modify another spkg. One spkg can depend on - other spkg -- see above. You need to first test for the existence of the - prerequisite spkg before installing an spkg that depends on it. - - - - -.. _section-old-spkg-patching-overview: - -Overview of Patching SPKGs -========================== - -Make sure you are familiar with the structure and conventions relating -to spkg's; see the chapter :ref:`chapter-old-spkg` for -details. Patching an spkg involves patching the installation script of -the spkg and/or patching the upstream source code contained in the -spkg. Say you want to patch the Matplotlib package -``matplotlib-1.0.1.p0``. Note that "p0" denotes the patch level of the -spkg, while "1.0.1" refers to the upstream version of Matplotlib as -contained under ``matplotlib-1.0.1.p0/src/``. The installation script -of that spkg is:: - - matplotlib-1.0.1.p0/spkg-install - -In general, a script with the name ``spkg-install`` is an -installation script for an spkg. To patch the installation script, use -a text editor to edit that script. Then in the log file ``SPKG.txt``, -provide a high-level description of your changes. Once you are -satisfied with your changes in the installation script and the log -file ``SPKG.txt``, use Mercurial to check in your changes and make -sure to provide a meaningful commit message. - -The directory ``src/`` contains the source code provided by the -upstream project. For example, the source code of Matplotlib 1.0.1 is -contained under :: - - matplotlib-1.0.1.p0/src/ - -To patch the upstream source code, you should edit a copy of the -relevant file -- files in the ``src/`` directory should be untouched, -"vanilla" versions of the source code. For example, you might copy -the entire ``src/`` directory: - -.. CODE-BLOCK:: shell-session - - $ pwd - matplotlib-1.0.1.p0 - $ cp -pR src src-patched - -Then edit files in ``src-patched/``. Once you are satisfied with your -changes, generate a unified diff between the original file and the -edited one, and save it in ``patches/``: - -.. CODE-BLOCK:: shell-session - - $ diff -u src/configure src-patched/configure > patches/configure.patch - -Save the unified diff to a file with the same name as the source file -you patched, but using the file extension ".patch". Note that the -directory ``src/`` should not be under revision control, whereas -``patches/`` must be under revision control. The Mercurial -configuration file ``.hgignore`` should contain the following line:: - - src/ - -Ensure that the installation script ``spkg-install`` contains code to -apply the patches to the relevant files under ``src/``. For example, -the file :: - - matplotlib-1.0.1.p0/patches/finance.py.patch - -is a patch for the file :: - - matplotlib-1.0.1.p0/src/lib/matplotlib/finance.py - -The installation script ``matplotlib-1.0.1.p0/spkg-install`` contains the -following code to install the relevant patches:: - - cd src - - # Apply patches. See SPKG.txt for information about what each patch - # does. - for patch in ../patches/*.patch; do - patch -p1 <"$patch" - if [ $? -ne 0 ]; then - echo >&2 "Error applying '$patch'" - exit 1 - fi - done - -Of course, this could be modified if the order in which the patches -are applied is important, or if some patches were platform-dependent. -For example:: - - if [ "$UNAME" = "Darwin" ]; then - for patch in ../patches/darwin/*.patch; do - patch -p1 <"$patch" - if [ $? -ne 0 ]; then - echo >&2 "Error applying '$patch'" - exit 1 - fi - done - fi - -(The environment variable :envvar:`UNAME` is defined by the script -``sage-env``, and is available when ``spkg-install`` is run.) - -Now provide a high-level explanation of your changes in ``SPKG.txt``. -Note the format of ``SPKG.txt`` -- see the chapter -:ref:`chapter-old-spkg` for details. Once you are satisfied with your -changes, use Mercurial to check in your changes with a meaningful -commit message. Then use the command ``hg tag`` to tag the tip with -the new version number (using "p1" instead of "p0": we have made -changes, so we need to update the patch level): - -.. CODE-BLOCK:: shell-session - - $ hg tag matplotlib-1.0.1.p1 - -Next, rename the directory ``matplotlib-1.0.1.p0`` to -``matplotlib-1.0.1.p1`` to match the new patch level. To produce the -actual spkg file, change to the parent directory of -``matplotlib-1.0.1.p1`` and execute - -.. CODE-BLOCK:: shell-session - - $ /path/to/sage-x.y.z/sage --pkg matplotlib-1.0.1.p1 - Creating Sage package matplotlib-1.0.1.p1 - - Created package matplotlib-1.0.1.p1.spkg. - - NAME: matplotlib - VERSION: 1.0.1.p1 - SIZE: 11.8M - HG REPO: Good - SPKG.txt: Good - -Spkg files are either bzipped tar files or just plain tar files; the -command ``sage --pkg ...`` produces the bzipped version. If your spkg -contains mostly binary files which will not compress well, you can use -``sage --pkg_nc ...`` to produce an uncompressed version, i.e., a -plain tar file: - -.. CODE-BLOCK:: shell-session - - $ sage --pkg_nc matplotlib-1.0.1.p0/ - Creating Sage package matplotlib-1.0.1.p0/ with no compression - - Created package matplotlib-1.0.1.p0.spkg. - - NAME: matplotlib - VERSION: 1.0.1.p0 - SIZE: 32.8M - HG REPO: Good - SPKG.txt: Good - -Note that this is almost three times the size of the compressed -version, so we should use the compressed version! - -At this point, you might want to submit your patched spkg for review. -So provide a URL to your spkg on the relevant trac ticket and/or in an -email to the relevant mailing list. Usually, you should not upload -your spkg itself to the relevant trac ticket -- don't post large -binary files to the trac server. - - -SPKG Versioning -=============== - -If you want to bump up the version of an spkg, you need to follow some -naming conventions. Use the name and version number as given by the -upstream project, e.g. ``matplotlib-1.0.1``. If the upstream package is -taken from some revision other than a stable version, you need to -append the date at which the revision is made, e.g. the Singular -package ``singular-3-1-0-4-20090818.p3.spkg`` is made with the -revision as of 2009-08-18. If you start afresh from an upstream -release without any patches to its source code, the resulting spkg -need not have any patch-level labels (appending ".p0" is allowed, but -is optional). For example, ``sagenb-0.6.spkg`` -is taken from the upstream stable version ``sagenb-0.6`` without any -patches applied to its source code. So you do not see any patch-level -numbering such as ``.p0`` or ``.p1``. - -Say you start with ``matplotlib-1.0.1.p0`` and you want to replace -Matplotlib 1.0.1 with version 1.0.2. This entails replacing the source -code for Matplotlib 1.0.1 under ``matplotlib-1.0.1.p0/src/`` with the -new source code. To start with, follow the naming conventions as -described in the section :ref:`section-old-spkg-patching-overview`. If -necessary, remove any obsolete patches and create any new ones, -placing them in the ``patches/`` directory. Modify the script -``spkg-install`` to take any changes to the patches into account; you -might also have to deal with changes to how the new version of the -source code builds. Then package your replacement spkg using the Sage -command line options ``--pkg`` or ``--pkg_nc`` (or tar and bzip2). - -To install your replacement spkg, you use:: - - sage -p http://URL/to/package-x.y.z.spkg - -or:: - - sage -p /path/to/package-x.y.z.spkg - -To compile Sage from source with the replacement (standard) spkg, -untar a Sage source tarball, remove the existing spkg under -``SAGE_ROOT/spkg/standard/``. In its place, put your replacement -spkg. Then execute ``make`` from ``SAGE_ROOT``. - diff --git a/src/doc/en/developer/portability_testing.rst b/src/doc/en/developer/portability_testing.rst new file mode 100644 index 00000000000..d4c625c5961 --- /dev/null +++ b/src/doc/en/developer/portability_testing.rst @@ -0,0 +1,1036 @@ +.. nodoctest + +.. highlight:: shell-session + +.. _chapter-portability_testing: + +============================= +Testing on multiple platforms +============================= + +Sage is intended to build and run on a variety of platforms, +including all major Linux distributions, as well as MacOS, and +Windows (with Cygwin). + +There is considerable variation among these platforms. +To ensure that Sage continues to build correctly on users' +machines, it is crucial to test changes to Sage, in particular +when external packages are added or upgraded, on a wide +spectrum of platforms. + +Sage patchbots +============== + +The `Sage patchbots `_ will +automatically test your Trac ticket by attempting an incremental build +of Sage and running doctests. + +Sage buildbots +============== + +The `Sage Release buildbot `_ +builds entire tarballs (e.g., all the development releases) on a +variety of machines. + +Developers' and users' tests on sage-release +============================================ + +Sage developers and users are encouraged to contribute to testing +releases that are announced on `Sage Release +`_ on their +machines and to report test results (success and failures) by +responding to the announcements. + +Testing Sage on a different platform using Docker +================================================= + +`Docker `_ is a popular virtualization +software, running Linux operating system images ("Docker images") in +containers on a shared Linux kernel. These containers can be run +using a Docker client on your Linux, Mac, or Windows box, as well as +on various cloud services. + +To get started, you need to install a `Docker client +`_. The clients are available for +Linux, Mac, and Windows. The clients for the latter are known as +"Docker Desktop". + +All examples in this section were obtained using Docker Desktop for +Mac; but the `command-line user interface +`_ for the +other platforms is identical. + +All major Linux distributions provide ready-to-use Docker images, +which are published via `Docker Hub `_. For +example, to run the current stable (LTS) version of Ubuntu +interactively, you can use the shell command:: + + [mkoeppe@sage sage]$ docker run -it ubuntu:latest + root@9f3398da43c2:/# + +Here ``ubuntu`` is referred to as the "image (name)" and ``latest`` as +the "tag". Other releases of Ubuntu are available under different +tags, such as ``xenial`` or ``devel``. + +The above command drops you in a root shell on the container:: + + root@9f3398da43c2:/# uname -a + Linux 9f3398da43c2 4.19.76-linuxkit #1 SMP Thu Oct 17 19:31:58 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux + root@9f3398da43c2:/# df -h + Filesystem Size Used Avail Use% Mounted on + overlay 181G 116G 56G 68% / + tmpfs 64M 0 64M 0% /dev + tmpfs 2.7G 0 2.7G 0% /sys/fs/cgroup + shm 64M 0 64M 0% /dev/shm + /dev/sda1 181G 116G 56G 68% /etc/hosts + tmpfs 2.7G 0 2.7G 0% /proc/acpi + tmpfs 2.7G 0 2.7G 0% /sys/firmware + +Exiting the shell terminates the container:: + + root@9f3398da43c2:/# ^D + [mkoeppe@sage sage]$ + +Let us work with a distclean Sage source tree. If you are using git, +a good way to get one (without losing a precious installation in +``SAGE_LOCAL``) is by creating a new worktree:: + + [mkoeppe@sage sage] git worktree add worktree-ubuntu-latest + [mkoeppe@sage sage] cd worktree-ubuntu-latest + [mkoeppe@sage worktree-ubuntu-latest] ls + COPYING.txt ... Makefile ... configure.ac ... src tox.ini + +This is not bootstrapped (``configure`` is missing), so let's bootstrap it:: + + [mkoeppe@sage worktree-ubuntu-latest] make configure + ... + +We can start a container again with same image, ``ubuntu:latest``, but +this time let's mount the current directory into it:: + + [mkoeppe@sage worktree-ubuntu-latest]$ docker run -it --mount type=bind,source=$(pwd),target=/sage ubuntu:latest + root@39d693b2a75d:/# mount | grep sage + osxfs on /sage type fuse.osxfs (rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other,max_read=1048576) + root@39d693b2a75d:/# cd sage + root@39d693b2a75d:/sage# ls + COPYING.txt ... Makefile ... config configure configure.ac ... src tox.ini + +Typical Docker images provide minimal installations of packages only:: + + root@39d693b2a75d:/sage# command -v python + root@39d693b2a75d:/sage# command -v gcc + root@39d693b2a75d:/sage# + +As you can see above, the image ``ubuntu:latest`` has neither a Python nor +a GCC installed, which are among the build prerequisites of Sage. We +need to install them using the distribution's package manager first. + +Sage facilitates testing various distributions on Docker as follows. + +Discovering the system's package system +--------------------------------------- + +:: + + root@39d693b2a75d:/sage# build/bin/sage-guess-package-system + debian + +Let's install gcc, hoping that the Ubuntu package providing it is +simply named ``gcc``. If we forgot what the package manager on +Debian-derived distributions is called, we can ask Sage for a +reminder:: + + root@39d693b2a75d:/sage# build/bin/sage-print-system-package-command debian install gcc + sudo apt-get install gcc + +We are already root, so we can drop the ``sudo``, of course. +And we remember that we need to fetch the current package lists +from the server first:: + + root@39d693b2a75d:/sage# apt-get update + root@39d693b2a75d:/sage# apt-get install gcc + +Using Sage's database of distribution prerequisites +--------------------------------------------------- + +The source code of the Sage distribution contains a database of +package names in various distributions' package managers. For +example, the file ``build/pkgs/debian.txt`` contains the following + +.. code-block:: yaml + + # This file, build/pkgs/debian.txt, contains names of Debian/Ubuntu packages + # needed for installation of Sage from source. + # + # In addition, the files build/pkgs/SPKG/debian.txt contain the names + # of packages that provide the equivalent of SPKG. + # + # Everything on a line after a # character is ignored. + binutils + make + m4 + perl + # python3-minimal is not enough on debian buster, ubuntu bionic - it does not have urllib + python3 # system python for bootstrapping the build + tar + bc + gcc + # On debian buster, need C++ even to survive 'configure'. Otherwise: + # checking how to run the C++ preprocessor... /lib/cpp + # configure: error: in `/sage': + # configure: error: C++ preprocessor "/lib/cpp" fails sanity check + g++ + # Needed if we download some packages from a https upstream URL + ca-certificates + +From this information, we know that we can use the following command +on our container to install the necessary build prerequisites:: + + root@39d693b2a75d:/sage# apt-get install binutils make m4 perl python3 tar bc gcc g++ ca-certificates + Reading package lists... Done + Building dependency tree + Reading state information... Done + tar is already the newest version (1.29b-2ubuntu0.1). + The following additional packages will be installed: + ... + Done. + +(The Sage `Installation Guide <../installation/index.html>`_ also +provides such command lines for some distributions; these are +automatically generated from the database of package names.) + +Now we can start the build:: + + root@39d693b2a75d:/sage# ./configure + checking for a BSD-compatible install... /usr/bin/install -c + checking for root user... yes + configure: error: You cannot build Sage as root, switch to an unprivileged user. (If building in a container, use --enable-build-as-root.) + +Let's just follow this helpful hint:: + + root@39d693b2a75d:/sage# ./configure --enable-build-as-root + checking for a BSD-compatible install... /usr/bin/install -c + ... + + +Using Sage's database of equivalent distribution packages +--------------------------------------------------------- + +At the end of the ``./configure`` run, Sage issued a message like the +following:: + + configure: notice: the following SPKGs did not find equivalent system packages: arb boost boost_cropped bzip2 ... yasm zeromq zlib + checking for the package system in use... debian + configure: hint: installing the following system packages is recommended and may avoid building some of the above SPKGs from source: + configure: $ sudo apt-get install libflint-arb-dev ... yasm libzmq3-dev libz-dev + configure: After installation, re-run configure using: + configure: $ ./config.status --recheck && ./config.status + +This information comes from Sage's database of equivalent distribution +packages. For example:: + + root@39d693b2a75d:/sage# ls build/pkgs/arb/distros/ + arch.txt conda.txt debian.txt gentoo.txt + root@39d693b2a75d:/sage# cat build/pkgs/arb/distros/debian.txt + libflint-arb-dev + +Note that these package equivalencies are based on a current stable or +testing version of the distribution; the packages are not guaranteed +to exist in every release or derivative distribution. + +The Sage distribution is intended to build correctly no matter what +superset of the set of packages forming the minimal build +prerequisites is installed on the system. If it does not, this is a +bug of the Sage distribution and should be reported and fixed on a +ticket. Crucial part of a bug report is the configuration of the +system, in particular a list of installed packages and their versions. + +Let us install a subset of these packages:: + + root@39d693b2a75d:/sage# apt-get install libbz2-dev bzip2 yasm libz-dev + Reading package lists... Done + ... + Setting up zlib1g-dev:amd64 (1:1.2.11.dfsg-0ubuntu2) ... + root@39d693b2a75d:/sage# + + +Committing a container to disk +------------------------------ + +After terminating the container, we can create a new image +corresponding to its current state:: + + root@39d693b2a75d:/sage# ^D + [mkoeppe@sage worktree-ubuntu-latest]$ docker ps -a | head -n3 + CONTAINER ID IMAGE COMMAND CREATED STATUS + 39d693b2a75d ubuntu:latest "/bin/bash" 8 minutes ago Exited (0) 6 seconds ago + 9f3398da43c2 ubuntu:latest "/bin/bash" 8 minutes ago Exited (0) 8 minutes ago + [mkoeppe@sage worktree-ubuntu-latest]$ docker commit 39d693b2a75d ubuntu-latest-minimal-17 + sha256:4151c5ca4476660f6181cdb13923da8fe44082222b984c377fb4fd6cc05415c1 + +Here, ``39d693b2a75d`` was the container id (which appeared in the +shell prompts and in the output of ``docker ps``), and +``ubuntu-latest-minimal-17`` is an arbitrary symbolic name for the new +image. The output of the command is the id of the new image. We can +use either the symbolic name or the id to refer to the new image. + +We can run the image and get a new container with the same state as +the one that we terminated. Again we want to mount our worktree into +it; otherwise, because we did not make a copy, the new container will +have no access to the worktree:: + + [mkoeppe@sage worktree-ubuntu-latest]$ docker run -it \ + --mount type=bind,source=$(pwd),target=/sage ubuntu-latest-minimal-17 + root@73987568712c:/# cd sage + root@73987568712c:/sage# command -v gcc + /usr/bin/gcc + root@73987568712c:/sage# command -v yasm + /usr/bin/yasm + root@73987568712c:/sage# ^D + [mkoeppe@sage worktree-ubuntu-latest]$ + +The image ``ubuntu-latest-minimal-17`` can be run in as many +containers as we want and can also be shared with other users or +developers so that they can run it in a container on their machine. +(See the Docker documentation on how to `share images on Docker Hub +`_ or to `save images to a +tar archive +`_.) + +This facilitates collaboration on fixing portability bugs of the Sage +distribution. After reproducing a portability bug on a container, +several developers can work on fixing the bug using containers running +on their respective machines. + + +Generating Dockerfiles +---------------------- + +Sage also provides a script for generating a ``Dockerfile``, which is +a recipe for automatically building a new image:: + + [mkoeppe@sage sage]$ build/bin/write-dockerfile.sh debian "@(standard|optional)" > Dockerfile + +(The second argument is a bash extglob pattern for the package type.) + +.. this interface should be improved obviously. See #29146 - Refactor tox.ini and build/bin/write_dockerfile.sh + +The ``Dockerfile`` instructs the command ``docker build`` to build a +new Docker image. Let us take a quick look at the generated file; +this is slightly simplified:: + + [mkoeppe@sage sage]$ cat Dockerfile + # Automatically generated by SAGE_ROOT/build/bin/write-dockerfile.sh + # the :comments: separate the generated file into sections + # to simplify writing scripts that customize this file + ... + +First, it instructs ``docker build`` to start from an existing base +image...:: + + ... + ARG BASE_IMAGE=ubuntu:latest + FROM ${BASE_IMAGE} + ... + +Then, to install system packages...:: + + ... + RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -qqq --no-install-recommends --yes binutils make m4 perl python3 ... yasm libzmq3-dev libz-dev && apt-get clean + +Then, to bootstrap and configure...:: + + RUN mkdir -p /sage + WORKDIR /sage + ADD Makefile VERSION.txt README.md bootstrap configure.ac sage ./ + ADD src/doc/bootstrap src/doc/bootstrap + ADD m4 ./m4 + ADD build ./build + RUN ./bootstrap + ADD src/bin src/bin + ARG EXTRA_CONFIGURE_ARGS="" + RUN ./configure --enable-build-as-root ${EXTRA_CONFIGURE_ARGS} || (cat config.log; exit 1) + +Finally, to build and test...:: + + ARG NUMPROC=8 + ENV MAKE="make -j${NUMPROC}" + ARG USE_MAKEFLAGS="-k" + RUN make ${USE_MAKEFLAGS} base-toolchain + ARG TARGETS_PRE="sagelib-build-deps" + RUN make ${USE_MAKEFLAGS} ${TARGETS_PRE} + ADD src src + ARG TARGETS="build ptest" + RUN make ${USE_MAKEFLAGS} ${TARGETS} + +You can customize the image build process by passing build arguments to the +command ``docker build``. For example:: + + [mkoeppe@sage sage]$ docker build . -f Dockerfile \ + --build-arg BASE_IMAGE=ubuntu:latest \ + --build-arg NUMPROC=4 \ + --build-arg EXTRA_CONFIGURE_ARGS="--with-python=2" + +These arguments (and their default values) are defined using ``ARG`` +commands in the ``Dockerfile``. + +The above command will build Sage from scratch and will therefore take +quite long. Let us instead just do a partial build, consisting of one +small package, by setting the arguments ``TARGETS_PRE`` and +``TARGETS``. We use a silent build (``make V=0``):: + + [mkoeppe@sage sage]$ docker build . -f Dockerfile \ + --build-arg TARGETS_PRE=ratpoints \ + --build-arg TARGETS=ratpoints \ + --build-arg USE_MAKEFLAGS="V=0" + Sending build context to Docker daemon 285MB + Step 1/28 : ARG BASE_IMAGE=ubuntu:latest + ... + Step 2/28 : FROM ${BASE_IMAGE} + ---> 549b9b86cb8d + ... + Step 25/28 : RUN make SAGE_SPKG="sage-spkg -y -o" ${USE_MAKEFLAGS} ${TARGETS_PRE} + ... + make[1]: Entering directory '/sage/build/make' + sage-logger -p 'sage-spkg -y -o ratpoints-2.1.3.p5' '/sage/logs/pkgs/ratpoints-2.1.3.p5.log' + [ratpoints-2.1.3.p5] installing. Log file: /sage/logs/pkgs/ratpoints-2.1.3.p5.log + [ratpoints-2.1.3.p5] successfully installed. + make[1]: Leaving directory '/sage/build/make' + + real 0m18.886s + user 0m1.779s + sys 0m0.314s + Sage build/upgrade complete! + ... + ---> 2d06689d39fa + Successfully built 2d06689d39fa + +We can now start a container using the image id shown in the last step:: + + [mkoeppe@sage sage]$ docker run -it 2d06689d39fa bash + root@fab59e09a641:/sage# ls -l logs/pkgs/ + total 236 + -rw-r--r-- 1 root root 231169 Mar 26 22:07 config.log + -rw-r--r-- 1 root root 6025 Mar 26 22:27 ratpoints-2.1.3.p5.log + root@fab59e09a641:/sage# ls -l local/lib/*rat* + -rw-r--r-- 1 root root 177256 Mar 26 22:27 local/lib/libratpoints.a + +You can customize the image build process further by editing the +``Dockerfile``. For example, by default, the generated ``Dockerfile`` +configures, builds, and tests Sage. By deleting or commenting out the +commands for the latter, you can adjust the Dockerfile to stop after +the ``configure`` phase, for example. + +``Dockerfile`` is the default filename for Dockerfiles. You can +change it to any other name, but it is recommended to use +``Dockerfile`` as a prefix, such as ``Dockerfile-debian-standard``. +It should be placed within the tree rooted at the current directory +(``.``); if you want to put it elsewhere, you need to learn about +details of "Docker build contexts". + +Note that in contrast to the workflow described in the above sections, +the ``Dockerfile`` **copies** a snapshot of your Sage worktree into +the build container, using ``ADD`` commands, instead of mounting the +directory into it. This copying is subject to the exclusions in the +``.gitignore`` file (via a symbolic link from ``.dockerignore``). +Therefore, only the sources are copied, but not your configuration +(such as the file ``config.status``), nor the ``$SAGE_LOCAL`` tree, +nor any other build artefacts. + +Because of this, you can build a Docker image using the generated +``Dockerfile`` from your main Sage development tree. It does not have +to be distclean to start, and the build will not write into it at all. +Hence, you can continue editing and compiling your Sage development +tree even while Docker builds are running. + + +Debugging a portability bug using Docker +---------------------------------------- + +Let us do another partial build. We choose a package that we suspect +might not work on all platforms, ``surf``, which was marked as +"experimental" in 2017:: + + [mkoeppe@sage sage]$ docker build . -f Dockerfile \ + --build-arg BASE_IMAGE=ubuntu:latest \ + --build-arg NUMPROC=4 \ + --build-arg TARGETS_PRE=surf \ + --build-arg TARGETS=surf + Sending build context to Docker daemon 285MB + Step 1/28 : ARG BASE_IMAGE=ubuntu:latest + Step 2/28 : FROM ${BASE_IMAGE} + ---> 549b9b86cb8d + ... + Step 24/28 : ARG TARGETS_PRE="sagelib-build-deps" + ---> Running in 17d0ddb5ad7b + Removing intermediate container 17d0ddb5ad7b + ---> 7b51411520c3 + Step 25/28 : RUN make SAGE_SPKG="sage-spkg -y -o" ${USE_MAKEFLAGS} ${TARGETS_PRE} + ---> Running in 61833bea6a6d + make -j4 build/make/Makefile --stop + ... + [surf-1.0.6-gcc6] Attempting to download package surf-1.0.6-gcc6.tar.gz from mirrors + ... + [surf-1.0.6-gcc6] http://mirrors.mit.edu/sage/spkg/upstream/surf/surf-1.0.6-gcc6.tar.gz + ... + [surf-1.0.6-gcc6] Setting up build directory for surf-1.0.6-gcc6 + ... + [surf-1.0.6-gcc6] /usr/bin/ld: cannot find -lfl + [surf-1.0.6-gcc6] collect2: error: ld returned 1 exit status + [surf-1.0.6-gcc6] Makefile:504: recipe for target 'surf' failed + [surf-1.0.6-gcc6] make[3]: *** [surf] Error 1 + ... + [surf-1.0.6-gcc6] Error installing package surf-1.0.6-gcc6 + ... + Makefile:2088: recipe for target '/sage/local/var/lib/sage/installed/surf-1.0.6-gcc6' failed + make[1]: *** [/sage/local/var/lib/sage/installed/surf-1.0.6-gcc6] Error 1 + make[1]: Target 'surf' not remade because of errors. + make[1]: Leaving directory '/sage/build/make' + ... + Error building Sage. + + The following package(s) may have failed to build (not necessarily + during this run of 'make surf'): + + * package: surf-1.0.6-gcc6 + last build time: Mar 26 22:07 + log file: /sage/logs/pkgs/surf-1.0.6-gcc6.log + build directory: /sage/local/var/tmp/sage/build/surf-1.0.6-gcc6 + + ... + Makefile:31: recipe for target 'surf' failed + make: *** [surf] Error 1 + The command '/bin/sh -c make SAGE_SPKG="sage-spkg -y -o" ${USE_MAKEFLAGS} ${TARGETS_PRE}' returned a non-zero code: 2 + +Note that no image id is shown at the end; the build failed, and no +image is created. However, the container in which the last step of +the build was attempted exists:: + + [mkoeppe@sage sage]$ docker ps -a |head -n3 + CONTAINER ID IMAGE COMMAND CREATED STATUS + 61833bea6a6d 7b51411520c3 "/bin/sh -c 'make SA…" 9 minutes ago Exited (2) 1 minute ago + 73987568712c ubuntu-latest-minimal-17 "/bin/bash" 24 hours ago Exited (0) 23 hours ago + +We can copy the build directory from the container for inspection:: + + [mkoeppe@sage sage]$ docker cp 61833bea6a6d:/sage/local/var/tmp/sage/build ubuntu-build + [mkoeppe@sage sage]$ ls ubuntu-build/surf*/src + AUTHORS TODO curve misc + COPYING acinclude.m4 debug missing + ChangeLog aclocal.m4 dither mkinstalldirs + INSTALL background.pic docs mt + Makefile config.guess draw src + Makefile.am config.log drawfunc surf.1 + Makefile.global config.status examples surf.xpm + Makefile.in config.sub gtkgui yaccsrc + NEWS configure image-formats + README configure.in install-sh + +Alternatively, we can use ``docker commit`` as explained earlier to +create an image from the container:: + + [mkoeppe@sage sage]$ docker commit 61833bea6a6d + sha256:003fbd511016fe305bd8494bb1747f0fbf4cb2c788b4e755e9099d9f2014a60d + [mkoeppe@sage sage]$ docker run -it 003fbd511 bash + root@2d9ac65f4572:/sage# (cd /sage/local/var/tmp/sage/build/surf* && /sage/sage --buildsh) + + Starting subshell with Sage environment variables set. Don't forget + to exit when you are done. + ... + Note: SAGE_ROOT=/sage + (sage-buildsh) root@2d9ac65f4572:surf-1.0.6-gcc6$ ls /usr/lib/libfl* + /usr/lib/libflint-2.5.2.so /usr/lib/libflint-2.5.2.so.13.5.2 /usr/lib/libflint.a /usr/lib/libflint.so + (sage-buildsh) root@2d9ac65f4572:surf-1.0.6-gcc6$ apt-get update && apt-get install apt-file + (sage-buildsh) root@2d9ac65f4572:surf-1.0.6-gcc6$ apt-file update + (sage-buildsh) root@2d9ac65f4572:surf-1.0.6-gcc6$ apt-file search "/usr/lib/libfl.a" + flex-old: /usr/lib/libfl.a + freebsd-buildutils: /usr/lib/libfl.a + (sage-buildsh) root@2d9ac65f4572:surf-1.0.6-gcc6$ apt-get install flex-old + (sage-buildsh) root@2d9ac65f4572:surf-1.0.6-gcc6$ ./spkg-install + checking for a BSD-compatible install... /usr/bin/install -c + checking whether build environment is sane... yes + ... + /usr/bin/install -c surf /sage/local/bin/surf + /usr/bin/install -c -m 644 ./surf.1 /sage/local/share/man/man1/surf.1 + ... + make[1]: Leaving directory '/sage/local/var/tmp/sage/build/surf-1.0.6-gcc6/src' + (sage-buildsh) root@2d9ac65f4572:surf-1.0.6-gcc6$ exit + root@2d9ac65f4572:/sage# exit + [mkoeppe@sage sage]$ + +A standard case of bitrot. + + +Automatic Docker-based build testing using tox +---------------------------------------------- + +`tox `_ is a Python package that +is widely used for automating tests of Python projects. + +Install ``tox`` for use with your system Python, for example using:: + + [mkoeppe@sage sage]$ pip install --user tox + +A tox "environment" is identified by a symbolic name composed of +several `Tox "factors" +`_, +which are defined in the file ``$SAGE_ROOT/tox.ini``. + +The **technology** factor describes how the environment is run: + +- ``docker`` builds a Docker image as described above. + +- ``local`` runs testing on the host OS instead. We explain this + technology in a later section. + +The next two factors determine the host system configuration: The +**system factor** describes a base operating system image. + +- Examples are ``ubuntu-focal``, ``debian-buster``, + ``archlinux-latest``, ``fedora-30``, ``slackware-14.2``, + ``centos-7-i386``, and ``ubuntu-bionic-arm64``. + +- See ``$SAGE_ROOT/tox.ini`` for a complete list, and to which images + on Docker hub they correspond. + +The **packages factor** describes a list of system packages to be +installed on the system before building Sage: + +- ``minimal`` installs the system packages known to Sage to provide + minimal prerequisites for bootstrapping and building the Sage + distribution. + +- ``standard`` additionally installs all known system packages that + are equivalent to standard packages of the Sage distribution, for + which the mechanism ``spkg-configure.m4`` is implemented. + This corresponds to the type pattern ``@(standard)``. + +- ``maximal`` does the same for all standard and optional packages. + This corresponds to the type pattern ``@(standard|optional)``. + +The factors are connected by a hyphen to name a system configuration, +such as ``debian-buster-standard`` and ``centos-7-i386-minimal``. + +Finally, the **configuration** factor (which is allowed to be empty) +controls how the ``configure`` script is run. + +- ``python2`` adds the argument ``--with-python=2`` to the + ``configure`` run. + +The factors are connected by a hyphen to name a tox environment. (The +order of the factors does not matter; however, for consistency and +because the ordered name is used for caching purposes, we recommend to +use the factors in the listed order.) + +To run an environment:: + + [mkoeppe@sage sage]$ tox -e docker-slackware-14.2-minimal + [mkoeppe@sage sage]$ tox -e docker-ubuntu-bionic-standard-python2 + +Arbitrary extra arguments to ``docker build`` can be supplied through +the environment variable ``EXTRA_DOCKER_BUILD_ARGS``. For example, +for a non-silent build (``make V=1``), use:: + + [mkoeppe@sage sage]$ EXTRA_DOCKER_BUILD_ARGS="--build-arg USE_MAKEFLAGS=\"V=1\"" \ + tox -e docker-ubuntu-bionic-standard + +By default, tox uses ``TARGETS_PRE=sagelib-build-deps`` and +``TARGETS=build``, leading to a complete build of Sage without the +documentation. If you pass positional arguments to tox (separated +from tox options by ``--``), then both ``TARGETS_PRE`` and ``TARGETS`` +are set to these arguments. In this way, you can build some specific +packages instead of all of Sage, for example:: + + [mkoeppe@sage sage]$ tox -e docker-centos-8-standard -- ratpoints + +If the build succeeds, this will create a new image named +``sage-docker-centos-8-standard-with-targets:9.1.beta9-431-gca4b5b2f33-dirty``, +where + +- the image name is derived from the tox environment name and the + suffix ``with-targets`` expresses that the ``make`` targets given in + ``TARGETS`` have been built; + +- the tag name describes the git revision of the source tree as per + ``git describe --dirty``. + +You can ask for tox to create named intermediate images as well. For +example, to create the images corresponding to the state of the OS +after installing all system packages (``with-system-packages``) and +the one just after running the ``configure`` script (``configured``):: + + [mkoeppe@sage sage]$ DOCKER_TARGETS="with-system-packages configured with-targets" \ + tox -e docker-centos-8-standard -- ratpoints + ... + Sending build context to Docker daemon ... + Step 1/109 : ARG BASE_IMAGE=fedora:latest + Step 2/109 : FROM ${BASE_IMAGE} as with-system-packages + ... + Step 109/109 : RUN yum install -y zlib-devel || echo "(ignoring error)" + ... + Successfully built 4bb14c3d5646 + Successfully tagged sage-docker-centos-8-standard-with-system-packages:9.1.beta9-435-g861ba33bbc-dirty + Sending build context to Docker daemon ... + ... + Successfully tagged sage-docker-centos-8-standard-configured:9.1.beta9-435-g861ba33bbc-dirty + ... + Sending build context to Docker daemon ... + ... + Successfully tagged sage-docker-centos-8-standard-with-targets:9.1.beta9-435-g861ba33bbc-dirty + +Let's verify that the images are available:: + + (base) egret:~/s/sage/sage-rebasing/worktree-algebraic-2018-spring (mkoeppe *$%>)$ docker images | head + REPOSITORY TAG IMAGE ID + sage-docker-centos-8-standard-with-targets 9.1.beta9-435-g861ba33bbc-dirty 7ecfa86fceab + sage-docker-centos-8-standard-configured 9.1.beta9-435-g861ba33bbc-dirty 4314929e2b4c + sage-docker-centos-8-standard-with-system-packages 9.1.beta9-435-g861ba33bbc-dirty 4bb14c3d5646 + ... + + +Automatic build testing on the host OS using tox -e local-direct +---------------------------------------------------------------- + +The ``local`` technology runs testing on the host OS instead. + +In contrast to the ``docker`` technology, it does not make a copy of +the source tree. It is most straightforward to run it from a +separate, distclean git worktree. + +Let us try a first variant of the ``local`` technology, the tox +environment called ``local-direct``. Because all builds with tox +begin by bootstrapping the source tree, you will need autotools and +other prerequisites installed in your system. See +``build/pkgs/*-bootstrap.txt`` for a list of system packages that +provide these prerequisites. + +We start by creating a fresh (distclean) git worktree. + + [mkoeppe@sage sage] git worktree add worktree-local + [mkoeppe@sage sage] cd worktree-local + [mkoeppe@sage worktree-local] ls + COPYING.txt ... Makefile ... configure.ac ... src tox.ini + +Again we build only a small package. Build targets can be passed as +positional arguments (separated from tox options by ``--``):: + + [mkoeppe@sage worktree-local] tox -e local-direct -- ratpoints + local-direct create: /Users/mkoeppe/.../worktree-local/.tox/local-direct + local-direct run-test-pre: PYTHONHASHSEED='2211987514' + ... + src/doc/bootstrap:48: installing src/doc/en/installation/debian.txt... + bootstrap:69: installing 'config/config.rpath' + configure.ac:328: installing 'config/compile' + configure.ac:113: installing 'config/config.guess' + ... + checking for a BSD-compatible install... /usr/bin/install -c + checking whether build environment is sane... yes + ... + sage-logger -p 'sage-spkg -y -o ratpoints-2.1.3.p5' '.../worktree-local/logs/pkgs/ratpoints-2.1.3.p5.log' + [ratpoints-2.1.3.p5] installing. Log file: .../worktree-local/logs/pkgs/ratpoints-2.1.3.p5.log + [ratpoints-2.1.3.p5] successfully installed. + ... + local-direct: commands succeeded + congratulations :) + +Let's investigate what happened here:: + + [mkoeppe@sage worktree-local]$ ls -la + total 2576 + drwxr-xr-x 35 mkoeppe staff 1120 Mar 26 22:20 . + drwxr-xr-x 63 mkoeppe staff 2016 Mar 27 09:35 .. + ... + lrwxr-xr-x 1 mkoeppe staff 10 Mar 26 20:34 .dockerignore -> .gitignore + -rw-r--r-- 1 mkoeppe staff 74 Mar 26 20:34 .git + ... + -rw-r--r-- 1 mkoeppe staff 1212 Mar 26 20:41 .gitignore + ... + drwxr-xr-x 7 mkoeppe staff 224 Mar 26 22:11 .tox + ... + -rw-r--r-- 1 mkoeppe staff 7542 Mar 26 20:41 Makefile + ... + lrwxr-xr-x 1 mkoeppe staff 114 Mar 26 20:45 config.log -> .tox/local-direct/log/config.log + -rwxr-xr-x 1 mkoeppe staff 90411 Mar 26 20:46 config.status + -rwxr-xr-x 1 mkoeppe staff 887180 Mar 26 20:45 configure + -rw-r--r-- 1 mkoeppe staff 17070 Mar 26 20:41 configure.ac + ... + lrwxr-xr-x 1 mkoeppe staff 103 Mar 26 20:45 logs -> .tox/local-direct/log + drwxr-xr-x 24 mkoeppe staff 768 Mar 26 20:45 m4 + lrwxr-xr-x 1 mkoeppe staff 105 Mar 26 20:45 prefix -> .tox/local-direct/local + -rwxr-xr-x 1 mkoeppe staff 4868 Mar 26 20:34 sage + drwxr-xr-x 16 mkoeppe staff 512 Mar 26 20:46 src + -rw-r--r-- 1 mkoeppe staff 13478 Mar 26 20:41 tox.ini + drwxr-xr-x 4 mkoeppe staff 128 Mar 26 20:46 upstream + +There is no ``local`` subdirectory. This is part of a strategy to +keep the source tree clean to the extent possible. In particular: + +- ``tox`` configured the build to use a separate ``$SAGE_LOCAL`` + hierarchy in a directory under the tox environment directory + ``.tox/local-direct``. It created a symbolic link ``prefix`` that + points there, for convenience:: + + [mkoeppe@sage worktree-local]$ ls -l prefix/lib/*rat* + -rw-r--r-- 1 mkoeppe staff 165968 Mar 26 20:46 prefix/lib/libratpoints.a + +- Likewise, it created a separate ``logs`` directory, again under the + tox environment directory, and a symbolic link. + +This makes it possible for advanced users to test several ``local`` +tox environments (such as ``local-direct`` and +``local-direct-python2``) out of one worktree. However, because a +build still writes configuration scripts and build artefacts (such as +``config.status``) into the worktree, only one ``local`` build can run +at a time in a given worktree. + +The tox environment directory will be reused for the next ``tox`` run, +which will therefore do an incremental build. To start a fresh build, +you can use the ``-r`` option. + +Automatic build testing on the host OS with best-effort isolation using tox -e local +------------------------------------------------------------------------------------ + +``tox -e local`` (without ``-direct``) attempts a best-effort +isolation from the user's environment as follows: + +- All environment variables are set to standard values; with the + exception of ``MAKE`` and ``EXTRA_CONFIGURE_ARGS``. In particular, + ``PATH`` is set to just ``/usr/bin:/bin:/usr/sbin:/sbin``; it does + not include ``/usr/local/bin``. + + +Note, however, that various packages have build scripts that use +``/usr/local`` or other popular file system locations such as +``/opt/sfw/``. Therefore, the isolation is not complete. Using +``/usr/local`` is considered standard behavior. On the other hand, we +consider a package build script that inspects other file system +locations to be a bug of the Sage distribution, which should be +reported and fixed on a ticket. + + +Automatic build testing on macOS with a best-effort isolated installation of Homebrew +------------------------------------------------------------------------------------- + +XCode on macOS does not provide the prerequisites for bootstrapping +the Sage distribution. A good way to install them is using the +Homebrew package manager. + +In fact, Sage provides a tox environment that automatically installs +an isolated copy of Homebrew with all prerequisites for bootstrapping:: + + [mkoeppe@sage worktree-local]$ tox -e local-homebrew-macos-minimal -- lrslib + local-homebrew-macos-minimal create: .../worktree-local/.tox/local-homebrew-macos-minimal + local-homebrew-macos-minimal run-test-pre: PYTHONHASHSEED='4246149402' + ... + Initialized empty Git repository in .../worktree-local/.tox/local-homebrew-macos-minimal/homebrew/.git/ + ... + Tapped 2 commands and 4942 formulae (5,205 files, 310.7MB). + ==> Downloading https://ftp.gnu.org/gnu/gettext/gettext-0.20.1.tar.xz + ... + ==> Pouring autoconf-2.69.catalina.bottle.4.tar.gz + ... + ==> Pouring pkg-config-0.29.2.catalina.bottle.1.tar.gz + .../worktree-local/.tox/local-homebrew-macos-minimal/homebrew/Cellar/pkg-config/0.29.2: 11 files, 623.4KB + ==> Caveats + ==> gettext + gettext is keg-only, which means it was not symlinked into .../worktree-local/.tox/local-homebrew-macos-minimal/homebrew, + because macOS provides the BSD gettext library & some software gets confused if both are in the library path. + + If you need to have gettext first in your PATH run: + echo 'export PATH=".../worktree-local/.tox/local-homebrew-macos-minimal/homebrew/opt/gettext/bin:$PATH"' >> ~/.bash_profile + + For compilers to find gettext you may need to set: + export LDFLAGS="-L.../worktree-local/.tox/local-homebrew-macos-minimal/homebrew/opt/gettext/lib" + export CPPFLAGS="-I.../worktree-local/.tox/local-homebrew-macos-minimal/homebrew/opt/gettext/include" + ... + local-homebrew-macos-minimal run-test: commands[0] | bash -c 'export PATH=.../worktree-local/.tox/local-homebrew-macos-minimal/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin && . .homebrew-build-env && ./bootstrap && ./configure --prefix=.../worktree-local/.tox/local-homebrew-macos-minimal/local && make -k V=0 ... lrslib' + ... + bootstrap:69: installing 'config/config.rpath' + ... + checking for a BSD-compatible install... /usr/bin/install -c + checking whether build environment is sane... yes + ... + configure: notice: the following SPKGs did not find equivalent system packages: arb cbc cliquer ... tachyon xz yasm zeromq + checking for the package system in use... homebrew + configure: hint: installing the following system packages is recommended and may avoid building some of the above SPKGs from source: + configure: $ brew install cmake gcc gsl mpfi ninja openblas gpatch r readline xz yasm zeromq + ... + sage-logger -p 'sage-spkg -y -o lrslib-062+autotools-2017-03-03.p1' '.../worktree-local/logs/pkgs/lrslib-062+autotools-2017-03-03.p1.log' + [lrslib-062+autotools-2017-03-03.p1] installing. Log file: .../worktree-local/logs/pkgs/lrslib-062+autotools-2017-03-03.p1.log + [lrslib-062+autotools-2017-03-03.p1] successfully installed. + ... + local-homebrew-macos-minimal: commands succeeded + congratulations :) + +The tox environment uses the subdirectory ``homebrew`` of the +environment directory ``.tox/local-homebrew-macos-minimal`` as the +Homebrew prefix. This installation does not interact in any way with +a Homebrew installation in ``/usr/local`` that you may have. + +The test script sets the ``PATH`` to the ``bin`` directory of the +Homebrew prefix, followed by ``/usr/bin:/bin:/usr/sbin:/sbin``. It +then uses the script ``$SAGE_ROOT/.homebrew-build-env`` to set +environment variables so that Sage's build scripts will find +"keg-only" packages such as ``gettext``. + +The ``local-homebrew-macos-minimal`` environment does not install +Homebrew's ``python3`` package. It uses XCode's ``/usr/bin/python3`` +as system python. However, because various packages are missing +that Sage considers as dependencies, Sage builds its own copy of +these packages and of ``python3``. + +The ``local-homebrew-macos-standard`` environment additionally +installs (in its separate isolated copy of Homebrew) all Homebrew +packages known to Sage for which the ``spkg-configure.m4`` mechanism +is implemented; this is similar to the ``docker-standard`` tox +environments described earlier. In particular it installs and uses +Homebrew's ``python3`` package. + +By using configuration factors, more variants can be tested. +The ``local-homebrew-macos-standard-python3_xcode`` environment +installs the same packages, but uses XCode's ``/usr/bin/python3``. + +The ``local-homebrew-macos-standard-python3_pythonorg`` expects an +installation of Python 3.7 in +``/Library/Frameworks/Python.framework``; this is where the binary +packages provided by python.org install themselves. + + +Automatic build testing with a best-effort isolated installation of Conda +------------------------------------------------------------------------- + +Sage provides environments ``local-conda-forge-standard`` and +``local-conda-forge-minimal`` that create isolated installations of +Miniconda in the subdirectory ``conda`` of the environment directory. +They do not interact in any way with other installations of Anaconda +or Miniconda that you may have on your system. + +The environments use the conda-forge channel and use the ``python`` +package and the compilers from this channel. + + +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 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``. + +An additional GitHub Actions workflow for testing on Cygwin, not based +on tox, is defined in the file +``$SAGE_ROOT/.github/workflows/ci-cygwin.yml``. + +GitHub Actions runs these build jobs on 2-core machines with 7 GB of +RAM memory and 14 GB of SSD disk space, cf. +`here `_, +and has a time limit of 6h per job. This is just barely enough for a +typical ``minimal`` build followed by ``make ptest`` to succeed; and +plenty of time for a typical ``standard`` build to succeed. + +Build logs become available as "artifacts" when all jobs of the +workflow have finished. Each job generates one tarball. +"Annotations" highlight certain top-level errors or warnings issued +during the build. + +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. + +- Edit ``$SAGE_ROOT/.github/workflows/tox.yml`` to include the system + config you wish to test. + +- Commit and push the branch to your GitHub fork of sage. + +- Push your development branch to your GitHub repository and create a + 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 +pane if ``make build doc-html`` finished without error. (It also runs +package testsuites and the Sage doctests but failures in these are not +in reflected in the left pane; see below.) + +The right pane ("Artifacts") offers archives of the logs for download. + +Scrolling down in the right pane shows "Annotations": + +* Red "check failure" annotations appear for each log file that + contains a build error. For example, you might see:: + + docker (fedora-28, standard) + artifacts/logs-commit-8ca1c2df8f1fb4c6d54b44b34b4d8320ebecb164-tox-docker-fedora-28-standard/logs/pkgs/sagetex-3.4.log#L1 + ==== ERROR IN LOG FILE artifacts/logs-commit-8ca1c2df8f1fb4c6d54b44b34b4d8320ebecb164-tox-docker-fedora-28-standard/logs/pkgs/sagetex-3.4.log ==== + +* Yellow "check warning" annotations. There are 2 types of these: + + a) Package testsuite or Sage doctest failures, like the following:: + + docker (fedora-30, standard) + artifacts/logs-commit-8ca1c2df8f1fb4c6d54b44b34b4d8320ebecb164-tox-docker-fedora-30-standard/logs/ptest.log#L1 + ==== TESTSUITE FAILURE IN LOG FILE artifacts/logs-commit-8ca1c2df8f1fb4c6d54b44b34b4d8320ebecb164-tox-docker-fedora-30-standard/logs/ptest.log ==== + + b) Notices from ./configure about not finding equivalent system + packages, like the following:: + + docker (fedora-31, standard) + artifacts/logs-commit-8ca1c2df8f1fb4c6d54b44b34b4d8320ebecb164-tox-docker-fedora-31-standard/config.log#L1 + configure: notice: the following SPKGs did not find equivalent system packages: arb cbc cddlib cmake eclib ecm fflas_ffpack flint flintqs fplll givaro gp + +Clicking on the annotations does not take you to a very useful +place. To view details, click on one of the items in the pane. This +changes the right pane to a log viewer. + +The ``docker`` workflows automatically push images to +``docker.pkg.github.com``. You find them in the Packages tab of your +GitHub repository. + +In order to pull them for use on your computer, you need to first +generate a Personal Access Token providing the ``read:packages`` scope +as follows. Visit https://github.com/settings/tokens/new (this may +prompt you for your GitHub password). As "Note", type "Access +docker.pkg.github.com"; then in "Select scopes", select the checkbox +for ``read:packages``. Finally, push the "Generate token" button at +the bottom. This will lead to a page showing your token, such as +``de1ec7ab1ec0ffee5ca1dedbaff1ed0ddba11``. Copy this token and paste +it to the command line:: + + $ echo de1ec7ab1ec0ffee5ca1dedbaff1ed0ddba11 | docker login docker.pkg.github.com --username YOUR-GITHUB-USERNAME + +where you replace the token by your token, of course, and +``YOUR-GITHUB-USERNAME`` by your GitHub username. + +Now you can pull the image and run it:: + + $ docker pull docker.pkg.github.com/YOUR-GITHUB-USERNAME/sage/sage-docker-fedora-31-standard-configured:f4bd671 + $ docker run -it docker.pkg.github.com/YOUR-GITHUB-USERNAME/sage/sage-docker-fedora-31-standard-configured:f4bd671 bash diff --git a/src/doc/en/developer/sage_manuals.rst b/src/doc/en/developer/sage_manuals.rst index 65a82c5f2d5..047a949900b 100644 --- a/src/doc/en/developer/sage_manuals.rst +++ b/src/doc/en/developer/sage_manuals.rst @@ -45,9 +45,6 @@ Sage's manuals are written in `ReST `_ Editing the documentation ========================= -(*Do you want to convert a Sage worksheet into documentation?* `Click here -<../thematic_tutorials/sws2rst.html>`_) - After modifying some files in the Sage tutorial (``SAGE_ROOT/src/doc/en/tutorial/``), you will want to visualize the result. In order to build a **html** version of this document, type:: diff --git a/src/doc/en/developer/sagenb/development_workflow.rst b/src/doc/en/developer/sagenb/development_workflow.rst deleted file mode 100644 index 75c9fdccc08..00000000000 --- a/src/doc/en/developer/sagenb/development_workflow.rst +++ /dev/null @@ -1,415 +0,0 @@ -.. _development-workflow: - -#################### -Development Workflow -#################### - -You already have your own forked copy of the `Sage Notebook`_ repository, by -following :ref:`forking`. You have :ref:`set-up-fork`. You have configured -git by following :ref:`section-git-configuration`. Now you are ready for some real work. - -Workflow Summary -================ - -In what follows we'll refer to the upstream Sage Notebook ``master`` branch, as -"trunk". - -* Don't use your ``master`` branch for anything. Consider deleting it. -* When you are starting a new set of changes, fetch any changes from trunk, - and start a new *feature branch* from that. -* Make a new branch for each separable set of changes |emdash| "one task, one - branch" (`ipython git workflow`_). -* Name your branch for the purpose of the changes - e.g. - ``bugfix-for-issue-14`` or ``refactor-database-code``. -* If you can possibly avoid it, avoid merging trunk or any other branches into - your feature branch while you are working. -* If you do find yourself merging from trunk, consider :ref:`rebase-on-trunk` -* Ask on the `Sage Notebook mailing list`_ if you get stuck. -* Ask for code review! - -This way of working helps to keep work well organized, with readable history. -This in turn makes it easier for project maintainers (that might be you) to see -what you've done, and why you did it. - -See `linux git workflow`_ and `ipython git workflow`_ for some explanation. - -Consider Deleting Your Master Branch -==================================== - -It may sound strange, but deleting your own ``master`` branch can help reduce -confusion about which branch you are on. See `deleting master on github`_ for -details. - -.. _update-mirror-trunk: - -Update the Mirror of trunk -========================== - -First make sure you have done :ref:`linking-to-upstream`. - -From time to time you should fetch the upstream (trunk) changes from github:: - - git fetch upstream - -This will pull down any commits you don't have, and set the remote branches to -point to the right commit. For example, 'trunk' is the branch referred to by -(remote/branchname) ``upstream/master`` - and if there have been commits since -you last checked, ``upstream/master`` will change after you do the fetch. - -.. _make-feature-branch: - -Make a New Feature Branch -========================= - -When you are ready to make some changes to the code, you should start a new -branch. Branches that are for a collection of related edits are often called -'feature branches'. - -Making an new branch for each set of related changes will make it easier for -someone reviewing your branch to see what you are doing. - -Choose an informative name for the branch to remind yourself and the rest of us -what the changes in the branch are for. For example ``add-ability-to-fly``, or -``buxfix-for-issue-42``. - -:: - - # Update the mirror of trunk - git fetch upstream - # Make new feature branch starting at current trunk - git branch my-new-feature upstream/master - git checkout my-new-feature - -Generally, you will want to keep your feature branches on your public github_ -fork of `Sage Notebook`_. To do this, you `git push`_ this new branch up to your -github repo. Generally (if you followed the instructions in these pages, and by -default), git will have a link to your github repo, called ``origin``. You push -up to your own repo on github with:: - - git push origin my-new-feature - -In git >= 1.7 you can ensure that the link is correctly set by using the -``--set-upstream`` option:: - - git push --set-upstream origin my-new-feature - -From now on git will know that ``my-new-feature`` is related to the -``my-new-feature`` branch in the github repo. - -.. _edit-flow: - -The Editing Workflow -==================== - -Overview --------- - -:: - - # hack hack - git add my_new_file - git commit -am 'NF - some message' - git push - -In More Detail --------------- - -#. Make some changes -#. See which files have changed with ``git status`` (see `git status`_). - You'll see a listing like this one:: - - # On branch ny-new-feature - # Changed but not updated: - # (use "git add ..." to update what will be committed) - # (use "git checkout -- ..." to discard changes in working directory) - # - # modified: README - # - # Untracked files: - # (use "git add ..." to include in what will be committed) - # - # INSTALL - no changes added to commit (use "git add" and/or "git commit -a") - -#. Check what the actual changes are with ``git diff`` (`git diff`_). -#. Add any new files to version control ``git add new_file_name`` (see - `git add`_). -#. To commit all modified files into the local copy of your repo,, do - ``git commit -am 'A commit message'``. Note the ``-am`` options to - ``commit``. The ``m`` flag just signals that you're going to type a - message on the command line. The ``a`` flag |emdash| you can just take on - faith |emdash| or see `why the -a flag?`_ |emdash| and the helpful use-case - description in the `tangled working copy problem`_. The `git commit`_ manual - page might also be useful. -#. To push the changes up to your forked repo on github, do a ``git - push`` (see `git push`_). - -Ask for Your Changes to be Reviewed or Merged -============================================= - -When you are ready to ask for someone to review your code and consider a merge: - -#. Go to the URL of your forked repo, say - ``http://github.com/your-user-name/sagenb``. -#. Use the 'Switch Branches' dropdown menu near the top left of the page to - select the branch with your changes: - - .. image:: branch_dropdown.png - -#. Click on the 'Pull request' button: - - .. image:: pull_button.png - - Enter a title for the set of changes, and some explanation of what you've - done. Say if there is anything you'd like particular attention for - like a - complicated change or some code you are not happy with. - - If you don't think your request is ready to be merged, just say so in your - pull request message. This is still a good way of getting some preliminary - code review. - -Some Other Things You Might Want to Do -====================================== - -Delete a Branch on Github -------------------------- - -:: - - git checkout master - # delete branch locally - git branch -D my-unwanted-branch - # delete branch on github - git push origin :my-unwanted-branch - -(Note the colon ``:`` before ``test-branch``. See also: -http://github.com/guides/remove-a-remote-branch - -Several People Sharing a Single Repository ------------------------------------------- - -If you want to work on some stuff with other people, where you are all -committing into the same repository, or even the same branch, then just -share it via github. - -First fork Sage Notebook into your account, as from :ref:`forking`. - -Then, go to your forked repository github page, say -``http://github.com/your-user-name/sagenb`` - -Click on the 'Admin' button, and add anyone else to the repo as a -collaborator: - - .. image:: pull_button.png - -Now all those people can do:: - - git clone git@githhub.com:your-user-name/sagenb.git - -Remember that links starting with ``git@`` use the ssh protocol and are -read-write; links starting with ``git://`` are read-only. - -Your collaborators can then commit directly into that repo with the -usual:: - - git commit -am 'ENH - much better code' - git push origin master # pushes directly into your repo - -Explore Your Repository ------------------------ - -To see a graphical representation of the repository branches and -commits:: - - gitk --all - -To see a linear list of commits for this branch:: - - git log - -You can also look at the `network graph visualizer`_ for your github -repo. - -Finally the :ref:`section-fancy-log` ``lg`` alias will give you a -reasonable text-based graph of the repository. - -.. _rebase-on-trunk: - -Rebasing on trunk ------------------ - -Let's say you thought of some work you'd like to do. You -:ref:`update-mirror-trunk` and :ref:`make-feature-branch` called -``cool-feature``. At this stage trunk is at some commit, let's call it E. Now -you make some new commits on your ``cool-feature`` branch, let's call them A, B, -C. Maybe your changes take a while, or you come back to them after a while. In -the meantime, trunk has progressed from commit E to commit (say) G:: - - A---B---C cool-feature - / - D---E---F---G trunk - -At this stage you consider merging trunk into your feature branch, and you -remember that this here page sternly advises you not to do that, because the -history will get messy. Most of the time you can just ask for a review, and not -worry that trunk has got a little ahead. But sometimes, the changes in trunk -might affect your changes, and you need to harmonize them. In this situation -you may prefer to do a rebase. - -rebase takes your changes (A, B, C) and replays them as if they had been made to -the current state of ``trunk``. In other words, in this case, it takes the -changes represented by A, B, C and replays them on top of G. After the rebase, -your history will look like this:: - - A'--B'--C' cool-feature - / - D---E---F---G trunk - -See `rebase without tears`_ for more detail. - -To do a rebase on trunk:: - - # Update the mirror of trunk - git fetch upstream - # go to the feature branch - git checkout cool-feature - # make a backup in case you mess up - git branch tmp cool-feature - # rebase cool-feature onto trunk - git rebase --onto upstream/master upstream/master cool-feature - -In this situation, where you are already on branch ``cool-feature``, the last -command can be written more succinctly as:: - - git rebase upstream/master - -When all looks good you can delete your backup branch:: - - git branch -D tmp - -If it doesn't look good you may need to have a look at -:ref:`recovering-from-mess-up`. - -If you have made changes to files that have also changed in trunk, this may -generate merge conflicts that you need to resolve - see the `git rebase`_ man -page for some instructions at the end of the "Description" section. There is -some related help on merging in the git user manual - see `resolving a merge`_. - -.. _recovering-from-mess-up: - -Recovering From Mess-Ups ------------------------- - -Sometimes, you mess up merges or rebases. Luckily, in git it is -relatively straightforward to recover from such mistakes. - -If you mess up during a rebase:: - - git rebase --abort - -If you notice you messed up after the rebase:: - - # reset branch back to the saved point - git reset --hard tmp - -If you forgot to make a backup branch:: - - # look at the reflog of the branch - git reflog show cool-feature - - 8630830 cool-feature@{0}: commit: BUG: io: close file handles immediately - 278dd2a cool-feature@{1}: rebase finished: refs/heads/my-feature-branch onto 11ee694744f2552d - 26aa21a cool-feature@{2}: commit: BUG: lib: make seek_gzip_factory not leak gzip obj - ... - - # reset the branch to where it was before the botched rebase - git reset --hard cool-feature@{2} - -.. _rewriting-commit-history: - -Rewriting Commit History ------------------------- - -.. note:: - - Do this only for your own feature branches. - -There's an embarassing typo in a commit you made? Or perhaps the you -made several false starts you would like the posterity not to see. - -This can be done via *interactive rebasing*. - -Suppose that the commit history looks like this:: - - git log --oneline - eadc391 Fix some remaining bugs - a815645 Modify it so that it works - 2dec1ac Fix a few bugs + disable - 13d7934 First implementation - 6ad92e5 * masked is now an instance of a new object, MaskedConstant - 29001ed Add pre-nep for a copule of structured_array_extensions. - ... - -and ``6ad92e5`` is the last commit in the ``cool-feature`` branch. Suppose we -want to make the following changes: - -* Rewrite the commit message for ``13d7934`` to something more sensible. -* Combine the commits ``2dec1ac``, ``a815645``, ``eadc391`` into a single one. - -We do as follows:: - - # make a backup of the current state - git branch tmp HEAD - # interactive rebase - git rebase -i 6ad92e5 - -This will open an editor with the following text in it:: - - pick 13d7934 First implementation - pick 2dec1ac Fix a few bugs + disable - pick a815645 Modify it so that it works - pick eadc391 Fix some remaining bugs - - # Rebase 6ad92e5..eadc391 onto 6ad92e5 - # - # Commands: - # p, pick = use commit - # r, reword = use commit, but edit the commit message - # e, edit = use commit, but stop for amending - # s, squash = use commit, but meld into previous commit - # f, fixup = like "squash", but discard this commit's log message - # - # If you remove a line here THAT COMMIT WILL BE LOST. - # However, if you remove everything, the rebase will be aborted. - # - -To achieve what we want, we will make the following changes to it:: - - r 13d7934 First implementation - pick 2dec1ac Fix a few bugs + disable - f a815645 Modify it so that it works - f eadc391 Fix some remaining bugs - -This means that (i) we want to edit the commit message for -``13d7934``, and (ii) collapse the last three commits into one. Now we -save and quit the editor. - -Git will then immediately bring up an editor for editing the commit -message. After revising it, we get the output:: - - [detached HEAD 721fc64] FOO: First implementation - 2 files changed, 199 insertions(+), 66 deletions(-) - [detached HEAD 0f22701] Fix a few bugs + disable - 1 files changed, 79 insertions(+), 61 deletions(-) - Successfully rebased and updated refs/heads/my-feature-branch. - -and the history looks now like this:: - - 0f22701 Fix a few bugs + disable - 721fc64 ENH: Sophisticated feature - 6ad92e5 * masked is now an instance of a new object, MaskedConstant - -If it went wrong, recovery is again possible as explained :ref:`above -`. - -.. include:: links.inc diff --git a/src/doc/en/developer/sagenb/following_latest.rst b/src/doc/en/developer/sagenb/following_latest.rst deleted file mode 100644 index 2bc5c374cca..00000000000 --- a/src/doc/en/developer/sagenb/following_latest.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _following-latest: - -============================= - Following the Latest Source -============================= - -These are the instructions if you just want to follow the latest -*Sage Notebook* source, but you don't need to do any development for now. - -The steps are: - -* :ref:`section-git-install` -* get local copy of the `Sage Notebook github`_ git repository -* update local copy from time to time - -Get the Local Copy of the Code -============================== - -From the command line:: - - git clone git://github.com/sagemath/sagenb.git - -You now have a copy of the code tree in the new ``sagenb`` directory. - -Updating the Code -================= - -From time to time you may want to pull down the latest code. Do this with:: - - cd sagenb - git pull - -The tree in ``sagenb`` will now have the latest changes from the initial -repository. - -.. include:: links.inc diff --git a/src/doc/en/developer/sagenb/forking_hell.rst b/src/doc/en/developer/sagenb/forking_hell.rst deleted file mode 100644 index d33bdfb953f..00000000000 --- a/src/doc/en/developer/sagenb/forking_hell.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _forking: - -====================================================== -Making Your Own Copy (Fork) of Sage Notebook -====================================================== - -You need to do this only once. The instructions here are very similar -to the instructions at http://help.github.com/forking/ |emdash| please see -that page for more detail. We're repeating some of it here just to give the -specifics for the `Sage Notebook`_ project, and to suggest some default names. - -Set Up and Configure a Github Account -===================================== - -If you don't have a github account, go to the github page, and make one. - -You then need to configure your account to allow write access |emdash| see -the ``Generating SSH keys`` help on `github help`_. - -Create Your Own Forked Copy of `Sage Notebook`_ -====================================================== - -#. Log into your github account. -#. Go to the `Sage Notebook`_ github home at `Sage Notebook github`_. -#. Click on the *fork* button: - - .. image:: forking_button.png - - Now, after a short pause, you should find yourself at the home - page for your own forked copy of `Sage Notebook`_. - -.. include:: links.inc - diff --git a/src/doc/en/developer/sagenb/git_links.inc b/src/doc/en/developer/sagenb/git_links.inc deleted file mode 100644 index bbaeda39dab..00000000000 --- a/src/doc/en/developer/sagenb/git_links.inc +++ /dev/null @@ -1,60 +0,0 @@ -.. This (-*- rst -*-) format file contains commonly used link targets - and name substitutions. It may be included in many files, - therefore it should only contain link targets and name - substitutions. Try grepping for "^\.\. _" to find plausible - candidates for this list. - -.. NOTE: reST targets are - __not_case_sensitive__, so only one target definition is needed for - nipy, NIPY, Nipy, etc... - -.. git stuff -.. _github: http://github.com -.. _github help: http://help.github.com -.. _msysgit: http://code.google.com/p/msysgit/downloads/list -.. _git-osx-installer: http://code.google.com/p/git-osx-installer/downloads/list -.. _subversion: http://subversion.tigris.org/ -.. _git cheat sheet: http://github.com/guides/git-cheat-sheet -.. _pro git book: http://progit.org/ -.. _git svn crash course: http://git-scm.com/course/svn.html -.. _learn.github: http://learn.github.com/ -.. _network graph visualizer: http://github.com/blog/39-say-hello-to-the-network-graph-visualizer -.. _git user manual: http://schacon.github.com/git/user-manual.html -.. _git tutorial: http://schacon.github.com/git/gittutorial.html -.. _git community book: http://book.git-scm.com/ -.. _git ready: http://www.gitready.com/ -.. _git casts: http://www.gitcasts.com/ -.. _Fernando's git page: http://www.fperez.org/py4science/git.html -.. _git magic: http://www-cs-students.stanford.edu/~blynn/gitmagic/index.html -.. _git concepts: http://www.eecs.harvard.edu/~cduan/technical/git/ -.. _git clone: http://schacon.github.com/git/git-clone.html -.. _git checkout: http://schacon.github.com/git/git-checkout.html -.. _git commit: http://schacon.github.com/git/git-commit.html -.. _git push: http://schacon.github.com/git/git-push.html -.. _git pull: http://schacon.github.com/git/git-pull.html -.. _git add: http://schacon.github.com/git/git-add.html -.. _git status: http://schacon.github.com/git/git-status.html -.. _git diff: http://schacon.github.com/git/git-diff.html -.. _git log: http://schacon.github.com/git/git-log.html -.. _git branch: http://schacon.github.com/git/git-branch.html -.. _git remote: http://schacon.github.com/git/git-remote.html -.. _git rebase: http://schacon.github.com/git/git-rebase.html -.. _git config: http://schacon.github.com/git/git-config.html -.. _why the -a flag?: http://www.gitready.com/beginner/2009/01/18/the-staging-area.html -.. _git staging area: http://www.gitready.com/beginner/2009/01/18/the-staging-area.html -.. _tangled working copy problem: http://tomayko.com/writings/the-thing-about-git -.. _git management: http://kerneltrap.org/Linux/Git_Management -.. _linux git workflow: http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html -.. _git parable: http://tom.preston-werner.com/2009/05/19/the-git-parable.html -.. _git foundation: http://matthew-brett.github.com/pydagogue/foundation.html -.. _deleting master on github: http://matthew-brett.github.com/pydagogue/gh_delete_master.html -.. _rebase without tears: http://matthew-brett.github.com/pydagogue/rebase_without_tears.html -.. _resolving a merge: http://schacon.github.com/git/user-manual.html#resolving-a-merge -.. _ipython git workflow: http://mail.scipy.org/pipermail/ipython-dev/2010-October/006746.html - -.. other stuff -.. _python: http://www.python.org - -.. |emdash| unicode:: U+02014 - -.. vim: ft=rst diff --git a/src/doc/en/developer/sagenb/github_development.rst b/src/doc/en/developer/sagenb/github_development.rst deleted file mode 100644 index 1ae96f3cd79..00000000000 --- a/src/doc/en/developer/sagenb/github_development.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _github-development: - -===================== - Git for Development -===================== - -Contents: - -.. toctree:: - :maxdepth: 2 - - forking_hell - set_up_fork - development_workflow - maintainer_workflow diff --git a/src/doc/en/developer/sagenb/index.rst b/src/doc/en/developer/sagenb/index.rst deleted file mode 100644 index 5e2a7f9db0c..00000000000 --- a/src/doc/en/developer/sagenb/index.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. _sagenb: - -============================= -Sage Notebook Developer Guide -============================= - - -Development of the Sage notebook currently occurs on Github using the -Git revision control system. The development model for the `Sage -Notebook`_ project is a `git `_ and github_ -workflow. - -To update to the latest development source, run the commands below, -where ``SAGE_ROOT`` is the root directory of the Sage installation, -and where ``hackdir`` is a directory you create for working on code -changes (it need not have the name or location given below). - -.. warning:: This will create a new sagenb repository ignoring any - changes you have made to the files. - -:: - - mkdir ~/hackdir - cd ~/hackdir - git clone git://github.com/sagemath/sagenb.git sagenb-git - cd SAGE_ROOT/src - rm sagenb - ln -s ~/hackdir/sagenb sagenb - cd sagenb - ../../sage setup.py develop - -What this has done is to create a new directory, move to that -directory, and create a clone of the most up-to-date version of -the upstream notebook sources there. Then we remove a symbolic -link ``sagenb`` in the Sage folder and replace it with a link -to your clone of upstream, finally making sure that the notebook -has the correct dependencies. - -An advantage of having the separate directory for sagenb is that -you would later be able to keep it and do development work in it -even when you upgrade Sage, or even if you accidentally destroy your -Sage installation somehow. - -The rest of these instructions is some very generic documentation, -slightly adapted to help develop the notebook using Git and Github. - -The most important section involves how to update your new sagenb -source repository and create a "fork" of the master copy, so that you -will be able to request your changes to be merged in the Sage notebook, -called a "pull request"; see :ref:`github-development`. - - -.. toctree:: - :maxdepth: 2 - - following_latest - patching - github_development - - -.. include:: links.inc diff --git a/src/doc/en/developer/sagenb/known_projects.inc b/src/doc/en/developer/sagenb/known_projects.inc deleted file mode 100644 index 29723528779..00000000000 --- a/src/doc/en/developer/sagenb/known_projects.inc +++ /dev/null @@ -1,41 +0,0 @@ -.. Known projects - -.. PROJECTNAME placeholders -.. _PROJECTNAME: http://neuroimaging.scipy.org -.. _`PROJECTNAME github`: http://github.com/nipy -.. _`PROJECTNAME mailing list`: http://projects.scipy.org/mailman/listinfo/nipy-devel - -.. numpy -.. _numpy: hhttp://numpy.scipy.org -.. _`numpy github`: http://github.com/numpy/numpy -.. _`numpy mailing list`: http://mail.scipy.org/mailman/listinfo/numpy-discussion - -.. scipy -.. _scipy: http://www.scipy.org -.. _`scipy github`: http://github.com/scipy/scipy -.. _`scipy mailing list`: http://mail.scipy.org/mailman/listinfo/scipy-dev - -.. nipy -.. _nipy: http://nipy.org/nipy -.. _`nipy github`: http://github.com/nipy/nipy -.. _`nipy mailing list`: http://mail.scipy.org/mailman/listinfo/nipy-devel - -.. ipython -.. _ipython: http://ipython.scipy.org -.. _`ipython github`: http://github.com/ipython/ipython -.. _`ipython mailing list`: http://mail.scipy.org/mailman/listinfo/IPython-dev - -.. dipy -.. _dipy: http://nipy.org/dipy -.. _`dipy github`: http://github.com/Garyfallidis/dipy -.. _`dipy mailing list`: http://mail.scipy.org/mailman/listinfo/nipy-devel - -.. nibabel -.. _nibabel: http://nipy.org/nibabel -.. _`nibabel github`: http://github.com/nipy/nibabel -.. _`nibabel mailing list`: http://mail.scipy.org/mailman/listinfo/nipy-devel - -.. marsbar -.. _marsbar: http://marsbar.sourceforge.net -.. _`marsbar github`: http://github.com/matthew-brett/marsbar -.. _`MarsBaR mailing list`: https://lists.sourceforge.net/lists/listinfo/marsbar-users diff --git a/src/doc/en/developer/sagenb/links.inc b/src/doc/en/developer/sagenb/links.inc deleted file mode 100644 index 20f4dcfffd4..00000000000 --- a/src/doc/en/developer/sagenb/links.inc +++ /dev/null @@ -1,4 +0,0 @@ -.. compiling links file -.. include:: known_projects.inc -.. include:: this_project.inc -.. include:: git_links.inc diff --git a/src/doc/en/developer/sagenb/maintainer_workflow.rst b/src/doc/en/developer/sagenb/maintainer_workflow.rst deleted file mode 100644 index fdc1d2cdf50..00000000000 --- a/src/doc/en/developer/sagenb/maintainer_workflow.rst +++ /dev/null @@ -1,96 +0,0 @@ -.. _maintainer-workflow: - -################### -Maintainer Workflow -################### - -This page is for maintainers |emdash| those of us who merge our own or other -peoples' changes into the upstream repository. - -Being as how you're a maintainer, you are completely on top of the basic stuff -in :ref:`development-workflow`. - -The instructions in :ref:`linking-to-upstream` add a remote that has read-only -access to the upstream repo. Being a maintainer, you've got read-write access. - -It's good to have your upstream remote have a scary name, to remind you that -it's a read-write remote:: - - git remote add upstream-rw git@github.com:sagemath/sagenb.git - git fetch upstream-rw - -******************* -Integrating Changes -******************* - -Let's say you have some changes that need to go into trunk -(``upstream-rw/master``). - -The changes are in some branch that you are currently on. For example, you are -looking at someone's changes like this:: - - git remote add someone git://github.com/someone/sagenb.git - git fetch someone - git branch cool-feature --track someone/cool-feature - git checkout cool-feature - -So now you are on the branch with the changes to be incorporated upstream. The -rest of this section assumes you are on this branch. - -A Few Commits -============= - -If there are only a few commits, consider rebasing to upstream:: - - # Fetch upstream changes - git fetch upstream-rw - # rebase - git rebase upstream-rw/master - -Remember that, if you do a rebase, and push that, you'll have to close any -github pull requests manually, because github will not be able to detect the -changes have already been merged. - -A Long Series of Commits -======================== - -If there are a longer series of related commits, consider a merge instead:: - - git fetch upstream-rw - git merge --no-ff upstream-rw/master - -The merge will be detected by github, and should close any related pull requests -automatically. - -Note the ``--no-ff`` above. This forces git to make a merge commit, rather than -doing a fast-forward, so that these set of commits branch off trunk then rejoin -the main history with a merge, rather than appearing to have been made directly -on top of trunk. - -Check the History -================= - -Now, in either case, you should check that the history is sensible and you have -the right commits:: - - git log --oneline --graph - git log -p upstream-rw/master.. - -The first line above just shows the history in a compact way, with a text -representation of the history graph. The second line shows the log of commits -excluding those that can be reached from trunk (``upstream-rw/master``), and -including those that can be reached from current HEAD (implied with the ``..`` -at the end). So, it shows the commits unique to this branch compared to trunk. -The ``-p`` option shows the diff for these commits in patch form. - -Push to trunk -============= - -:: - - git push upstream-rw my-new-feature:master - -This pushes the ``my-new-feature`` branch in this repository to the ``master`` -branch in the ``upstream-rw`` repository. - -.. include:: links.inc diff --git a/src/doc/en/developer/sagenb/patching.rst b/src/doc/en/developer/sagenb/patching.rst deleted file mode 100644 index fb7fc4cbe95..00000000000 --- a/src/doc/en/developer/sagenb/patching.rst +++ /dev/null @@ -1,143 +0,0 @@ -================ - Making a Patch -================ - -You've discovered a bug or something else you want to change -in `Sage Notebook`_ |emdash| excellent! - -You've worked out a way to fix it |emdash| even better! - -You want to tell us about it |emdash| best of all! - -The easiest way is to make a *patch* or set of patches. Here -we explain how. - -Making a patch is simple and quick, but it is not part of our -normal workflow. So if you are going to be doing anything more -than a once-off patch one time, please consider following the -:ref:`github-development` model instead. See especially the part -about "pull requests" at :ref:`edit-flow`. - -.. _making-patches: - -Making Patches -============== - -Overview --------- - -:: - - # tell git who you are - git config --global user.email you@yourdomain.example.com - git config --global user.name "Your Name Comes Here" - # get the repository if you don't have it - git clone git://github.com/sagemath/sagenb.git - # make a branch for your patching - cd sagenb - git branch the-fix-im-thinking-of - git checkout the-fix-im-thinking-of - # hack, hack, hack - # Tell git about any new files you've made - git add somewhere/tests/test_my_bug.py - # commit work in progress as you go - git commit -am 'BF - added tests for Funny bug' - # hack hack, hack - git commit -am 'BF - added fix for Funny bug' - # make the patch files - git format-patch -M -C master - -You may attach a short generated patch file to the `Sage Notebook -mailing list`_ or better, open an issue at the `Sage Notebook github`_ -site (see :ref:`github-development`) and cut and paste your patch in a -comment there. In either case we will thank you warmly. - -In Detail ---------- - -#. Tell git who you are so it can label the commits you've - made:: - - git config --global user.email you@yourdomain.example.com - git config --global user.name "Your Name Comes Here" - -#. If you don't already have one, clone a copy of the - `Sage Notebook`_ repository:: - - git clone git://github.com/sagemath/sagenb.git - cd sagenb - -#. Make a 'feature branch'. This will be where you work on - your bug fix. It's nice and safe and leaves you with - access to an unmodified copy of the code in the main - branch:: - - git branch the-fix-im-thinking-of - git checkout the-fix-im-thinking-of - -#. Do some edits, and commit them as you go:: - - # hack, hack, hack - # Tell git about any new files you've made - git add somewhere/tests/test_my_bug.py - # commit work in progress as you go - git commit -am 'BF - added tests for Funny bug' - # hack hack, hack - git commit -am 'BF - added fix for Funny bug' - - Note the ``-am`` options to ``commit``. The ``m`` flag just - signals that you're going to type a message on the command - line. The ``a`` flag |emdash| you can just take on faith |emdash| - or see `why the -a flag?`_. - -#. When you have finished, check you have committed all your - changes:: - - git status - -#. Finally, make your commits into patches. You want all the - commits since you branched from the ``master`` branch:: - - git format-patch -M -C master - - You will now have several files named for the commits:: - - 0001-BF-added-tests-for-Funny-bug.patch - 0002-BF-added-fix-for-Funny-bug.patch - - Although some projects would have you send these files to - the `Sage Notebook mailing list`_, we prefer submitting an issue - request at the web interface to the `Sage Notebook github`_ - page. See :ref:`edit-flow` for how to create a "pull request" once you - have created a Github account. - -When you are done, to switch back to the main copy of the -code, just return to the ``master`` branch:: - - git checkout master - -Moving from Patching to Development -=================================== - -If you find you have done some patches, and you have one or -more feature branches, you will probably want to switch to -development mode. You can do this with the repository you -have. - -Fork the `Sage Notebook`_ repository on github |emdash| :ref:`forking`. -Then:: - - # checkout and refresh master branch from main repo - git checkout master - git pull origin master - # rename pointer to main repository to 'upstream' - git remote rename origin upstream - # point your repo to default read / write to your fork on github - git remote add origin git@github.com:your-user-name/sagenb.git - # push up any branches you've made and want to keep - git push origin the-fix-im-thinking-of - -Then you can, if you want, follow the -:ref:`development-workflow`. - -.. include:: links.inc diff --git a/src/doc/en/developer/sagenb/set_up_fork.rst b/src/doc/en/developer/sagenb/set_up_fork.rst deleted file mode 100644 index b0c82043b49..00000000000 --- a/src/doc/en/developer/sagenb/set_up_fork.rst +++ /dev/null @@ -1,68 +0,0 @@ -.. _set-up-fork: - -================== -Set Up Your Fork -================== - -First you follow the instructions for :ref:`forking`. - -Overview -======== - -:: - - git clone git@github.com:your-user-name/sagenb.git - cd sagenb - git remote add upstream git://github.com/sagemath/sagenb.git - -In Detail -========= - -Clone Your Fork ---------------- - -#. Clone your fork to the local computer with ``git clone - git@github.com:your-user-name/sagenb.git`` -#. Investigate. Change directory to your new repo: ``cd sagenb``. Then - ``git branch -a`` to show you all branches. You'll get something - like:: - - * master - remotes/origin/master - - This tells you that you are currently on the ``master`` branch, and - that you also have a ``remote`` connection to ``origin/master``. - What remote repository is ``remote/origin``? Try ``git remote -v`` to - see the URLs for the remote. They will point to your github fork. - - Now you want to connect to the upstream `Sage Notebook github`_ repository, so - you can merge in changes from trunk. - -.. _linking-to-upstream: - -Linking Your Repository to the Upstream Repo --------------------------------------------- - -:: - - cd sagenb - git remote add upstream git://github.com/sagemath/sagenb.git - -``upstream`` here is just the arbitrary name we're using to refer to the -main `Sage Notebook`_ repository at `Sage Notebook github`_. - -Note that we've used ``git://`` for the URL rather than ``git@``. The -``git://`` URL is read only. This means we that we can't accidentally -(or deliberately) write to the upstream repo, and we are only going to -use it to merge into our own code. - -Just for your own satisfaction, show yourself that you now have a new -'remote', with ``git remote -v show``, giving you something like:: - - upstream git://github.com/sagemath/sagenb.git (fetch) - upstream git://github.com/sagemath/sagenb.git (push) - origin git@github.com:your-user-name/sagenb.git (fetch) - origin git@github.com:your-user-name/sagenb.git (push) - -.. include:: links.inc - diff --git a/src/doc/en/developer/sagenb/this_project.inc b/src/doc/en/developer/sagenb/this_project.inc deleted file mode 100644 index 3b331f10fa3..00000000000 --- a/src/doc/en/developer/sagenb/this_project.inc +++ /dev/null @@ -1,5 +0,0 @@ -.. Sage Notebook -.. _`Sage Notebook`: http://nb.sagemath.org -.. _`Sage Notebook github`: http://github.com/sagemath/sagenb - -.. _`Sage Notebook mailing list`: http://groups.google.com/group/sage-notebook diff --git a/src/doc/en/faq/faq-contribute.rst b/src/doc/en/faq/faq-contribute.rst index 69ea2aa400c..bc70ab6c145 100644 --- a/src/doc/en/faq/faq-contribute.rst +++ b/src/doc/en/faq/faq-contribute.rst @@ -7,10 +7,9 @@ FAQ: Contributing to Sage How can I start contributing to Sage? """"""""""""""""""""""""""""""""""""" -The first step -is to use Sage and encourage your friends to use Sage. If you find -bugs or confusing documentation along the way, please report your -problems! +The first step is to use Sage and encourage your friends to use +Sage. If you find bugs or confusing documentation along the way, +please report your problems! Two popular ways to contribute to Sage are to write code and to create documentation or tutorials. Some steps in each direction @@ -20,12 +19,12 @@ I want to contribute code to Sage. How do I get started? """""""""""""""""""""""""""""""""""""""""""""""""""""""" Take a look at the -`official development guide `_ +`official development guide `_ for Sage. At a minimum, the first chapter in that guide is required reading for any Sage developer. Also pay special attention to the -`trac guidelines `_. +`trac guidelines `_. You can also join the -`sage-devel `_ +`sage-devel `_ mailing list or hang around on the ``#sage-devel`` IRC channel on `freenode `_. While you are getting to know @@ -35,7 +34,7 @@ source and familiarize yourself with the The best way to become familiar with the Sage development process is to choose a ticket from the -`trac server `_ +`trac server `_ and review the proposed changes contained in that ticket. If you want to implement something, it is a good practice to discuss your ideas on the ``sage-devel`` mailing list first, so that other developers have a @@ -43,7 +42,7 @@ chance to comment on your ideas/proposals. They are pretty open to new ideas, too, as all mathematicians should be. Sage's main programming language is -`Python `_. +`Python `_. Some parts of Sage may be written in other languages, especially the components that do the heavy number crunching, but most native functionality is done using Python, including "glue code". One of the @@ -62,11 +61,11 @@ evil." If you do not know Python, you should start learning that language. A good place to start is the -`Python Official Tutorial `_ +`Python Official Tutorial `_ and other documents in the -`Python standard documentation `_. +`Python standard documentation `_. Another good place to take a look at is -`Dive Into Python `_ +`Dive Into Python `_ by Mark Pilgrim, which may be pretty helpful on some specific topics such as test-driven development. The book `Building Skills in Python `_ @@ -82,17 +81,8 @@ programming concepts are explained more thoroughly in Python-centered resources than in Sage-centered resources; in the latter, mathematics is usually the priority. -Can I contribute to Sage using SageMathCloud? -""""""""""""""""""""""""""""""""""""""""""""" - -Absolutely! If you want to write code for Sage or update the -official documentation, -you will need your own installation of Sage on `SageMathCloud `_. -You can find more information about the details of installation in -`the SageMathCloud FAQ `_. - -I'm not a programmer. Is there another way I can help out? -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +I am not a programmer. Is there another way I can help out? +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" Yes. As with any free open source software project, there are numerous ways in which you could help out within the Sage community, and @@ -109,23 +99,20 @@ has written some which we highly recommend. For the graphic designers or the artistically creative, you can -help out with improving the design of the Sage website. Or you can -cast your critical artistic eyes over the interfaces of SageMathCloud -or the Sage notebook, and find out where they need improvement. +help out with improving the design of the Sage website. If you can speak, read, and write in another (natural) language, there are many ways in which your contribution would be very valuable to the whole Sage community. Say you know Italian. Then you can write a Sage tutorial in Italian, or help out with translating the official Sage tutorial to -Italian. +Italian. -The above is a very short -list. There are many, many more ways in which you can help out. Feel -free to send an email to the -`sage-devel `_ -mailing list to ask about possible ways in which you could help out, -or to suggest a project idea. +The above is a very short list. There are many, many more ways in +which you can help out. Feel free to send an email to the `sage-devel +`_ mailing list to ask +about possible ways in which you could help out, or to suggest a +project idea. Where can I find resources on Python or Cython? @@ -136,31 +123,26 @@ resources can be found by a web search. **General resources** -* `Cython `_ +* `Cython `_ * `pep8 `_ * `py2depgraph `_ * `pycallgraph `_ * `PyChecker `_ * `PyFlakes `_ * `Pylint `_ -* `Python `_ home page and the - `Python standard documentation `_ +* `Python `_ home page and the + `Python standard documentation `_ * `Snakefood `_ * `Sphinx `_ * `XDot `_ **Tutorials and books** -* `Building Skills in Python `_ - by Steven F. Lott * `Cython Tutorial `_ by Stefan Behnel, Robert W. Bradshaw, and Dag Sverre Seljebotn -* `Dive into Python `_ by Mark Pilgrim * `Dive Into Python 3 `_ by Mark Pilgrim * `Fast Numerical Computations with Cython `_ by Dag Sverre Seljebotn -* `How to Think Like a Computer Scientist `_ - by Jeffrey Elkner, Allen B. Downey, and Chris Meyers * `Official Python Tutorial `_ **Articles and HOWTOs** @@ -173,9 +155,6 @@ resources can be found by a web search. * `Regular Expression HOWTO `_ by A. M. Kuchling * `reStructuredText `_ -* `Static Code Analizers for Python `_ - by Doug Hellmann - Are there any coding conventions I need to follow? """""""""""""""""""""""""""""""""""""""""""""""""" @@ -183,7 +162,7 @@ Are there any coding conventions I need to follow? You should follow the standard Python conventions as documented at :pep:`8` and :pep:`257`. Also consult the Sage Developer's Guide, especially the chapter -`Conventions for Coding in Sage `_. +`Conventions for Coding in Sage `_. I submitted a bug fix to the trac server several weeks ago. Why are you ignoring my patch? @@ -268,7 +247,12 @@ necessity to import what you need. from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - You can ask Sage where to find ``PolynomialRing`` using:: + You can use ``import_statements`` to get the exact necessary line:: + + sage: import_statements(PolynomialRing) + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + + If this fails, you can ask Sage where to find ``PolynomialRing`` using:: sage: PolynomialRing.__module__ 'sage.rings.polynomial.polynomial_ring_constructor' diff --git a/src/doc/en/faq/faq-general.rst b/src/doc/en/faq/faq-general.rst index c18c31576a7..f539d0dd56f 100644 --- a/src/doc/en/faq/faq-general.rst +++ b/src/doc/en/faq/faq-general.rst @@ -85,7 +85,7 @@ previous and ongoing work of many authors of included components. A list of (some) direct contributors can be found on the `Sage Development Map `_ -and the history of changes can be found in the high-level +and the history of changes can be found in the `changelogs `_. Refer to the `acknowledgment page `_ @@ -204,9 +204,11 @@ functionalities are made possible through FOSS projects such as statistical computing and graphics. * And many more too numerous to list here. -An up-to-date list can be found on the page for the -`standard packages repository `_. -The principle programming languages of Sage are +An up-to-date list can be found in the section +`External Packages <../reference/spkg/index.html>`_ +in the Sage Reference Manual. + +The principal programming languages of Sage are `Python `_ and `Cython `_. @@ -237,29 +239,22 @@ See http://www.sagemath.org/help.html for a listing of other resources. Wouldn't it be way better if Sage did not ship as a gigantic bundle? """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -This topic has been discussed over and over again. So before you -resume the discussion, ensure you have read and understood the -arguments below. Sage is a distribution of over 90 FOSS packages for -symbolic, numerical, and scientific computation. In general, the -combinatorial explosion of configurations to debug is way too -large. It is next to impossible to find any Linux distribution -(e.g. Arch, CentOS, Debian, Fedora, Gentoo, Mandriva, Ubuntu) where -the version numbers of packages that Sage depends on even remotely -match. - -The majority of people who contribute to Sage do so in their free -time. These are people who hold day jobs that are not directly related -to computer programming or software development. It is next to -impossible for anyone to track down the correct versions of packages, -configure and compile them on Linux, Mac OS X, Solaris, or Windows, -just so that they could start using Sage or start working on their -first contribution to Sage. While the Sage project aims to be useful -to as wide an audience as possible, we believe that Sage first needs -to be as easy as possible to install by anyone with any level of -computer experience. If you want to help Sage realize this goal, -please email the -`sage-devel `_ -mailing list. +The SageMath distribution continues to vendor versions of required +software packages ("SPKGs") that work well together. + +However, in order to reduce compilation times and the size of the Sage +installation, a development effort ongoing since the 8.x release +series has made it possible to use many system packages provided by +the OS distribution (or by the Homebrew or conda-forge distributions) +instead of building SageMath's own copies. + +This so-called "spkg-configure" mechanism runs at the beginning of a +build from source, during the ``./configure`` phase. + +To ensure that SageMath builds and runs correctly on a wide variety of +systems, we use automated testing. See the chapter `Portability +testing <../developer/portability_testing.html>`_ in the Developer's +Guide for details. With so many bugs in Sage and hundreds of open tickets, why don't you produce a stabilization release? diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index ed397648c6e..0c5efde1b95 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -12,7 +12,7 @@ You can try out Sage without downloading anything: * **CoCalc™:** Go to https://cocalc.com and set up a free account. - If you log in, you will gain access to the latest version of Sage and to + If you log in, you will gain access to the latest version of Sage and to many other programs. Note that this website is an independent commercial service. @@ -35,11 +35,6 @@ issue the following command in a terminal, if ``sage`` is in your ``PATH`` $ sage -notebook -You can also run it from the command line of sage:: - - sage: notebook() # not tested - - What are the prerequisites for installing a copy of Sage on my computer? """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -52,9 +47,8 @@ from the page https://www.virtualbox.org/wiki/Downloads. After installing VirtualBox, you need to download a VirtualBox distribution of Sage available at http://www.sagemath.org/download-windows.html. Ensure you follow the -instructions at that page. Now you can start the Sage virtual machine -using the VirtualBox software, wait for the virtual machine to boot -up, then type ``notebook`` at the prompt. +instructions at that page, then start the Sage virtual machine +using the VirtualBox software. You can get the complete source for Sage to compile it on your own Linux or Mac OS X system. Sage lives in an isolated directory and does @@ -117,8 +111,8 @@ How do I import Sage into a Python script? You can import Sage as a library in a Python script. One caveat is that you need to run that Python script using the version of Python -that is bundled with Sage; currently Python 2.6.x. To import Sage, put -the following in your Python script: +that is bundled with Sage (Sage 9.2 ships with Python 3.7.x). +To import Sage, put the following in your Python script: .. CODE-BLOCK:: python @@ -419,13 +413,13 @@ e.g. :: sage: list(map(ord, "Big Mac")) [66, 105, 103, 32, 77, 97, 99] -How can I wrote multiplication implicitly as in Mathematica? +How can I write multiplication implicitly as in Mathematica? """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" Sage has a function that enables this:: sage: implicit_multiplication(True) - sage: x 2 x # Not tested + sage: x 2 x # not tested 2*x^2 sage: implicit_multiplication(False) @@ -441,7 +435,7 @@ complicated situation. To see what the preparser does:: See https://wiki.sagemath.org/sage_mathematica for more information about Mathematica vs. SageMath. - + Can I make Sage automatically execute commands on startup? """""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -451,20 +445,6 @@ environment variable ``DOT_SAGE`` points to the hidden directory ``$HOME/.sage``, which by default is the case. -My Sage upgrade failed with missing gmp symbols on OSX 10.4. What can I do? -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -Moving a Sage install on Mac OS X 10.4 and then upgrading anything -that is linked against NTL leads to link errors due to missing gmp -symbols. The problem is the link mode with which the dynamic NTL is -created. There is have a fix, but it still being verified that it -really fixes the issue. Everything that is linked against NTL needs to -be recompiled, i.e. singular and cremona at the moment. To add to the -confusion: This is not an issue on Mac OS X 10.5. A fix for this issue -went into Sage 2.8.15, so please report if you see this with a more -current Sage release. - - When I compile Sage my computer beeps and shuts down or hangs. """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -690,14 +670,19 @@ How do I plot the cube root (or other odd roots) for negative input? This is one of the most frequently asked questions. There are several methods mentioned in the plot documentation, but this one is easiest:: - sage: plot(sign(x)*abs(x)^(1/3),-1,1) + sage: plot(real_nth_root(x, 3), (x, -1, 1)) Graphics object consisting of 1 graphics primitive -The *reason* this is necessary is that Sage returns complex numbers -for odd roots of negative numbers when numerically approximated, which -is a `standard convention `_. +On the other hand, note that the straightforward :: - sage: N((-1)^(1/3)) + sage: plot(x^(1/3), (x, -1, 1)) # not tested + +produces the expected plot only for positive `x`. The *reason* is that Sage +returns complex numbers for odd roots of negative numbers when numerically +approximated, which is a `standard convention +`_. :: + + sage: numerical_approx( (-1)^(1/3) ) 0.500000000000000 + 0.866025403784439*I How do I use the bitwise XOR operator in Sage? @@ -789,13 +774,6 @@ You will need to do this from the command line. Just run a command like this. $ BROWSER='open -a Firefox %s' ./sage --notebook jupyter $ BROWSER='open -a Google\ Chrome %s' ./sage --notebook jupyter - With the old SageNB notebook: - - .. CODE-BLOCK:: shell-session - - $ BROWSER='open -a Firefox' ./sage --notebook - $ BROWSER='open -a Google\ Chrome' ./sage --notebook - Where is the source code for ````? """""""""""""""""""""""""""""""""""""""""""" @@ -805,7 +783,7 @@ on the IPython command line with the ``??`` shortcut:: sage: plot?? # not tested Signature: plot(*args, **kwds) - Source: + Source: ... Objects that are built into Python or IPython are compiled and will diff --git a/src/doc/en/installation/conda.rst b/src/doc/en/installation/conda.rst index f7ce7cf0a91..0b090cb2089 100644 --- a/src/doc/en/installation/conda.rst +++ b/src/doc/en/installation/conda.rst @@ -11,7 +11,7 @@ then type in the following commands in a terminal: * Add the conda-forge channel: ``conda config --add channels conda-forge`` * Create a new environment containing SageMath: ``conda create -n sage sage python=X``, where - ``X`` is version of Python, e.g. ``2.7`` + ``X`` is version of Python, e.g. ``3.7`` * Enter the new environment: ``conda activate sage`` * Start SageMath: ``sage`` diff --git a/src/doc/en/installation/debian-optional.txt b/src/doc/en/installation/debian-optional.txt deleted file mode 100644 index 081fc080e7b..00000000000 --- a/src/doc/en/installation/debian-optional.txt +++ /dev/null @@ -1,2 +0,0 @@ - $ sudo apt-get install cmake libterm-readline-gnu-perl ninja-build \ - librw-dev diff --git a/src/doc/en/installation/debian.txt b/src/doc/en/installation/debian.txt deleted file mode 100644 index c2a6bad93a3..00000000000 --- a/src/doc/en/installation/debian.txt +++ /dev/null @@ -1,9 +0,0 @@ - $ sudo apt-get install binutils pixz gcc g++ gfortran make m4 perl tar \ - git patch openssl libssl-dev libz-dev bc libbz2-dev liblzma-dev libgmp-dev \ - libffi-dev libgf2x-dev libcurl4-openssl-dev libzmq3-dev curl yasm \ - pkg-config libntl-dev libmpfr-dev libmpc-dev libflint-dev \ - libpcre3-dev libgd-dev libflint-dev libflint-arb-dev \ - libsymmetrica2-dev gmp-ecm libecm-dev libisl-dev libgivaro-dev \ - libpari-dev pari-gp2c libec-dev liblrcalc-dev \ - libm4ri-dev libm4rie-dev liblfunction-dev lcalc \ - libopenblas-dev r-base-dev libgsl-dev libcliquer-dev cliquer diff --git a/src/doc/en/installation/fedora-optional.txt b/src/doc/en/installation/fedora-optional.txt deleted file mode 100644 index a668e66888e..00000000000 --- a/src/doc/en/installation/fedora-optional.txt +++ /dev/null @@ -1 +0,0 @@ - $ sudo yum install cmake perl-Term-ReadLine-Gnu ninja-build rw-devel diff --git a/src/doc/en/installation/fedora.txt b/src/doc/en/installation/fedora.txt deleted file mode 100644 index d135fee0180..00000000000 --- a/src/doc/en/installation/fedora.txt +++ /dev/null @@ -1,8 +0,0 @@ - $ sudo yum install binutils xz gcc gcc-c++ gcc-gfortran make m4 perl \ - tar git patch perl-ExtUtils-MakeMaker openssl openssl-devel zlib-devel \ - bzip2 bzip2-devel xz-devel gmp gmp-devel libcurl-devel curl yasm \ - pkg-config ntl-devel mpfr-devel libmpc-devel libsymmetrica-devel \ - eclib-devel gmp-ecm-devel lrcalc-devel isl-devel givaro-devel \ - pari-devel pari-elldata pari-seadata pari-galdata pari-galpol \ - m4ri-devel m4rie-devel L-function-devel \ - openblas openblas-devel R R-devel gsl gsl-devel cliquer cliquer-devel 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 996b5f0e676..6553f603c8c 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -43,23 +43,13 @@ for some operating systems, rather than compiling from source. Supported platforms ------------------- -See https://wiki.sagemath.org/SupportedPlatforms for the full list of platforms -on which Sage is supported and the level of support for these systems. - -Sage is supported on a number of `Linux `_, -`macOS `_ , -Sun/Oracle `Solaris `_ releases, -but not necessarily all versions of these operating systems. -There is no native version of Sage which installs on -`Microsoft Windows `_, -although Sage can be used on Windows with the aid of a -`virtual machine `_ -or the `Cygwin `_ Linux API layer. - -On the `list of supported platforms `_, -you can find details about -`ports `_ -to other operating systems or processors which may be taking place. +Sage runs on all major `Linux `_ +distributions, `macOS `_ , and Windows +(via the `Cygwin `_ Linux API layer). + +Other installation options for Windows are using the Windows Subsystem +for Linux (WSL), or with the aid of a `virtual machine +`_. .. _section-prereqs: @@ -75,9 +65,7 @@ also the `System-specific requirements`_ below. Disk space and memory ^^^^^^^^^^^^^^^^^^^^^ -Your computer comes with at least 6 GB of free disk space running one of the -supported versions of an operating system listed at -https://wiki.sagemath.org/SupportedPlatforms. +Your computer comes with at least 6 GB of free disk space. It is recommended to have at least 2 GB of RAM, but you might get away with less (be sure to have some swap space in this case). @@ -100,8 +88,9 @@ computer: - **perl**: version 5.8.0 or later. - **ar** and **ranlib**: can be obtained as part of GNU binutils. - **tar**: GNU tar version 1.17 or later, or BSD tar. -- **python**: Python >= 3.6. +- **python**: Python 3, 3.3 or later, or Python 2 (deprecated), 2.6 or later. +Other versions of these may work, but they are untested. Libraries ^^^^^^^^^ @@ -132,7 +121,13 @@ development files. Fortran and compiler suites ########################### -Sage installation also needs a Fortran compiler. Officially we support +Sage installation also needs a Fortran compiler. It is determined +automatically whether Sage's GCC package, or just its part containing +Fortran compiler ``gfortran`` needs to be installed. This can be +overwritten by running ``./configure`` with option +``--without-system-gcc``. + +Officially we support gfortran from `GNU Compiler Collection (GCC) `_. If C and C++ compilers also come from there (i.e., gcc and g++), their versions should match. @@ -147,6 +142,13 @@ this is work in progress at the moment (May 2019)). Therefore, if you plan on using your own GCC compilers, then make sure that their versions match. +To force using specific compilers, set environment variables ``CC``, +``CXX``, and ``FC`` (for C, C++, and Fortran compilers, respectively) +to the desired values, and run ``./configure``. For example, +``./configure CC=clang CXX=clang++ FC=gfortran`` will configure Sage +to be built with Clang C/C++ compilers and Fortran compiler +``gfortran``. + Alternatively, Sage includes a GCC package, so that C, C++ and Fortran compilers will be built when the build system detects that it is needed, e.g., non-GCC compilers, or @@ -159,6 +161,13 @@ Note that you can always override this behavior through the configure options ``--without-system-gcc`` and ``--with-system-gcc``, see :ref:`section_compilers`. +There are some known problems with old assemblers, in particular when +building the ``ecm`` and ``fflas_ffpack`` packages. You should ensure +that your assembler understands all instructions for your +processor. On Linux, this means you need a recent version of +``binutils``; on macOS you need a recent version of Xcode. + + Other notes ^^^^^^^^^^^ @@ -182,10 +191,10 @@ some registration on Apple's developer site; see :ref:`section_macprereqs`. On Redhat-derived systems not all perl components are installed by -default and you might have to install the **perl-ExtUtils-MakeMaker** +default and you might have to install the ``perl-ExtUtils-MakeMaker`` package. -On Cygwin, the **lapack** and **liblapack-devel** packages are required to +On Cygwin, the ``lapack`` and ``liblapack-devel`` packages are required to provide ATLAS support as the Sage package for ATLAS is not built by default. Installing prerequisites @@ -227,10 +236,21 @@ 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 +On Arch Linux: + +.. literalinclude:: arch.txt + (These examples suppose that you choose to use a systemwide OpenSSL library.) In addition to these, if you don't want Sage to build optional packages that might @@ -243,6 +263,10 @@ On Fedora / Redhat / CentOS: .. literalinclude:: fedora-optional.txt +On Arch Linux: + +.. literalinclude:: arch-optional.txt + On other Linux systems, you might use `rpm `_, `yum `_, @@ -287,20 +311,129 @@ a registration. - Alternately, https://developer.apple.com/opensource/ should have a link 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 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sage can be built only on the 64-bit version of Cygwin. See +``README.md`` for the most up-to-date instructions for building Sage +on Cygwin. + +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 +installer) the following packages at a minimum:: + + bzip2 coreutils gawk gzip tar wget + +With the exception of ``wget`` most of these are included in the default +package selection when you install Cygwin. Then, to install ``apt-cyg`` +run:: + + $ curl -OL https://rawgit.com/transcode-open/apt-cyg/master/apt-cyg + $ install apt-cyg /usr/local/bin + $ rm -f apt-cyg + +To install the current set of system packages known to work for building +Sage, run: + +.. literalinclude:: cygwin.txt + +Optional packages that are also known to be installable via system packages +include: + +.. literalinclude:: cygwin-optional.txt + Other platforms ^^^^^^^^^^^^^^^ On Solaris, you would use ``pkgadd`` and on OpenSolaris ``ipf`` to install the necessary software. -On Cygwin, you would use the ``setup.exe`` program. -As on Linux systems, ``ar`` and ``ranlib`` are provided by the ``binutils`` package. -As far as compilers are concerned, you should either install matching versions -of the ``gcc4-core``, ``gcc4-g++``, and ``gcc4-gfortran`` packages, or -the ``gcc4-core`` package alone if you plan on using Sage's own GCC. - On other systems, check the documentation for your particular operating system. +.. _section_conda_compilers: + +Notes on using Anaconda/Miniconda +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If Conda is installed (check by typing ``conda info``), there are two ways to +prepare for installing SageMath from source: + +- Make sure that a Conda environment is active (for the current shell session) + that has at least the following Conda packages required for building SageMath:: + + c-compiler cxx-compiler fortran-compiler + + - Activate a Conda environment that has these packages, using:: + + $ conda activate ENVIRONMENT + + - The packages can be installed into the current Conda environment using:: + + $ conda install c-compiler cxx-compiler fortran-compiler + + - Optionally, install additional Conda packages. + + Then SageMath will be built using the compilers provided by Conda. + +- Deactivate conda (for the current shell session). + + - Type:: + + $ conda deactivate + + - Repeat the command until ``conda info`` shows:: + + $ conda info + + active environment : None + ... + + Then SageMath will be built either using the compilers provided by the + operating system, or its own compilers. + Specific notes for ``make`` and ``tar`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -356,7 +489,7 @@ in order to check that Sage will not try to build GCC. Namely, there should be l ... gfortran-7.2.0 will not be installed (configure check) -indicating that Sage will no attempt to build ``gcc/g++/gfortran``. +indicating that Sage will not attempt to build ``gcc/g++/gfortran``. If you are interested in working on support for commercial compilers from `HP `_, @@ -405,10 +538,28 @@ and seems to be faster than ImageMagick when creating animated GIFs. Either ImageMagick or dvipng is used for displaying some LaTeX output in the Sage notebook. +On Debian/Ubuntu, the following system packages are recommended. + +- ``texlive-generic-extra`` (to generate pdf documentation) + +- ``texlive-xetex`` (to convert Jupyter notebooks to pdf) + +- ``latexmk`` (to generate pdf documentation) + +- ``pandoc`` (to convert Jupyter notebooks to pdf) + +- ``dvipng`` (to render text with LaTeX in Matplotlib) + +- ``default-jdk`` (to run the Jmol 3D viewer from the console and generate images for 3D plots in the documentation) + +- ``ffmpeg`` (to produce animations) + +- ``libavdevice-dev`` (to produce animations) + Notebook additional features ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**attention: Sage's notebook is deprecated. Use Jupyter notebook instead** +**attention: Sage's notebook is deprecated, and notebook() command has been removed. Use Jupyter notebook instead** By default, the Sage notebook uses the `HTTP `_ @@ -494,12 +645,10 @@ several of Sage's components will not build if there are spaces in the path. Running Sage from a directory with spaces in its name will also fail. #. Go to https://www.sagemath.org/download-source.html, select a mirror, - and download the file :file:`sage-x.y.tar`. + and download the file :file:`sage-x.y.tar.gz`. - This tarfile contains the source code for Sage and the source for all - programs on which Sage depends. - Note that this file is not compressed; it's just a plain tarball (which - happens to be full of compressed files). + This compressed archive file contains the source code for Sage and + the source for all programs on which Sage depends. Download it into any directory you have write access to, preferably on a fast filesystem, avoiding @@ -507,11 +656,11 @@ Running Sage from a directory with spaces in its name will also fail. On personal computers, any subdirectory of your :envvar:`HOME` directory should do. Note that once you have built Sage (by running ``make``, as described below), you will not be able to move or rename its - directory without likely breaking Sage. + directory without breaking Sage. -#. Extract the tarfile:: +#. Extract the archive:: - $ tar xvf sage-x.y.tar + $ tar xvf sage-x.y.tar.gz This creates a directory :file:`sage-x.y`. @@ -529,15 +678,17 @@ 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 Sage. Note that some packages will ignore the compiler settings and use values deemed safe for that package on a particular OS. -#. Optional: Run the configure script to set some options that +#. Run the configure script to set some options that influence the build process. - Choose the installation hierarchy (:envvar:`SAGE_LOCAL`). @@ -984,7 +1135,9 @@ Here are some of the more commonly used variables affecting the build process: - :envvar:`SAGE_CHECK` - if set to ``yes``, then during the build process, or when installing packages manually, - run the test suite for each package which has one. + run the test suite for each package which has one, and stop with an error + if tests are failing. If set to ``warn``, then only a warning is printed + in this case. See also :envvar:`SAGE_CHECK_PACKAGES`. - :envvar:`SAGE_CHECK_PACKAGES` - if :envvar:`SAGE_CHECK` is set to ``yes``, @@ -1000,10 +1153,9 @@ Here are some of the more commonly used variables affecting the build process: .. note:: - As of this writing (September 2017, Sage 8.1), the test suites for the - Python 2 and 3 spkgs fail on most platforms. - So when this variable is empty or unset, Sage uses a default of - ``!python2,!python3``. + As of Sage 9.1, the test suites for the Python 2 and 3 spkgs fail + on most platforms. So when this variable is empty or unset, Sage + uses a default of ``!python2,!python3``. - :envvar:`SAGE_INSTALL_GCC` - **Obsolete, do not use, to be removed** @@ -1144,30 +1296,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`` @@ -1421,4 +1549,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 May 2020 (Sage 9.1).** diff --git a/src/doc/en/installation/troubles.rst b/src/doc/en/installation/troubles.rst index 93385c44aa1..cb70ba69167 100644 --- a/src/doc/en/installation/troubles.rst +++ b/src/doc/en/installation/troubles.rst @@ -7,7 +7,29 @@ If no binary version is available for your system, you can fallback to the :ref:`sec-installation-from-sources` or use one of the alternatives proposed at the end of :ref:`installation-guide`. -If you have any trouble, have a look at the -`FAQ wiki page `_, -the `SageMath forum `_ or -the `sage-support mailing list `_. +If you have any problems building or running Sage, please take a look +at the Installation FAQ in the `Sage Release Tour +`_ corresponding to the version +that you are installing. It may offer version-specific installation +help that has become available after the release was made and is +therefore not covered by this manual. + +Also please do not hesitate to ask for help in the `SageMath forum +`_ or the sage-support mailing +list at https://groups.google.com/forum/#!forum/sage-support. + +Also note the following. Each separate component of Sage is +contained in an SPKG; these are stored in ``build/pkgs/``. As each one +is built, a build log is stored in ``logs/pkgs/``, so you can browse these +to find error messages. If an SPKG fails to build, the whole build +process will stop soon after, so check the most recent log files +first, or run:: + + grep -li "^Error" logs/pkgs/* + +from the top-level Sage directory to find log files with error +messages in them. Send the file ``config.log`` as well as (a small +part of) the relevant log file to the sage-support mailing list +at https://groups.google.com/group/sage-support, making sure to +include at least some of the error messages; probably someone there +will have some helpful suggestions. diff --git a/src/doc/en/prep/Intro-Tutorial.rst b/src/doc/en/prep/Intro-Tutorial.rst index c7624c96070..2febd01ac19 100644 --- a/src/doc/en/prep/Intro-Tutorial.rst +++ b/src/doc/en/prep/Intro-Tutorial.rst @@ -24,7 +24,6 @@ following sections: - :ref:`SageCommands` - See :ref:`SageJupyterCommands` for the Jupyter notebook - - See :ref:`SageNBCommands` for the legacy Sage notebook - :ref:`SageFunctions` @@ -33,7 +32,6 @@ following sections: - :ref:`SageAnnotation` - See :ref:`JupyterAnnotation` for the Jupyter notebook - - See :ref:`SageNBAnnotation` for the legacy Sage notebook This tutorial only introduces the most basic level of functionality. @@ -47,11 +45,7 @@ Evaluating Sage Commands .. rubric:: Or, How do I get Sage to do some math? -We have two parallel subsections here which cover (roughly) the -same content. - - See :ref:`SageJupyterCommands` for the Jupyter notebook -- See :ref:`SageNBCommands` for the legacy Sage notebook .. _SageJupyterCommands: @@ -97,7 +91,7 @@ To do more mathematics, just do the same thing with more cells! .. image:: media/MoreCells.png :align: center -Unlike in the SageNB, one has to learn a variety of keyboard shortcuts +One has to learn a variety of keyboard shortcuts or click on various menu items to manipulate cells. There is a help menu to get you started on this; the Jupyter developers also maintain `an example notebook `_ @@ -106,93 +100,6 @@ which may assist you. .. image:: media/JupyterHelpMenu.png :align: center - - -.. _SageNBCommands: - -Evaluating in the SageNB notebook -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In any Sage worksheet, there are little boxes called *input cells* or -*code cells*. They should be about the width of your browser. - -.. image:: media/FirstCell.png - :align: center - -Evaluating the content of an input cell is very easy. - -- First, click inside the cell so that the cell is active (i.e., has a - bright blue border). - - .. image:: media/ActiveCell.png - :align: center - -- Then, just below the cell on the left, an "evaluate" link appears; - clicking this link evaluates the cell. - - .. image:: media/EvaluateCell.png - :align: center - -If you are using the live version of this documentation, try evaluating -the following cell. - -:: - - sage: 2+2 - 4 - -Sage prints out its response just below the cell (that's the ``4`` -above, so Sage confirms that :math:`2+2=4`). Note also that Sage has -automatically made the next cell active after you evaluated your first -cell. - -You can also evaluate a cell using a keyboard shortcut. - -- If a cell isn't active (such as below, in the live documentation), - click in it. - -- Then hold down the Shift key while you press the Enter key. - -We call this "Shift\-Enter". Try doing Shift\-Enter with this cell. - -:: - - sage: factor(2012) - 2^2 * 503 - -An input cell isn't much use if it can only do one thing, so you can -edit a cell and evaluate it again. Just click inside, and then make any -changes you wish by typing as usual. - -Try changing the number ``2012`` above to ``2011`` and evaluate the cell -to find its factorization (surprised?); then try your own favorite -number. - -To do more math, we'll need to be able to create new input cells. This -is also easy. - -- Move your cursor over the space above or below another cell. - -- A blue horizontal line as wide as the browser should appear. - -- Click on the line to insert a new cell. - -.. image:: media/ClickBlueLine.png - :align: center - -If for some reason you need to remove or delete an input cell, just -delete all the text inside of it, and then press backspace in the -now\-empty cell. - -Try creating a few new input cells below, doing some arithmetic in those -cells, and then deleting one of the input cells. - -.. skip - -:: - - 'Do some arithmetic in me, and make some other cells after me!' - .. _SageFunctions: Functions in Sage @@ -599,7 +506,6 @@ helpful to describe to the reader what is being done, such as in the description you are now reading. - :ref:`JupyterAnnotation` -- :ref:`SageNBAnnotation` .. _JupyterAnnotation: @@ -613,8 +519,7 @@ and the TeX rendering engine called more in Sage than just Sage commands. This math\-aware setup makes Sage perfect for annotating computations. -While the Jupyter notebook does not have as fully-featured a word -processor as the SageNB, we can still do a fair amount. +Jupyter notebook can function as a word processor. To use this functionality, we create a *Markdown cell* (as opposed to a *input cell* that contains Sage commands that Sage evaluates). @@ -656,113 +561,6 @@ in a Markdown cell. :align: center - -.. _SageNBAnnotation: - -SageNB Annotation -~~~~~~~~~~~~~~~~~ - -Thanks to `the mini\-word processor TinyMCE -`_ and a TeX rendering engine called -`MathJax `_, you can type much -more in the SageNB notebook worksheets -than just Sage commands. This math\-aware setup makes Sage -perfect for annotating computations. - -To use the word processor, we create a *text cell* (as opposed to a -*input cell* that contains Sage commands that Sage evaluates). - -To create a text cell, do the following. - -- First, move the cursor between two input cells, until the thin blue - line appears. - -- Then hold the Shift key and click on the thin blue line. - - .. image:: media/ClickBlueLine.png - :align: center - -So to create an input cell, one merely clicks, but one "Shift\-Click"s -to create a text cell. Here is what your text cell will look like. - - .. image:: media/TextEditor.png - :align: center - -In the live documentation, try inserting a text cell between the input -cells below. - -.. skip - -:: - - 2+2 - -.. skip - -:: - - 2+2 - -TinyMCE makes it easy for format text in many ways. Try experimenting -with the usual **bold** button, underline button, different text fonts -and colors, ordered and unordered lists, centering, and so on. Some of -the shortcut keys you are familiar with from other word processors may -also work, depending on your system. - -There are two other things you can do which take advantage of the -worksheet being on the web. - -- It is easy to link to other helpful websites for additional information. - - - While in the editor, highlight a word or two, and then click on the - little chain link toward the bottom right of the buttons. - - - You can now type in a web address to link to. - - - Be sure to prepend ``http://`` to the address. Normally, one should - also select it to appear in a new window (so the Sage session isn't - interrupted if someone clicks on it). - -- You may have already noticed that some of the descriptions above had - typeset mathematics in them. In fact we can add nearly arbitrary LaTeX - to our text cells! - - - For instance, it isn't too hard to add things like - - .. MATH:: - - \zeta(s)=\sum_{n=1}^{\infty}\frac{1}{n^s}=\prod_p \left(\frac{1}{1-p^{-s}}\right)\; . - - - One just types things like: - - .. CODE-BLOCK:: latex - - $$\zeta(s)=\sum_{n=1}^{\infty}\frac{1}{n^s}=\prod_p \left(\frac{1}{1-p^{-s}}\right)$$ - - in the word processor. - - - Whether this shows up as nicely as possible depends on what fonts you - have in your browser, but it should be legible. - - - More realistically, we might type ``$f(x)=x^2$`` so that we remember - that :math:`f(x)=x^2` in this worksheet. - -Here is a simpler example. - -:: - - sage: f(x)=x^2 - sage: f(9) - 81 - -If :math:`f(x)=x^2`, then :math:`f(9)=81`. - -It is simple to edit a text cell; simply double\-click on the text. - -If you are in the live version of this tutorial, try double\-clicking on -this text to edit this text cell (or any text cell) to see how we typed -the mathematics! - Of course, one can do much more, since Sage can execute arbitrary commands in the `Python `_ programming language, as well as output nicely formatted HTML, and so on. If you have enough diff --git a/src/doc/en/prep/Logging-On.rst b/src/doc/en/prep/Logging-On.rst index 3c5b1e43b70..cad2c8455c6 100644 --- a/src/doc/en/prep/Logging-On.rst +++ b/src/doc/en/prep/Logging-On.rst @@ -32,16 +32,6 @@ somewhat similar behavior. contact them or read some of their `documentation `_ for further assistance. -- Further below, we describe the process for logging on and making a - worksheet in the legacy :ref:`SageNB ` server, including: - - - :ref:`logging in ` for the first time - - - :ref:`editing a copy ` of a worksheet someone sent you - - - :ref:`making your own worksheet ` from scratch - - .. _Export: The Export screen and Jupyter notebook @@ -61,9 +51,8 @@ the third option to "export" them will not make sense. .. image:: media/NotebookExportDetails.png :align: center -The legacy SageNB is still a powerful web app, and has some advantages, -but is not longer under active development, so we recommend that new users -start with the Jupyter notebook. Jupyter will bring you to a screen +The legacy SageNB has been retired in Sage 9.1. +Please use the Jupyter notebook. Jupyter will bring you to a screen that is simply a listing of files in whatever folder Sage has opened in. .. image:: media/JupyterIntroScreen.png @@ -97,180 +86,3 @@ You should now have a worksheet that looks more or less like this. Now you are ready to begin to :ref:`evaluate Sage commands `! - - -.. _SageNB: - -Instructions for SageNB server -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. _LogOn: - -Creating an Account -------------------- - -When coming to a Sage server for the first time, it will look something -like this. - -.. image:: media/SignIn.png - :align: center - -You can create an account in three ways. - -- Use an :ref:`OpenID ` account to verify your account - -- Create a login in the :ref:`standard way ` by creating a - username and password. - -- If you have opened it on your local machine (e.g. from the command - line or the Mac app), you should be automatically logged in. - -.. _OpenID: - -OpenID -~~~~~~ - -With many public Sage servers, you can use an OpenID such as Google, -Yahoo!, and so forth to create an account. - -.. image:: media/SignInOpenID.png - :align: center - -To create an account, just make sure you are logged in with your -verification website, and then click the correct logo of the many on the -lower right. Then you should come to a page like this. - -.. image:: media/OpenIDPage.png - :align: center - -From there, you should be taken directly to your new notebook, ready to -make your :ref:`first worksheet `. - -.. _Standard: - -Standard Account Creation -~~~~~~~~~~~~~~~~~~~~~~~~~ - -The normal way to create an account is quite straightforward as well. - -.. image:: media/SignInNormal.png - :align: center - -Just click on the relevant link, and you'll be taken to a page where you -create a new username and password. - -.. image:: media/RegularSigninPage.png - :align: center - -In this example, there is a "magic word"; there could be a different -security question as well. In that case, you'll have already been given -the information if you're authorized to be on that server. - -In this scenario, you'll be taken back to the main login page, where -you'll need to put in your new login information. - -.. image:: media/HaveSignin.png - :align: center - -Then you'll be sent to a new notebook, ready to make your :ref:`first -worksheet `. - -Two Usage Scenarios -------------------- - -There are two main scenarios when starting with Sage. - -- You are running Sage locally or - going to a Sage server, and just want to start trying some - mathematics. We cover this situation :ref:`first `. - -- Someone has given you a link to a published tutorial or other - worksheet (perhaps one similar to this!) and you would like to try out - the mathematics there, using your own editable copy of the worksheet. - We cover this less common situation :ref:`below `. - -.. NOTE:: - - In either scenario, the Sage notebook will be saving your files - in a "hidden" location not meant for ordinary users and you will - ordinarily interact with your Sage worksheets only through the - notebook server. - -.. _FromScratch: - -Starting a New Worksheet from Scratch -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sage on a server functions via individual documents called *worksheets*. -If you are sent to one and you want to make a :ref:`live copy -`, that is one thing, but usually you will start your Sage -session with just an empty notebook, with no worksheets yet in it. - -.. image:: media/EmptyNotebook.png - :align: center - -There are a few things you can do here, but usually you'll want to start -a new worksheet. - -.. image:: media/EmptyNotebookGetNew.png - :align: center - -Once you've done this, it should look something like this: - -.. image:: media/NewWorksheet.png - :align: center - -You can leave the name, or call it whatever you like. Then you should -see your first "cell", the rectangle in this picture. - -.. image:: media/FirstCell.png - :align: center - -But at this point you are ready to go on :ref:`evaluate Sage commands -`! - -.. _LiveCopy: - -Getting a Live Copy of a Worksheet -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Occasionally, you'll get started with Sage by someone giving you a link -to a *published worksheet* that someone else has created. In order to -do math on it, you'll need your *own* copy of the worksheet on the -server. - -If you are logged in and have your own copy, it should look like this at -the top: - -.. image:: media/LiveWorksheet.png - :align: center - -Except, of course, *your* username will appear! If you already have a -live copy, you're all set and should start trying it out, possibly -referring to the :doc:`first tutorial ` for tips. - -More likely, you'll need to follow a few steps. - -- Take another look at the top of the screen. Does it look like this? - - .. image:: media/NotLoggedIn.png - :align: center - - If you already have an account on the server, log in; otherwise, you - may want to review how to :ref:`get an account `. - -- Once you have an account and are logged in, you'll need to go back to - your original link for the published worksheet. In either event, the - worksheet should now look like this. - - .. image:: media/LoggedIn.png - :align: center - -- Now just click 'Edit a copy' so that it looks like this! - - .. image:: media/LiveWorksheet.png - :align: center - -Now you're ready to learn how to actually :ref:`evaluate those Sage -commands `! Good luck. - diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index f0e1a1647e1..2a7c43950da 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -48,12 +48,10 @@ Named associative algebras sage/algebras/cluster_algebra sage/combinat/descent_algebra sage/algebras/hall_algebra - sage/algebras/iwahori_hecke_algebra sage/combinat/posets/incidence_algebras sage/algebras/group_algebra sage/combinat/grossman_larson_algebras sage/combinat/posets/moebius_algebra - sage/algebras/nil_coxeter_algebra sage/algebras/orlik_terao sage/algebras/orlik_solomon sage/algebras/quantum_matrix_coordinate_algebra @@ -68,6 +66,16 @@ Named associative algebras sage/algebras/steenrod/steenrod_algebra_mult sage/algebras/weyl_algebra sage/algebras/yangian + +Hecke algebras +-------------- + +.. toctree:: + :maxdepth: 2 + + sage/algebras/hecke_algebras/ariki_koike_algebra + sage/algebras/iwahori_hecke_algebra + sage/algebras/nil_coxeter_algebra sage/algebras/yokonuma_hecke_algebra Various associative algebras @@ -80,6 +88,7 @@ Various associative algebras sage/algebras/cellular_basis sage/algebras/commutative_dga sage/algebras/q_system + sage/algebras/splitting_algebra Non-associative algebras ------------------------ 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/coding/index.rst b/src/doc/en/reference/coding/index.rst index 2e3011f37e7..7b83537c5a6 100644 --- a/src/doc/en/reference/coding/index.rst +++ b/src/doc/en/reference/coding/index.rst @@ -14,7 +14,9 @@ decoders. The following modules provide the base classes defining them. :maxdepth: 1 sage/coding/abstract_code + sage/coding/linear_code_no_metric sage/coding/linear_code + sage/coding/linear_rank_metric sage/coding/channel sage/coding/encoder sage/coding/decoder @@ -49,6 +51,7 @@ computations for structural invariants are available. sage/coding/reed_muller_code sage/coding/grs_code sage/coding/goppa_code + sage/coding/kasami_codes In contrast, for some code families Sage can only construct their generator matrix and has no other a priori knowledge on them: diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 4b6c0b4e656..d8be446a41e 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -25,6 +25,7 @@ Comprehensive Module list sage/combinat/baxter_permutations sage/combinat/binary_recurrence_sequences sage/combinat/binary_tree + sage/combinat/blob_algebra sage/combinat/cartesian_product sage/combinat/catalog_partitions sage/combinat/chas/__init__ @@ -119,6 +120,7 @@ Comprehensive Module list sage/combinat/enumeration_mod_permgroup sage/combinat/expnums sage/combinat/family + sage/combinat/fast_vector_partitions sage/combinat/finite_state_machine sage/combinat/finite_state_machine_generators sage/combinat/fqsym @@ -170,6 +172,9 @@ Comprehensive Module list sage/combinat/output sage/combinat/parallelogram_polyomino sage/combinat/parking_functions + sage/combinat/path_tableaux/catalog + sage/combinat/path_tableaux/dyck_path + sage/combinat/path_tableaux/path_tableau sage/combinat/plane_partition sage/combinat/partition sage/combinat/partition_algebra @@ -183,7 +188,9 @@ Comprehensive Module list sage/combinat/posets/__init__ sage/combinat/posets/all sage/combinat/posets/cartesian_product + sage/combinat/posets/d_complete sage/combinat/posets/elements + sage/combinat/posets/forest sage/combinat/posets/hasse_diagram sage/combinat/posets/incidence_algebras sage/combinat/posets/lattices @@ -277,6 +284,7 @@ Comprehensive Module list sage/combinat/root_system/weight_lattice_realizations sage/combinat/root_system/weight_space sage/combinat/root_system/weyl_characters + sage/combinat/root_system/fusion_ring sage/combinat/root_system/weyl_group sage/combinat/rooted_tree sage/combinat/rsk diff --git a/src/doc/en/reference/conf.py b/src/doc/en/reference/conf.py index f6a5c4490dc..c2372cf6ca3 100644 --- a/src/doc/en/reference/conf.py +++ b/src/doc/en/reference/conf.py @@ -59,8 +59,8 @@ # Sorted list of subdocs. Include all subdirectories of ref_src except # for 'static' and 'templates', and to deal with upgrades: 'sage', -# 'sagenb', 'media', and 'other'. -bad_directories = ['static', 'templates', 'sage', 'sagenb', 'media', 'other'] +# 'media', and 'other'. +bad_directories = ['static', 'templates', 'sage', 'media', 'other'] multidocs_subdoc_list = sorted([x for x in os.listdir(ref_src) if os.path.isdir(os.path.join(ref_src, x)) and x not in bad_directories]) @@ -68,5 +68,5 @@ # List of directories, relative to source directory, that shouldn't be # searched for source files. exclude_patterns += multidocs_subdoc_list + [ - 'sage', 'sagenb', 'options' + 'sage', 'options' ] diff --git a/src/doc/en/reference/conf_sub.py b/src/doc/en/reference/conf_sub.py index 058fc011010..9ca45fad1a4 100644 --- a/src/doc/en/reference/conf_sub.py +++ b/src/doc/en/reference/conf_sub.py @@ -16,8 +16,6 @@ from sage.docs.conf import release, exclude_patterns from sage.docs.conf import * -from six.moves import range - ref_src = os.path.join(SAGE_DOC_SRC, 'en', 'reference') ref_out = os.path.join(SAGE_DOC, 'html', 'en', 'reference') @@ -39,14 +37,14 @@ title = title.replace(u'`', u'$') # General information about the project. -project = u'Sage Reference Manual: ' + title +project = u'Sage {} Reference Manual: '.format(release) + title # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -html_title = u'Sage Reference Manual v' + release + ': ' + title +html_title = project # A shorter title for the navigation bar. Default is the same as html_title. -html_short_title = title +html_short_title = project # HTML theme (e.g., 'default', 'sphinxdoc'). The pages for the # reference manual use a custom theme, a slight variant on the 'sage' diff --git a/src/doc/en/reference/curves/index.rst b/src/doc/en/reference/curves/index.rst index 21dc2344e89..fdaa33509bd 100644 --- a/src/doc/en/reference/curves/index.rst +++ b/src/doc/en/reference/curves/index.rst @@ -114,6 +114,7 @@ Modularity and `L`-series over `\QQ`. sage/schemes/elliptic_curves/modular_parametrization sage/schemes/elliptic_curves/ell_modular_symbols + sage/schemes/elliptic_curves/mod_sym_num sage/schemes/elliptic_curves/lseries_ell sage/schemes/elliptic_curves/heegner sage/schemes/elliptic_curves/padic_lseries diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst index 3ed01c597ee..3d027326933 100644 --- a/src/doc/en/reference/discrete_geometry/index.rst +++ b/src/doc/en/reference/discrete_geometry/index.rst @@ -33,6 +33,7 @@ Polyhedra sage/geometry/polyhedron/plot sage/geometry/polyhedron/face sage/geometry/polyhedron/cdd_file_format + sage/geometry/polyhedron/modules/formal_polyhedra_module Lattice polyhedra ~~~~~~~~~~~~~~~~~ @@ -54,6 +55,7 @@ Toric geometry sage/geometry/toric_lattice sage/geometry/cone + sage/geometry/cone_catalog sage/geometry/fan sage/geometry/fan_morphism sage/geometry/point_collection diff --git a/src/doc/en/reference/dynamics/cellular_automata.rst b/src/doc/en/reference/dynamics/cellular_automata.rst index 8aeefcf2947..6197e127e23 100644 --- a/src/doc/en/reference/dynamics/cellular_automata.rst +++ b/src/doc/en/reference/dynamics/cellular_automata.rst @@ -4,5 +4,8 @@ Cellular Automata .. toctree:: :maxdepth: 2 - ../sage/dynamics/cellular_automata/solitons + ../sage/dynamics/cellular_automata/catalog + ../sage/dynamics/cellular_automata/elementary + ../sage/dynamics/cellular_automata/glca + ../sage/dynamics/cellular_automata/solitons diff --git a/src/doc/en/reference/graphs/index.rst b/src/doc/en/reference/graphs/index.rst index a3ba6ea46e7..32758403793 100644 --- a/src/doc/en/reference/graphs/index.rst +++ b/src/doc/en/reference/graphs/index.rst @@ -75,7 +75,6 @@ Libraries of algorithms sage/graphs/matchpoly sage/graphs/genus sage/graphs/lovasz_theta - sage/graphs/linearextensions sage/graphs/schnyder sage/graphs/planarity sage/graphs/traversals diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index ee2aa969af3..6deebf5e63d 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -14,7 +14,6 @@ User Interface * :doc:`Command Line Interface (REPL) ` * For the Jupyter notebook interface, visit `its documentation `_. -* For the legacy notebook interface, which is no longer actively maintained, visit the `source repository `_. Graphics ======== @@ -37,10 +36,11 @@ Basic Rings and Fields * :doc:`Integers and Rational Numbers ` * :doc:`Real and Complex Numbers ` -* :doc:`Polynomials ` +* :doc:`Commutative Polynomials ` * :doc:`Power Series and Laurent Series ` * :doc:`Finite Rings and Fields ` * :doc:`p-Adic Numbers ` +* :doc:`Noncommutative Polynomials ` * :doc:`Quaternion Algebras ` Linear Algebra @@ -151,6 +151,7 @@ Interfaces General Information =================== +* :doc:`External Packages ` * :doc:`References ` * :doc:`History and License ` * :ref:`genindex` diff --git a/src/doc/en/reference/interfaces/index.rst b/src/doc/en/reference/interfaces/index.rst index 8680b94deb8..95a8678b071 100644 --- a/src/doc/en/reference/interfaces/index.rst +++ b/src/doc/en/reference/interfaces/index.rst @@ -76,6 +76,7 @@ and testing to make sure nothing funny is going on). sage/interfaces/gp sage/interfaces/jmoldata sage/interfaces/kash + sage/interfaces/kenzo sage/interfaces/latte sage/interfaces/lie sage/interfaces/lisp diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index 8b438ad7255..9a7702b1309 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -16,14 +16,6 @@ General Infrastructure Programming Utilities --------------------- -Python 2 and 3 Compatibility -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. toctree:: - :maxdepth: 1 - - sage/misc/six - Special Base Classes, Decorators, etc. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -39,6 +31,7 @@ Special Base Classes, Decorators, etc. sage/misc/method_decorator sage/misc/object_multiplexer sage/misc/fast_methods + sage/misc/call Lists and Iteration, etc. ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -104,6 +97,7 @@ Miscellaneous Useful Functions sage/misc/misc sage/misc/misc_c + sage/misc/verbose Lazyness ~~~~~~~~ @@ -174,6 +168,7 @@ Formatted Output sage/typeset/character_art_factory sage/typeset/ascii_art sage/typeset/unicode_art + sage/misc/repr sage/misc/sage_input sage/misc/table @@ -218,7 +213,6 @@ Interactive Sage Sessions .. toctree:: :maxdepth: 1 - sage/misc/log sage/misc/banner sage/misc/reset sage/misc/viewer diff --git a/src/doc/en/reference/modules/index.rst b/src/doc/en/reference/modules/index.rst index 35d0e3f2cf4..0975f276ffb 100644 --- a/src/doc/en/reference/modules/index.rst +++ b/src/doc/en/reference/modules/index.rst @@ -35,6 +35,7 @@ Modules sage/modules/with_basis/cell_module sage/modules/with_basis/morphism sage/modules/with_basis/subquotient + sage/modules/with_basis/representation sage/modules/finite_submodule_iter sage/modules/free_quadratic_module diff --git a/src/doc/en/reference/monoids/index.rst b/src/doc/en/reference/monoids/index.rst index a91b3b9cc2c..0e7279db6d3 100644 --- a/src/doc/en/reference/monoids/index.rst +++ b/src/doc/en/reference/monoids/index.rst @@ -2,7 +2,8 @@ Monoids ======= Sage supports free monoids and free abelian monoids in any -finite number of indeterminates. +finite number of indeterminates, as well as free partially +commutative monoids (trace monoids). .. toctree:: :maxdepth: 2 @@ -18,5 +19,6 @@ finite number of indeterminates. sage/monoids/string_ops sage/monoids/hecke_monoid sage/monoids/automatic_semigroup + sage/monoids/trace_monoid .. include:: ../footer.txt diff --git a/src/doc/en/reference/noncommutative_polynomial_rings/conf.py b/src/doc/en/reference/noncommutative_polynomial_rings/conf.py new file mode 120000 index 00000000000..2bdf7e68470 --- /dev/null +++ b/src/doc/en/reference/noncommutative_polynomial_rings/conf.py @@ -0,0 +1 @@ +../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/noncommutative_polynomial_rings/index.rst b/src/doc/en/reference/noncommutative_polynomial_rings/index.rst new file mode 100644 index 00000000000..9848988d126 --- /dev/null +++ b/src/doc/en/reference/noncommutative_polynomial_rings/index.rst @@ -0,0 +1,35 @@ +Noncommutative Polynomials +========================== + +Univariate Ore polynomial rings +------------------------------- + +.. toctree:: + :maxdepth: 2 + + sage/rings/polynomial/ore_polynomial_ring + sage/rings/polynomial/ore_polynomial_element + sage/rings/polynomial/skew_polynomial_ring + sage/rings/polynomial/skew_polynomial_element + sage/rings/polynomial/skew_polynomial_finite_order + sage/rings/polynomial/skew_polynomial_finite_field + +Fraction field of Ore polynomial rings +-------------------------------------- + +.. toctree:: + :maxdepth: 2 + + sage/rings/polynomial/ore_function_field + sage/rings/polynomial/ore_function_element + +Noncommutative Multivariate Polynomials +--------------------------------------- + +.. toctree:: + :maxdepth: 1 + + sage/rings/polynomial/plural + +.. include:: ../footer.txt + diff --git a/src/doc/en/reference/plot3d/threejs.rst b/src/doc/en/reference/plot3d/threejs.rst index 82ddf609f71..3c1cda92343 100644 --- a/src/doc/en/reference/plot3d/threejs.rst +++ b/src/doc/en/reference/plot3d/threejs.rst @@ -1,3 +1,4 @@ +.. _threejs_viewer: ================================== Three.js JavaScript WebGL Renderer @@ -6,7 +7,7 @@ Three.js JavaScript WebGL Renderer A web-based interactive viewer using the Three.js JavaScript library maintained by https://threejs.org. -The viewer is invoked by adding the keyword argument ``viewer='threejs'`` to the command +The viewer is invoked by adding the keyword argument ``viewer='threejs'`` to the command ``show()`` or any three-dimensional graphic. The scene is rendered and displayed in the users's web browser. Interactivity includes @@ -59,6 +60,28 @@ Options currently supported by the viewer: - ``thickness`` -- (default: 1) numeric value for thickness of lines +- ``viewpoint`` -- (default: None) list or tuple of the form [[x,y,z],angle] setting the initial + viewpoint of the scene, where angle is in degrees; can be determined using the 'Get Viewpoint' + option of the information menu + +In addition, the following animation-related options are supported: + +- ``animate`` -- (default: depends) whether to enable animation. Automatically set to ``True`` + if animation data is present in the plot. If ``False``, all frames of animation will be displayed + simultaneously. + +- ``animation_controls`` -- (default: True) whether to include the playback slider and buttons + (play, pause, etc.) in the page + +- ``auto_play`` -- (default: True) whether to immediately start playing the animation when the page + loads. Recommend setting ``animation_controls=True`` to be able to start playback. + +- ``delay`` -- (default: 20) an integer amount of time between consecutive frames of animation, + in hundredths of a second + +- ``loop`` -- (default: True) whether to loop the animation or have it stop after reaching the end. + Can be toggled on the page itself if ``animation_controls`` is set. + Clicking on the information icon in the lower right-hand corner of the viewer opens a menu of available actions. These include saving the three-dimensional scene as a static PNG image or as complete HTML source code. @@ -66,6 +89,7 @@ PNG image or as complete HTML source code. AUTHORS: - Paul Masson (2016): Initial version +- Joshua Campbell (2020): Animation support EXAMPLES: @@ -87,6 +111,19 @@ A parametric helix:: .. RAW:: html :file: threejs_examples/helix.html +An :meth:`~sage.plot.animate.Animation.interactive` animation:: + + sage: def build_frame(t): + ....: e = parametric_plot3d([sin(x-t), 0, x], (x, 0, 2*pi), color='red') + ....: m = parametric_plot3d([0, -sin(x-t), x], (x, 0, 2*pi), color='green') + ....: return e + m + sage: frames = [build_frame(t) for t in (0, pi/32, pi/16, .., 2*pi)] + sage: plot = animate(frames).interactive() + sage: show(plot, delay=5, auto_play=False, projection='orthographic') + +.. RAW:: html + :file: threejs_examples/animation.html + .. RAW:: html diff --git a/src/doc/en/reference/plot3d/threejs_examples/animation.html b/src/doc/en/reference/plot3d/threejs_examples/animation.html new file mode 100644 index 00000000000..5a8a1e1e884 --- /dev/null +++ b/src/doc/en/reference/plot3d/threejs_examples/animation.html @@ -0,0 +1,992 @@ + \ No newline at end of file diff --git a/src/doc/en/reference/plot3d/threejs_examples/template.html b/src/doc/en/reference/plot3d/threejs_examples/template.html index 3ae675a246c..04fc7d5818b 100644 --- a/src/doc/en/reference/plot3d/threejs_examples/template.html +++ b/src/doc/en/reference/plot3d/threejs_examples/template.html @@ -1,5 +1,6 @@ \ No newline at end of file diff --git a/src/doc/en/reference/polynomial_rings/index.rst b/src/doc/en/reference/polynomial_rings/index.rst index 13d6b62af4a..4a30310f740 100644 --- a/src/doc/en/reference/polynomial_rings/index.rst +++ b/src/doc/en/reference/polynomial_rings/index.rst @@ -29,16 +29,6 @@ Multivariate Polynomials invariant_theory polynomial_rings_toy_implementations -Skew Polynomials ----------------- - -.. toctree:: - :maxdepth: 2 - - sage/rings/polynomial/skew_polynomial_element - sage/rings/polynomial/skew_polynomial_ring_constructor - sage/rings/polynomial/skew_polynomial_ring - Rational Functions ------------------ @@ -79,12 +69,4 @@ Boolean Polynomials sage/rings/polynomial/pbori -Noncommutative Polynomials --------------------------- - -.. toctree:: - :maxdepth: 1 - - sage/rings/polynomial/plural - .. include:: ../footer.txt diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 93820404a1a..b5fd40b2265 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -91,6 +91,10 @@ REFERENCES: Journal of Combinatorial Theory, Series B, vol 98, n.6, pp 1119-1164, 2008. :doi:`10.1016/j.jctb.2007.12.006`. +.. [ABS2004] \N. Alon, I. Benjamini and Alan Stacey, *Percolation on finite + graphs and isoperimetric inequalities*, The Annals of Probability + 32 (2004), no. 3A, 1727-1745. + .. [ADKF1970] \V. Arlazarov, E. Dinic, M. Kronrod, and I. Faradzev. 'On Economical Construction of the Transitive Closure of a Directed Graph.' @@ -295,6 +299,9 @@ REFERENCES: Math., Combinatorics (T. S. Motzkin, ed.), vol. 19, AMS, Providence 1971 +.. [At1990] \M. D. Atkinson. *On computing the number of linear extensions of a + tree.* Order 7 (1990) 20-25. + .. [At1992] \M. D. Atkinson. *Solomon's descent algebra revisited.* Bull. London Math. Soc. 24 (1992) 545-551. http://www.cs.otago.ac.nz/staffpriv/mike/Papers/Descent/DescAlgRevisited.pdf @@ -344,6 +351,9 @@ REFERENCES: **B** +.. [BaKi2001] Bakalov and Kirillov, *Lectures on tensor categories and modular functors*, + AMS (2001). + .. [Ba1994] Kaushik Basu. *The Traveler's Dilemma: Paradoxes of Rationality in Game Theory*. The American Economic Review (1994): 391-395. @@ -608,6 +618,13 @@ REFERENCES: Annals of Mathematics, Second Series, Vol. 29, No. 1/4 (1927 - 1928), pp. 38-46 +.. [Ben1998] \I. Benjamini, *Expanders are not hyperbolic*, Israel Journal of + Mathematics 108 (1998), 33-36. + +.. [BS1997] \I. Benjamini and O. Schramm, *Every graph with a positive Cheeger + constant contains a tree with a positive Cheeger constant*, + Geometric and Functional Analysis 7 (1997), no. 3, 403-419. + .. [Benasque2009] Fernando Rodriguez Villegas, *The L-function of the quintic*, http://users.ictp.it/~villegas/hgm/benasque-2009-report.pdf @@ -864,6 +881,13 @@ REFERENCES: .. [BM1983] Buer, B., and Mohring, R. H. A fast algorithm for decomposition of graphs and posets, Math. Oper. Res., Vol 8 (1983): 170-184. +.. [BM1993] \M. Broué and G. Malle, *Zyklotomische Heckealgebren*, + Asterisque, **212** (1993), 119-89. + +.. [BM1997] \K. Bremke and G. Malle, + *Reduced words and a length function for* `G(e,1,n)`. + Indag. Mathem., N.S., **8** (1997), no. 4, 453-469. + .. [BM2008] John Adrian Bondy and U.S.R. Murty, "Graph theory", Volume 244 of Graduate Texts in Mathematics, 2nd edition, Springer, 2008. @@ -1057,6 +1081,10 @@ REFERENCES: :doi:`10.1109/SFCS.1989.63516`, _ +.. [Broder2000] Broder, A.Z., Kumar, R., Maghoul, F., Raghavan, P., Rajagopalan, + S., Stata, R., Tomkins, A., Wiener, J.L.: Graph structure in + the web. Computer Networks 33(1-6), 309–320 (2000) + .. [BRS2015] \A. Boussicault, S. Rinaldi et S. Socci. *The number of directed k-convex polyominoes* 27th Annual International Conference on Formal Power Series and @@ -1122,6 +1150,11 @@ REFERENCES: :doi:`10.1016/j.dam.2003.06.002` http://jl.baril.u-bourgogne.fr/derange.pdf +.. [BV2009] Stephen Boyd and Lieven Vandenberghe. + Convex Optimization. + Cambridge University Press, Cambridge, 2009. + ISBN 9780521833783. + .. [BvR1982] Andries Brouwer and John van Rees, More mutually orthogonal Latin squares, Discrete Mathematics, @@ -1170,6 +1203,10 @@ REFERENCES: .. [Car1972] \R. W. Carter. *Simple groups of Lie type*, volume 28 of Pure and Applied Mathematics. John Wiley and Sons, 1972. +.. [CQ2019] \A. Cassella and C. Quadrelli. + *Right-angled Artin groups and enhanced Koszul properties*. + Preprint, :arxiv:`1907.03824`, (2019). + .. [CS1996] \G. Call and J. Silverman. Computing the Canonical Height on K3 Surfaces. Mathematics of Comp. , 65 (1996), 259-290. @@ -1282,6 +1319,13 @@ REFERENCES: Computer Science, vol. 6346, 302-313. Springer (2010). :doi:`10.1007/978-3-642-15775-2_26`. +.. [CGLM2012] \Crescenzi P., Grossi R., Lanzi L., Marino A. (2012) + *On Computing the Diameter of Real-World Directed (Weighted) + Graphs*. In: Klasing R. (eds) Experimental Algorithms. SEA 2012. + Lecture Notes in Computer Science, vol 7276. + Springer, Berlin, Heidelberg + :doi:`https://doi.org/10.1007/978-3-642-30850-5_10`. + .. [CGW2013] Daniel Cabarcas, Florian Göpfert, and Patrick Weiden. Provably Secure LWE-Encryption with Uniform Secret. Cryptology ePrint Archive, Report 2013/164. 2013. @@ -1331,7 +1375,10 @@ REFERENCES: Experience, 4:1-10 (2001). http://www.hipersoft.rice.edu/grads/publications/dom14.pdf -.. [CHPSS18] C. Cid, T. Huang, T. Peyrin, Y. Sasaki, L. Song. +.. [Chu2007] \F. Chung, *Random walks and local cuts in graphs*, Linear Algebra + and its Applications 423 (2007), no. 1, 22-32. + +.. [CHPSS18] \C. Cid, T. Huang, T. Peyrin, Y. Sasaki, L. Song. *Boomerang Connectivity Table: A New Cryptanalysis Tool* (2018) IACR Transactions on Symmetric Cryptology. Vol 2017, Issue 4. pre-print available at https://eprint.iacr.org/2018/161.pdf @@ -1374,6 +1421,10 @@ REFERENCES: Ann. Combinatorics (6), 2002 pp. 125-145. :doi:`10.1007/PL00012580`. +.. [CL2017] Xavier Caruso and Jérémy Le Borgne, + *A new faster algorithm for factoring skew polynomials over finite fields* + J. Symbolic Comput. 79 (2017), 411-443. + .. [CL2013] Maria Chlouveraki and Sofia Lambropoulou. *The Yokonuma-Hecke algebras and the HOMFLYPT polynomial*. (2015) :arxiv:`1204.1871v4`. @@ -1435,6 +1486,14 @@ REFERENCES: "Orange" https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/orange-spec.pdf +.. [CoCo1] J.H. Conway, H.S.M. Coxeter + *Triangulated polygons and frieze patterns*, + The Mathematical Gazette (1973) 57 p.87-94 + +.. [CoCo2] J.H. Conway, H.S.M. Coxeter + *Triangulated polygons and frieze patterns (continued)*, + The Mathematical Gazette (1973) 57 p.175-183 + .. [Co1984] \J. Conway, Hexacode and tetracode - MINIMOG and MOG. *Computational group theory*, ed. M. Atkinson, Academic Press, 1984. @@ -1549,7 +1608,10 @@ REFERENCES: Curves*. Cambridge University Press, 1997. .. [Cre2003] Cressman, Ross. *Evolutionary dynamics and extensive form - games*. MIT Press, 2003. + games*. MIT Press, 2003. + +.. [Cre2020] Creedon, Samuel. *The center of the partition algebra*. + Preprint, :arxiv:`2005.00600` (2020). .. [Cro1983] \M. Crochemore, Recherche linéaire d'un carré dans un mot, C. R. Acad. Sci. Paris Sér. I Math. 296 (1983) 14 @@ -1578,6 +1640,10 @@ REFERENCES: and groups, 3rd. ed., Grundlehren der Mathematischen Wissenschaften, vol. 290, Springer-Verlag, New York, 1999. +.. [CS1988] Conway, J. H., and N. J. A. Sloane. “Low-Dimensional Lattices. IV. + The Mass Formula.” Proceedings of the Royal Society of London. + Series A, Mathematical and Physical Sciences, vol. 419, no. 1857, 1988, pp. 259-286. + .. [CS2003] \John E. Cremona and Michael Stoll. On The Reduction Theory of Binary Forms. Journal für die reine und angewandte Mathematik, 565 (2003), 79-99. @@ -1601,6 +1667,10 @@ REFERENCES: Construction of Differentially 4-Uniform Bijections*, Inscrypt, pp. 22-38, 2013. +.. [CT1998] \F. Chung and P. Tetali, *Isoperimetric inequalities for + Cartesian products of graphs*, Combinatorics, Probability and + Computing 7 (1998), no. 2, 141-148. + .. [Cu1984] \R. Curtis, The Steiner system `S(5,6,12)`, the Mathieu group `M_{12}`, and the kitten. *Computational group theory*, ed. M. Atkinson, Academic Press, 1984. @@ -1609,6 +1679,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. @@ -1727,6 +1804,10 @@ REFERENCES: standards et permutations de Baxter, proceedings of Formal Power Series and Algebraic Combinatorics, 1994. +.. [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 + .. [DGMPPS2019] \N. Datta, A. Ghoshal, D. Mukhopadhyay, S. Patranabis, S. Picek, R. Sashukhan. "TRIFLE" https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/trifle-spec.pdf @@ -1831,6 +1912,11 @@ REFERENCES: .. [DR2002] Joan Daemen, Vincent Rijmen. *The Design of Rijndael*. Springer-Verlag Berlin Heidelberg, 2002. +.. [Dragan2018] Feodor Dragan, Michel Habib, Laurent Viennot. + *Revisiting Radius, Diameter, and all Eccentricity Computation + in Graphs through Certificates*. + http://arxiv.org/abs/1803.04660 + .. [Dro1987] Carl Droms. *Isomorphisms of graph groups*. Proc. of the Amer. Math. Soc. **100** (1987). No 3. http://educ.jmu.edu/~dromscg/vita/preprints/Isomorphisms.pdf @@ -1838,10 +1924,6 @@ REFERENCES: .. [DS1994] J. Dalbec and B. Sturmfels. Invariant methods in discrete and computational geometry, chapter Introduction to Chow forms, pages 37-58. Springer Netherlands, 1994. -.. [DS2010] \K. Duggal, B. Sahin, - *Differential Geometry of Lightlike Submanifolds*, - Frontiers in Mathematics, 2010. - .. [DS2004] Dan Gusfield, Jens Stoye, Linear time algorithms for finding and representing all the tandem repeats in a string, Journal of Computer and System Sciences, @@ -1850,6 +1932,14 @@ REFERENCES: Pages 525-546, https://doi.org/10.1016/j.jcss.2004.03.004. +.. [DS2009] \W. Decker, F.-O. Schreyer, + *Varieties, Gröbner Bases, and Algebraic Curves*, 2009. + https://www.math.uni-sb.de/ag/schreyer/images/PDFs/teaching/ws1617ag/book.pdf + +.. [DS2010] \K. Duggal, B. Sahin, + *Differential Geometry of Lightlike Submanifolds*, + Frontiers in Mathematics, 2010. + .. [Du2001] \I. Duursma, "From weight enumerators to zeta functions", in Discrete Applied Mathematics, vol. 111, no. 1-2, pp. 55-73, 2001. @@ -1873,6 +1963,9 @@ REFERENCES: Connection Table* (preprint); in Cryptology ePrint Archive, (2018), 631. +.. [Duc1998] \L. Ducos, Optimizations of the Subresultant Algorithm. + http://www-math.univ-poitiers.fr/~ducos/Travaux/sous-resultants.pdf + .. [Dur1998] \F. Durand, *A characterization of substitutive sequences using return words*, Discrete Math. 179 (1998) 89-101. @@ -1919,6 +2012,9 @@ REFERENCES: 1981, Pages 108-125, ISSN 0097-3165,:doi:`10.1016/0097-3165(81)90007-8`. (http://www.sciencedirect.com/science/article/pii/0097316581900078) +.. [EGNO2015] Pavel Etingof, Shlomo Gelaki, Dmitri Nikshych and Victor Ostrik, + *Tensor Categories*, AMS Mathematical Surveys and Monographs 205 (2015). + .. [EGHLSVY] Pavel Etingof, Oleg Golberg, Sebastian Hensel, Tiankai Liu, Alex Schwendner, Dmitry Vaintrob, Elena Yudovina, "Introduction to representation theory", @@ -1933,6 +2029,9 @@ REFERENCES: Version 1.3". 2013. http://www.wolfgang-ehrhardt.de/specialfunctions.pdf. +.. [EL2002] Ekedahl, Torsten & Laksov, Dan. (2002). *Splitting algebras, Symmetric functions and + Galois Theory*. J. Algebra Appl. 4, :doi:`10.1142/S0219498805001034` + .. [EM2001] Pavel Etingof and Xiaoguang Ma. *Lecture notes on Cherednik algebras*. http://www-math.mit.edu/~etingof/73509.pdf :arxiv:`1001.0432`. @@ -1941,6 +2040,13 @@ REFERENCES: *Fifth Annual Graph Drawing Contest*; http://www.merl.com/papers/docs/TR98-16.pdf +.. [Eny2012] \J. Enyang. *Jucys-Murphy elements and a presentation + for the partition algebra*. J. Algebraic Combin. + **37** (2012) no 3, 401--454. + +.. [Eny2013] \J. Enyang. *A seminormal form for partition algebras*. + J. Combin. Theory Series A **120** (2013) 1737--1785. + .. [EP2013] David Einstein, James Propp. *Combinatorial, piecewise-linear, and birational homomesy for products of two chains*. :arxiv:`1310.5294v1`. @@ -1995,6 +2101,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`. @@ -2269,6 +2379,11 @@ REFERENCES: .. [GJ2007] \A. Glen, J. Justin, Episturmian words: a survey, Preprint, 2007, :arxiv:`0801.1655`. +.. [GJ2016] Muddappa Seetharama Gowda and Juyoung Jeong. + Spectral cones in Euclidean Jordan algebras. + Linear Algebra and its Applications, 509:286-305, 2016. + :doi:`10.1016/j.laa.2016.08.004`. + .. [GJKPRSS2019] \D. Goudarzi, J. Jean, S. Koelbl, T. Peyrin, M. Rivain, Y. Sasaki, S. M. Sim. "Pyjamask" https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/Pyjamask-spec.pdf @@ -2377,9 +2492,13 @@ REFERENCES: .. [Gr2007] \J. Green, Polynomial representations of `GL_n`, Springer Verlag, 2007. +.. [Graham1985] \J. Graham, + *Modular representations of Hecke algebras and related algebras*. + PhD thesis, University of Sydney, 1985. + .. [GriRei18] Darij Grinberg, Victor Reiner, *Hopf Algebras in Combinatorics*, - :arxiv:`1409.8356v5`. + :arxiv:`1409.8356v6`. .. [GR1989] \A. M. Garsia, C. Reutenauer. *A decomposition of Solomon's descent algebra.* Adv. Math. **77** (1989). @@ -2415,6 +2534,12 @@ REFERENCES: .. [GS1999] Venkatesan Guruswami and Madhu Sudan, Improved Decoding of Reed-Solomon Codes and Algebraic-Geometric Codes, 1999 +.. [GS2010] Daniel Gourion and Alberto Seeger. + Critical angles in polyhedral convex cones: numerical and + statistical considerations. + Mathematical Programming, 123:173-198, 2010. + :doi:`10.1007/s10107-009-0317-2`. + .. [Go1993] David M. Goldschmidt. *Group characters, symmetric functions, and the Hecke algebras*. AMS 1993. @@ -2611,6 +2736,10 @@ REFERENCES: .. [Hoc] Winfried Hochstaettler, "About the Tic-Tac-Toe Matroid", preprint. +.. [HJ18] Thorsten Holm and Peter Jorgensen + *A p-angulated generalisation of Conway and Coxeter's theorem on frieze patterns*, + International Mathematics Research Notices (2018) + .. [Hopcroft1973] J. E. Hopcroft and R. E. Tarjan. *Dividing a Graph into Triconnected Components*, SIAM J. Comput., 2(3), 135–158 @@ -2677,6 +2806,9 @@ REFERENCES: Operations", Annual ACM Symposium on Theory of Computing, Proceedings of the Fourth Annual ACM Symposium on Theory of Computing, pp. 108--118, 1972 +.. [HR2005] Tom Halverson and Arun Ram. *Partition algebras*. + Euro. J. Combin. **26** (2005) 869--921. + .. [HR2016] Clemens Heuberger and Roswitha Rissner, "Computing `J`-Ideals of a Matrix Over a Principal Ideal Domain", :arxiv:`1611.10308`, 2016. @@ -2704,6 +2836,12 @@ REFERENCES: Mathematische Zeitschrift 105(2): 110-113, 1968. :doi:`10.1007/BF01110435`. +.. [HS2010] Rene Henrion and Alberto Seeger. + Inradius and Circumradius of Various Convex Cones Arising in + Applications. + Set-Valued and Variational Analysis, 18(3-4):483-511, 2010. + :doi:`10.1007/s11228-010-0150-z`. + .. [HS2018] \B. Hutz, M. Stoll. "Smallest representatives of `SL(2,\ZZ)`-orbits of binary forms and endomorphisms of P1", :arxiv:`1805.08579`, 2018. @@ -2802,10 +2940,20 @@ REFERENCES: Science, 447, 74–84 (2012). :doi:`10.1016/j.tcs.2011.11.011` +.. [ILZ2018] \K. Iohara, G. Lehrer, and R. Zhang. + *Schur-Weyl duality for certain infinite dimensional* + `U_q(\mathfrak{sl}_2)`-*modules*. + Preprint, :arxiv:`1811.01325` (2018). + .. [IR1990] \K. Ireland and M. Rosen, *A Classical Introduction to Modern Number Theory*, Springer-Verlag, GTM volume 84, 1990. +.. [IS2005] Alfredo Iusem and Alberto Seeger. + On pairs of vectors achieving the maximal angle of a convex cone. + Mathematical Programming, 104(2-3):501-523, 2005. + :doi:`10.1007/s10107-005-0626-z`. + .. [ISSK2009] \M. Izadi, B. Sadeghiyan, S. S. Sadeghian, H. A. Khanooki, *MIBS: A new lightweight block cipher*; in CANS, (2009), pp. 334-348. @@ -2836,6 +2984,10 @@ REFERENCES: .. [Jet2008] \D. Jetchev. Global divisibility of Heegner points and Tamagawa numbers. Compos. Math. 144 (2008), no. 4, 811--826. +.. [Jeong2017] Juyoung Jeong. + Spectral sets and functions on Euclidean Jordan algebras. + University of Maryland, Baltimore County, Ph.D. thesis, 2017. + .. [JK1981] Gordon James, Adalbert Kerber, *The Representation Theory of the Symmetric Group*, Encyclopedia of Mathematics and its Applications, vol. 16, @@ -2972,6 +3124,18 @@ REFERENCES: subcodes of the second order binary Reed-Muller codes*. Information and Control, 18, pp. 369-394, 1971. +.. [Kas1966a] \T. Kasami: *Weight Distributions of + Bose-Chaudhuri-Hocquenghem Codes*. Coordinated Science + Laboratory, University of Illinois at Urbana-Champaign. + 1966 http://hdl.handle.net/2142/74459 + +.. [Kas1966b] \T. Kasami: *Weight Distribution Formula for Some Class + of Cyclic Codes*. Coordinated Science Laboratory, + University of Illinois at Urbana-Champaign. 1966 + +.. [Kas2018] András Kaszanyitzky. *The GraftalLace Cellular Automata*. + Preprint, :arxiv:`1805.11532`. + .. [Kat1991] Nicholas M. Katz, *Exponential sums and differential equations*, Princeton University Press, Princeton NJ, 1991. @@ -3133,6 +3297,9 @@ REFERENCES: .. [Knu1995] Donald E. Knuth, *Overlapping Pfaffians*, :arxiv:`math/9503234v1`. +.. [Knu2011] Donald E. Knuth, *The Art of Computer Programming. Volume 4A. + Combinatorial Algorithms, Part 1*. + .. [Knu2005] Lars R. Knudsen, *SMASH - A Cryptographic Hash Function*; in FSE'05, (2005), pp. 228-242. @@ -3179,13 +3346,16 @@ REFERENCES: http://portal.acm.org/citation.cfm?id=763203 and free of charge at :arxiv:`math/0106043` -.. [KP2011] Manuel Kauers and Peter Paule. The Concrete Tetrahedron. - Springer-Verlag, 2011. - .. [KP2002b] James Kuzmanovich; Andrey Pavlichenkov, *Finite Groups of Matrices Whose Entries Are Integers*, The American Mathematical Monthly, Vol. 109, No. 2. (2002) pp. 173-186 +.. [KP2011] Manuel Kauers and Peter Paule. The Concrete Tetrahedron. + Springer-Verlag, 2011. + +.. [KP2020] Lars Kastner and Marta Panizzut, *Hyperplane arrangements + in polymake*, arxiv:`2003.13548`. + .. [KPRWZ2010] \M. H. Klin, C. Pech, S. Reichard, A. Woldar, M. Zvi-Av, *Examples of computer experimentation in algebraic combinatorics*, ARS MATHEMATICA CONTEMPORANEA 3 (2010) 237–258. @@ -3197,6 +3367,9 @@ REFERENCES: Journal of Combinatorial Theory, Series A, **88** (1999), 66-92, http://www.sciencedirect.com/science/article/pii/0012365X9290368P +.. [Kra1999det] \C. Krattenthaler, *Advanced determinant calculus*, + Sém. Lothar. Combin. **42** (1999), Art. B42q, 67pp. + .. [Kra2006] Christian Krattenthaler. *Growth diagrams, and increasing and decreasing chains in fillings of Ferrers shapes*. Advances in Applied Mathematics Volume 37, @@ -3302,6 +3475,10 @@ REFERENCES: CMS Conf. Proc., **24**, Amer. Math. Soc., Providence, RI, 1998. :mathscinet:`MR1648638` +.. [KY2019] Jang Soo Kim, Meesue Yoo. + *Hook length property of d-complete posets via q-integrals*. + J. Combin. Theory Ser. A, **162** (2019), pp. 167-221. + .. [KZ2003] \M. Kontsevich, A. Zorich "Connected components of the moduli space of Abelian differentials with prescripebd singularities" Invent. math. 153, 631-678 (2003) @@ -3314,6 +3491,9 @@ REFERENCES: Mémoire de maîtrise en Mathématiques, Montréal, UQAM, 2008, 109 pages. +.. [Lak2010] Dan Laksov. *Splitting algebras and Gysin homomorphisms*. + Journal of Commutative Algebra, Volume 2, Number 3, Fall 2010 + .. [Lam1996] \T. K. Lam. *B and D analogues of stable Schubert polynomials and related insertion algorithms*. PhD Thesis, MIT, 1996. @@ -3597,6 +3777,9 @@ REFERENCES: groups*. Australian Mathematical Society Lecture Series, 2009. +.. [LT2012] Dan Laksov, Anders Thorup. *Splitting algebras and Schubert calculus*, + Indiana Univ. Math. J. 61 (2012), 1253-1312 :doi:`10.1512/iumj.2012.61.4791` + .. [DeLuca2006] \A. De Luca, *Pseudopalindrome closure operators in free monoids*, Theoret. Comput. Sci. 362 (2006) 282--300. @@ -3835,9 +4018,17 @@ REFERENCES: Systèmes dynamiques/Dynamical Systems. :arxiv:`math/0304469v1` +.. [MM1998] Gunter Malle and Andrew Mathas. + *Symmetric cyclotomic Hecke algebras* J. Algebra. + **205** (1998) pp. 275-293. + .. [MM2015] \J. Matherne and \G. Muller, *Computing upper cluster algebras*, Int. Math. Res. Not. IMRN, 2015, 3121-3149. +.. [Moh1988] \B. Mohar, *Isoperimetric inequalities, growth, and the spectrum + of graphs*, Linear Algebra and its Applications 103 (1988), + 119–131. + .. [MNO1994] Alexander Molev, Maxim Nazarov, and Grigori Olshanski. *Yangians and classical Lie algebras*. (1994) :arxiv:`hep-th/9409025` @@ -3904,6 +4095,10 @@ REFERENCES: .. [MS1977] \F. J. MacWilliams, N. J. A. Sloane, *The Theory of Error-Correcting Codes*, North-Holland, Amsterdam, 1977 +.. [MS1994] \P. Martin and H. Saleur. + *The blob algebra and the periodic Temperley-Lieb algebra*. + Lett. Math. Phys., **30** (1994), no. 3. pp. 189-206. + .. [MS2003] \T. Mulders, A. Storjohann, "On lattice reduction for polynomial matrices", J. Symbolic Comput. 35 (2003), no. 4, 377--401 @@ -3979,6 +4174,10 @@ REFERENCES: **N** +.. [NaiRow2011] Naidu and Rowell, A finiteness property for braided fusion + categories. Algebr. Represent. Theory 14 (2011), no. 5, 837–855. + :arXiv:`0903.4157`. + .. [Nas1950] John Nash. *Equilibrium points in n-person games.* Proceedings of the National Academy of Sciences 36.1 (1950): 48-49. @@ -4024,6 +4223,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*. @@ -4244,6 +4446,17 @@ REFERENCES: Providence, RI, 2013. :arxiv:`1112.6163` +.. [Proc1999] \R. A. Proctor, *Minuscule elements of Weyl groups, the numbers game, + and d-complete posets*. J. Algebra, 213(1):272–303, 1999. + +.. [PDynk1999] \R. A. Proctor. *Dynkin diagram classification of λ-minuscule + Bruhat lattices and of d-complete posets*. J. Algebraic Combin., + 9(1):61-94, 1999. + +.. [Proc2014] \R. A. Proctor. `d`-*complete posets generalize Young diagrams + for the hook product formula: Partial Presentation of Proof*. + RIMS Kôkyûroku, 1913:120-140, 2014. + .. [PR2003] Perrin-Riou, *Arithmétique des courbes elliptiques à réduction supersingulière en `p`*, Experiment. Math. 12 (2003), no. 2, 155-186. @@ -4465,6 +4678,13 @@ REFERENCES: .. [Rot2006] Ron Roth, Introduction to Coding Theory, Cambridge University Press, 2006 +.. [Row2006] Eric Rowell, *From quantum groups to unitary modular tensor categories*. + In Representations of algebraic groups, quantum groups, and Lie algebras, + Contemp. Math., **413**, Amer. Math. Soc., Providence, RI, 2006. + :arXiv:`math/0503226`. + +.. [RoStWa2009] Eric Rowell, Richard Stong and Zhenghan Wang, *On classification + of modular tensor categories*, Comm. Math. Phys. 292, 343--389, 2009. .. [RR1997] Arun Ram and Jeffrey Remmel. *Applications of the Frobenius formulas and the characters of the symmetric group and the @@ -4769,6 +4989,11 @@ REFERENCES: J. Combin. Theory Ser. A, **133** (2015) pp. 29-75. :arxiv:`1404.6539`. +.. [SS2016] Alberto Seeger and David Sossa. + Critical angles between two convex cones I. General theory. + TOP, 24(1):44-65, 2016. + :doi:`10.1007/s11750-015-0375-y`. + .. [SS2017] Ben Salisbury and Travis Scrimshaw. *Rigged configurations for all symmetrizable types*. Electron. J. Combin., **24(1)** (2017) #P1.30. :arxiv:`1509.07833`. @@ -4804,10 +5029,6 @@ REFERENCES: .. [St1986] Richard Stanley. *Two poset polytopes*, Discrete Comput. Geom. (1986), :doi:`10.1007/BF02187680` -.. [St2011b] \W. Stein, "Toward a Generalization of the Gross-Zagier - Conjecture", Int Math Res Notices (2011), - :doi:`10.1093/imrn/rnq075` - .. [Sta1973] \H. M. Stark, Class-Numbers of Complex Quadratic Fields. In: Kuijk W. (eds) Modular Functions of One Variable I. Lecture Notes in Mathematics, @@ -4843,6 +5064,15 @@ REFERENCES: .. [Star2011] Š. Starosta, *On Theta-palindromic Richness*, Theoret. Comp. Sci. 412 (2011) 1111--1121 +.. [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. + AMS Graduate Studies in Mathematics, Volume 79, 2007. + :doi:`10.1090/gsm/079` + .. [Sei2002] \T. R. Seifullin, *Computation of determinants, adjoint matrices, and characteristic polynomials without division* :doi:`10.1023/A:1021878507303` @@ -5077,6 +5307,9 @@ REFERENCES: height on elliptic curves over number fields, Math. Comp. 79 (2010), pages 2431-2449. +.. [Tho2011] Anders Thorup. *ON THE INVARIANTS OF THE SPLITTING ALGEBRA*, + 2011, :arxiv:`1105.4478` + .. [TIDES] \A. Abad, R. Barrio, F. Blesa, M. Rodriguez. TIDES tutorial: Integrating ODEs by using the Taylor Series Method (http://www.unizar.es/acz/05Publicaciones/Monografias/MonografiasPublicadas/Monografia36/IndMonogr36.htm) @@ -5267,6 +5500,10 @@ REFERENCES: .. [Wer1998] Annette Werner, Local heights on abelian varieties and rigid analytic uniformization, Doc. Math. 3 (1998), 301-319. +.. [Wes2017] Bruce Westbury. + *Coboundary categories and local rules*, + The Electronic Journal of Combinatorics, *25* (2018) + .. [WFYTP2008] \D. Watanable, S. Furuya, H. Yoshida, K. Takaragi, and B. Preneel, *A new keystream generator MUGI*; in FSE, (2002), pp. 179-194. @@ -5317,9 +5554,13 @@ REFERENCES: submitted to NIST, (2008), available at http://www3.ntu.edu.sg/home/wuhj/research/jh/jh_round3.pdf -.. [Wu2004] Wuthrich, C. (2004). On p-adic heights in families of - elliptic curves. Journal of the London Mathematical - Society, 70(1), 23-40. +.. [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. .. [WW1991] Michelle Wachs and Dennis White, *p, q-Stirling numbers and set partition statistics*, Journal of Combinatorial diff --git a/src/doc/en/reference/repl/index.rst b/src/doc/en/reference/repl/index.rst index e9881603361..572ec416246 100644 --- a/src/doc/en/reference/repl/index.rst +++ b/src/doc/en/reference/repl/index.rst @@ -79,7 +79,6 @@ Display Backend Infrastructure sage/repl/rich_output/backend_test sage/repl/rich_output/backend_doctest sage/repl/rich_output/backend_ipython - sage/repl/rich_output/backend_sagenb Miscellaneous ------------- @@ -92,7 +91,6 @@ Miscellaneous sage/repl/interface_magic sage/repl/ipython_kernel/interact sage/repl/ipython_kernel/widgets - sage/repl/ipython_kernel/widgets_sagenb sage/repl/ipython_kernel/install sage/repl/ipython_kernel/kernel sage/repl/ipython_tests diff --git a/src/doc/en/reference/repl/options.rst b/src/doc/en/reference/repl/options.rst index 5a53b611067..715a6816608 100644 --- a/src/doc/en/reference/repl/options.rst +++ b/src/doc/en/reference/repl/options.rst @@ -9,216 +9,4 @@ Installation Guide for information about making sure your Command-line options for Sage ----------------------------- -.. rubric:: Running Sage, the most common options - -- ``file.[sage|py|spyx]`` -- run the given .sage, .py or .spyx - files (as in ``sage my_file.sage``) -- ``-h``, ``-?``, ``--help`` -- print a short help message -- ``-v``, ``--version`` -- print the Sage version -- ``--advanced`` -- print (essentially this) list of Sage options -- ``-c cmd`` -- evaluate ``cmd`` as sage code. For example, ``sage - -c 'print(factor(35))'`` will print "5 * 7". - -.. rubric:: Running Sage, other options - -- ``--preparse file.sage`` -- preparse ``file.sage``, a file of - Sage code, and produce the corresponding Python file - ``file.sage.py``. See the Sage tutorial for more about preparsing - and the differences between Sage and Python. -- ``-q`` -- quiet; start with no banner -- ``--grep [options] `` -- grep through all the Sage library - code for ``string``. Any options will get passed to the "grep" - command; for example, ``sage --grep -i epstein`` will search for - ``epstein``, and the ``-i`` flag tells grep to ignore case when - searching. Note that while running Sage, you can also use the - function :func:`search_src ` to - accomplish the same thing. -- ``--grepdoc [options] `` -- grep through all the Sage - documentation for ``string``. Note that while running Sage, you can - also use the function :func:`search_doc - ` to accomplish the same thing. -- ``--min [...]`` -- do not populate global namespace (must be first - option) -- ``-gthread``, ``-qthread``, ``-q4thread``, ``-wthread``, - ``-pylab`` -- pass the option through to IPython -- ``--nodotsage`` -- run Sage without using the user's - :file:`.sage` directory: create and use a temporary :file:`.sage` - directory instead. Warning: notebooks are stored in the - :file:`.sage` directory, so any notebooks created while running with - ``--nodotsage`` will be temporary also. - -.. rubric:: Running the notebook - -- ``-n [...]``, ``--notebook=[...]`` -- start the notebook, valid options - are ``default``, ``sagenb``, ``jupyter`` and ``export`` (see the - output of ``sage --notebook --help`` for more details and examples of - how to pass optional arguments) -- ``-bn [...]``, ``--build-and-notebook [...]`` -- build the Sage - library (as by running ``sage -b``) then start the Sage notebook -- ``--inotebook [...]`` -- start the *insecure* Sage notebook - -.. rubric:: Running external programs and utilities - -- ``--cython [...]`` -- run Cython with the given arguments -- ``--ecl [...]``, ``--lisp [...]`` -- run Sage's copy of ECL - (Embeddable Common Lisp) with the given arguments -- ``--gap [...]`` -- run Sage's Gap with the given arguments -- ``--git [...]`` -- run Sage's Git with the given arguments -- ``--gp [...]`` -- run Sage's PARI/GP calculator with the given arguments -- ``--ipython [...]`` -- run Sage's IPython using the default - environment (not Sage), passing additional options to IPython -- ``--kash [...]`` -- run Sage's Kash with the given arguments -- ``--M2 [...]`` -- run Sage's Macaulay2 with the given arguments -- ``--maxima [...]`` -- run Sage's Maxima with the given arguments -- ``--mwrank [...]`` -- run Sage's mwrank with the given arguments -- ``--python [...]``, ``--python2 [...]`` -- run the Python 2 interpreter -- ``--python3 [...]`` -- run the Python 3 interpreter -- ``-R [...]`` -- run Sage's R with the given arguments -- ``--scons [...]`` -- run Sage's scons -- ``--singular [...]`` -- run Sage's singular with the given arguments -- ``--twistd [...]`` -- run Twisted server -- ``--sh [...]`` -- run a shell with Sage environment variables set -- ``--gdb`` -- run Sage under the control of gdb -- ``--gdb-ipython`` -- run Sage's IPython under the control of gdb -- ``--cleaner`` -- run the Sage cleaner. This cleans up after Sage, - removing temporary directories and spawned processes. (This gets - run by Sage automatically, so it is usually not necessary to run - it separately.) - -.. rubric:: Installing packages and upgrading - -- ``-i [options] [packages]`` -- install the given Sage packages (unless - they are already installed); if no packages are given, print - a list of all installed packages. Options: - - - ``-c`` -- run the packages' test suites, overriding the settings of - :envvar:`SAGE_CHECK` and :envvar:`SAGE_CHECK_PACKAGES`. - - ``-f`` -- force build: install the packages even if they are - already installed. - - ``-s`` -- do not delete the ``spkg/build`` directories after a - successful build -- useful for debugging. - -- ``-f [options] [packages]`` -- shortcut for ``-i -f``: force build of - the given Sage packages. -- ``--info [packages]`` -- display the ``SPKG.txt`` file of the given - Sage packages. -- ``--standard`` -- list all standard packages that can be installed -- ``--optional`` -- list all optional packages that can be installed -- ``--experimental`` -- list all experimental packages that can be installed -- ``--upgrade [url]`` -- download, build and install standard - packages from given url. If url not given, automatically selects - a suitable mirror. If url='ask', it lets you select the mirror. - -.. rubric:: Building and testing the Sage library - -- ``--root`` -- print the Sage root directory -- ``-b`` -- build Sage library -- do this if you have modified - any source code files in :file:`$SAGE_ROOT/src/sage/`. -- ``-ba`` -- same as ``-b``, but rebuild *all* Cython - code. This could take a while, so you will be asked if you want - to proceed. -- ``-ba-force`` -- same as ``-ba``, but don't query before rebuilding -- ``--br`` -- build and run Sage -- ``-t [options] `` -- test examples in .py, .pyx, .sage - or .tex files. Options: - - - ``--long`` -- include lines with the phrase 'long time' - - ``--verbose`` -- print debugging output during the test - - ``--optional`` -- also test all examples labeled ``# optional`` - - ``--only-optional[=tags]`` -- if no ``tags`` are specified, only - run blocks of tests containing a line labeled ``# optional``. If - a comma separated list of tags is specified, only run blocks containing - a line labeled ``# optional tag`` for any of the tags given and in these blocks only - run the lines which are unlabeled or labeled ``#optional`` or labeled - ``#optional tag`` for any of the tags given. - - ``--randorder[=seed]`` -- randomize order of tests - - ``--short[=seconds]`` -- run as many doctests as possible in about 300 - seconds (or the number of seconds given.) This runs the tests for each - module from the top of the file and skips tests once it exceeds the budget - allocated for that file. - -- ``-tnew [...]`` -- like ``-t`` above, but only tests files - modified since last commit -- ``-tp [...]`` -- like ``-t`` above, but tests in parallel - using ``N`` threads with 0 interpreted as ``minimum(8, cpu_count())`` -- ``--testall [options]`` -- test all source files, docs, and - examples; options are the same as for ``-t``. -- ``-bt [...]`` -- build and test, options like ``-t`` above -- ``-btp [...]`` -- build and test in parallel, options like - ``-tp`` above -- ``-btnew [...]`` -- build and test modified files, options like ``-tnew`` -- ``--fixdoctests file.py [output_file] [--long]`` -- writes a new - version of ``file.py`` to ``output_file`` (default: ``file.py.out``) - that will pass the doctests. With the optional ``--long`` argument - the long time tests are also checked. A patch for the new file is - printed to stdout. -- ``--startuptime [module]`` -- display how long each component of Sage takes - to start up. Optionally specify a module (e.g., "sage.rings.qqbar") to get - more details about that particular module. -- ``--coverage `` -- give information about doctest coverage - of files -- ``--coverageall`` -- give summary info about doctest coverage of - all files in the Sage library - -.. rubric:: Documentation - -- ``--docbuild [options] document (format | command)`` -- build or - return information about the Sage documentation. - - - ``document`` -- name of the document to build - - ``format`` -- document output format - - ``command`` -- document-specific command - - A ``document`` and either a ``format`` or a ``command`` are required, unless a - list of one or more of these is requested. - - Options: - - - ``help``, ``-h``, ``--help`` -- print a help message - - ``-H``, ``--help-all`` -- print an extended help message, - including the output from the options ``-h``, ``-D``, ``-F``, - ``-C all``, and a short list of examples. - - ``-D``, ``--documents`` -- list all available documents - - ``-F``, ``--formats`` -- list all output formats - - ``-C DOC``, ``--commands=DOC`` -- list all commands for document - ``DOC``; use ``-C all`` to list all - - ``-i``, ``--inherited`` -- include inherited members in - reference manual; may be slow, may fail for PDF output - - ``-u``, ``--underscore`` -- include variables prefixed with - ``_`` in reference manual; may be slow, may fail for PDF output - - ``-j``, ``--jsmath`` -- render math using jsMath; formats: - ``html``, ``json``, ``pickle``, ``web`` - - ``--no-pdf-links`` -- do not include PDF links in document - ``website``; formats: ``html``, ``json``, ``pickle``, ``web`` - - ``--check-nested`` -- check picklability of nested classes in - document ``reference`` - - ``-N``, ``--no-colors`` -- do not color output; does not affect - children - - ``-q``, ``--quiet`` -- work quietly; same as ``--verbose=0`` - - ``-v LEVEL``, ``--verbose=LEVEL`` -- report progress at level 0 - (quiet), 1 (normal), 2 (info), or 3 (debug); does not affect - children - - Advanced -- use these options with care: - - - ``-S OPTS``, ``--sphinx-opts=OPTS`` -- pass comma-separated ``OPTS`` - to sphinx-build - - ``-U``, ``--update-mtimes`` -- before building reference manual, - update modification times for auto-generated ReST files - -.. rubric:: Making Sage packages or distributions - -- ``--pkg dir`` -- create the Sage package ``dir.spkg`` from the - directory ``dir`` -- ``--pkg_nc dir`` -- as ``--pkg``, but do not compress the package -- ``--merge`` -- run Sage's automatic merge and test script -- ``--sdist`` -- build a source distribution of Sage - -.. rubric:: Valgrind memory debugging - -- ``--cachegrind`` -- run Sage using Valgrind's cachegrind tool -- ``--callgrind`` -- run Sage using Valgrind's callgrind tool -- ``--massif`` -- run Sage using Valgrind's massif tool -- ``--memcheck`` -- run Sage using Valgrind's memcheck tool -- ``--omega`` -- run Sage using Valgrind's omega tool -- ``--valgrind`` -- this is an alias for ``--memcheck`` +.. literalinclude:: options.txt diff --git a/src/doc/en/reference/repl/startup.rst b/src/doc/en/reference/repl/startup.rst index 279eb50cf25..44ce958d95f 100644 --- a/src/doc/en/reference/repl/startup.rst +++ b/src/doc/en/reference/repl/startup.rst @@ -17,7 +17,7 @@ This script is sourced not only when running Sage itself, but also when running any of the subcommands (like ``sage --python``, ``sage -b`` or ``sage -i ``). In particular, setting ``PS1`` here overrides the default prompt for -the Sage shell ``sage --sh``. +the Sage shells ``sage --buildsh`` and ``sage --sh``. .. note:: diff --git a/src/doc/en/reference/schemes/index.rst b/src/doc/en/reference/schemes/index.rst index 18545b20b04..a672067914d 100644 --- a/src/doc/en/reference/schemes/index.rst +++ b/src/doc/en/reference/schemes/index.rst @@ -2,9 +2,9 @@ Schemes ======= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 - sage/schemes/readme + sage/schemes/overview sage/schemes/generic/scheme sage/schemes/generic/spec @@ -24,7 +24,7 @@ Affine Schemes -------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/affine/affine_space sage/schemes/affine/affine_morphism @@ -37,7 +37,7 @@ Projective Schemes ------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/projective/projective_space sage/schemes/projective/projective_morphism @@ -50,7 +50,7 @@ Products of Projective Spaces ----------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/product_projective/space sage/schemes/product_projective/homset @@ -62,7 +62,7 @@ Products of Projective Spaces Toric Varieties --------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/toric/variety sage/schemes/toric/fano_variety @@ -86,8 +86,7 @@ Toric Varieties Cyclic Covers --------------- .. toctree:: - :maxdepth: 2 - + :maxdepth: 1 sage/schemes/cyclic_covers/cycliccover_finite_field sage/schemes/cyclic_covers/cycliccover_generic diff --git a/src/doc/en/reference/spkg/conf.py b/src/doc/en/reference/spkg/conf.py new file mode 120000 index 00000000000..2bdf7e68470 --- /dev/null +++ b/src/doc/en/reference/spkg/conf.py @@ -0,0 +1 @@ +../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 6125b9ecb1f..2b0273da06e 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -299,14 +299,12 @@ considerations: etc. **We do not override the default double underscore __add__, __mul__**, since otherwise, we could not use Sage's coercion model. -- Comparisons can be implemented using ``_richcmp_`` or - ``_cmp_``. This automatically makes the relational operators like - ``==`` and ``<`` work. **Beware**: in these methods, calling the - Python2-only ``cmp`` function should be avoided for compatibility - with Python3. You can use instead the ``richcmp`` function provided - by sage. - - Note that either ``_cmp_`` or ``_richcmp_`` should be provided, +- Comparisons can be implemented using ``_richcmp_``. + This automatically makes the relational operators like + ``==`` and ``<`` work. Inside this method, you can use + the ``richcmp`` functions and related tools provided by sage. + + Note that ``_richcmp_`` should be provided, since otherwise comparison does not work:: sage: class Foo(sage.structure.element.Element): @@ -319,7 +317,7 @@ considerations: sage: a <= b Traceback (most recent call last): ... - NotImplementedError: comparison not implemented for + TypeError: '<=' not supported between instances of 'Foo' and 'Foo' - In the single underscore methods, we can assume that *both arguments belong to the same parent*. @@ -447,10 +445,10 @@ Sage's category framework can differentiate the two cases:: And indeed, ``MS2`` has *more* methods than ``MS1``:: sage: import inspect - sage: len([s for s in dir(MS1) if inspect.ismethod(getattr(MS1,s,None))]) - 81 - sage: len([s for s in dir(MS2) if inspect.ismethod(getattr(MS2,s,None))]) - 120 + sage: L1 = len([s for s in dir(MS1) if inspect.ismethod(getattr(MS1,s,None))]) + sage: L2 = len([s for s in dir(MS2) if inspect.ismethod(getattr(MS2,s,None))]) + sage: L1 < L2 + True This is because the class of ``MS2`` also inherits from the parent class for algebras:: diff --git a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/introduction.rst b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/introduction.rst index 28157494b86..7d2d82c17c8 100644 --- a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/introduction.rst +++ b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/introduction.rst @@ -34,8 +34,7 @@ Using Sage ---------- To use Sage, install it on your computer, and use either the command -line or start the Sage notebook by typing ``notebook()`` at the -command line. +line or use the notebook by starting Sage as ``sage -n``. We show Sage sessions as follows:: diff --git a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst index b8847cf06cc..9b48b106888 100644 --- a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst +++ b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst @@ -70,7 +70,10 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.dim` | the dimension of the polytope :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.dimension` | alias of dim :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** @@ -97,7 +100,7 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.intersection` | intersection of two polyhedra :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.join` | join of two polyhedra :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.convex_hull` | convex hull of the union of two polyhedra - :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.affine_hull` | constructs an affinely equivalent full dimensional polyhedra + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.affine_hull_projection` | constructs an affinely equivalent full-dimensional polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.barycentric_subdivision` | constructs a geometric realization of the barycentric subdivision :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.dilation` | scalar dilation :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.face_truncation` | truncates a specific face @@ -130,6 +133,7 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.vertex_facet_graph` | bipartite digraph given vertex-facet adjacency :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.adjacency_matrix` | adjacency matrix :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.incidence_matrix` | incidence matrix + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.slack_matrix` | slack matrix :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.facet_adjacency_matrix` | adjacency matrix of the facets :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.vertex_adjacency_matrix` | adjacency matrix of the vertices @@ -140,7 +144,10 @@ List of Polyhedron methods :widths: 30, 70 :delim: | - :meth:`~sage.geometry.polyhedron.base_ZZ.Polyhedron_ZZ.ehrhart_polynomial` | the Ehrhart polynomial (only for :class:`Polyhedron over ZZ `) + :meth:`~sage.geometry.polyhedron.base_ZZ.Polyhedron_ZZ.ehrhart_polynomial` | the Ehrhart polynomial for :class:`Polyhedron over ZZ ` + :meth:`~sage.geometry.polyhedron.base_QQ.Polyhedron_QQ.ehrhart_polynomial` | the Ehrhart polynomial for :class:`Polyhedron over QQ ` + :meth:`~sage.geometry.polyhedron.base_QQ.Polyhedron_QQ.ehrhart_quasipolynomial` | the Ehrhart quasipolynomial for :class:`Polyhedron over QQ ` + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.h_star_vector` | the `h^*`-vector for polytopes with integral vertices :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.integral_points` | list of integral points :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.integral_points_count` | number of integral points :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.get_integral_point` | get the i-th integral point without computing all interior lattice points @@ -157,7 +164,9 @@ 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 :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.face_generator` | a generator over the faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.faces` | the list of faces diff --git a/src/doc/en/thematic_tutorials/geometry/polyhedra_tutorial.rst b/src/doc/en/thematic_tutorials/geometry/polyhedra_tutorial.rst index 6ff4a38e0fa..bf6ff21766b 100644 --- a/src/doc/en/thematic_tutorials/geometry/polyhedra_tutorial.rst +++ b/src/doc/en/thematic_tutorials/geometry/polyhedra_tutorial.rst @@ -166,6 +166,13 @@ The following example demonstrates the limitations of :code:`RDF`. sage: D A 3-dimensional polyhedron in (Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^3 defined as the convex hull of 20 vertices sage: D_RDF = Polyhedron(vertices = [n(v.vector(),digits=6) for v in D.vertices()], base_ring=RDF) + doctest:warning + ... + UserWarning: This polyhedron data is numerically complicated; cdd + could not convert between the inexact V and H representation + without loss of data. The resulting object might show + inconsistencies. + sage: D_RDF = Polyhedron(vertices = sorted([n(v.vector(),digits=6) for v in D.vertices()]), base_ring=RDF) Traceback (most recent call last): ... ValueError: *Error: Numerical inconsistency is found. Use the GMP exact arithmetic. diff --git a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst index 6f0a36c980c..f5d45485680 100644 --- a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst +++ b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst @@ -21,18 +21,16 @@ tutorial shows how it all works. Instructions """""""""""" -To put an image of a 3d-polytope in LaTeX using TikZ and Sage, simply follow the instructions: +To put an image of a 3D-polytope in LaTeX using TikZ and Sage, simply follow the instructions: - Install `SageTex `_ (optional but recommended!) - Put ``\usepackage{tikz}`` in the preamble of your article - Open Sage and change the directory to your article's by the command ``cd /path/to/article`` - Input your polytope, called P for example, to Sage - Visualize the polytope P using the command ``P.show(aspect_ratio=1)`` -- This will open an interactive viewer named Jmol, in which you can rotate the polytope. Once the wished view angle is found, right click on the image and select *Console* -- In the dialog box click the button *State* -- Scroll up to the line starting with *moveto* -- It reads something like ``moveto 0.0 {x y z angle} scale`` -- Go back to Sage and type ``Img = P.projection().tikz([x,y,z],angle)`` +- This will open an interactive view in your default browser, where you can rotate the polytope. +- Once the desired view angle is found, click on the information icon in the lower right-hand corner and select *Get Viewpoint*. This will copy a string of the form '[x,y,z],angle' to your local clipboard. +- Go back to Sage and type ``Img = P.projection().tikz([x,y,z],angle)``. You can paste the string here to save some typing. - *Img* now contains a Sage object of type ``LatexExpr`` containing the raw TikZ picture of your polytope Then, you can either copy-paste it to your article by typing ``Img`` in Sage or save it to a file, by doing diff --git a/src/doc/en/thematic_tutorials/geometry/tips.rst b/src/doc/en/thematic_tutorials/geometry/tips.rst index a484b5d2f72..9e2de1b16b6 100644 --- a/src/doc/en/thematic_tutorials/geometry/tips.rst +++ b/src/doc/en/thematic_tutorials/geometry/tips.rst @@ -43,13 +43,13 @@ object, you can! sage: Cube = polytopes.cube() sage: TCube = Cube.truncation().dilation(1/2) sage: sage_input(TCube) - Polyhedron(backend='ppl', base_ring=QQ, vertices=[(-1/2, -1/2, -1/6), - (-1/2, -1/2, 1/6), (-1/2, -1/6, -1/2), (-1/2, -1/6, 1/2), (-1/2, 1/6, - -1/2), (-1/2, 1/6, 1/2), (-1/2, 1/2, -1/6), (-1/2, 1/2, 1/6), (-1/6, -1/2, - -1/2), (-1/6, -1/2, 1/2), (-1/6, 1/2, -1/2), (-1/6, 1/2, 1/2), (1/6, -1/2, - -1/2), (1/6, -1/2, 1/2), (1/6, 1/2, -1/2), (1/6, 1/2, 1/2), (1/2, -1/2, - -1/6), (1/2, -1/2, 1/6), (1/2, -1/6, -1/2), (1/2, -1/6, 1/2), (1/2, 1/6, - -1/2), (1/2, 1/6, 1/2), (1/2, 1/2, -1/6), (1/2, 1/2, 1/6)]) + Polyhedron(backend='ppl', base_ring=QQ, vertices=[(1/6, -1/2, -1/2), + (1/2, -1/6, -1/2), (1/2, 1/6, -1/2), (1/2, 1/2, -1/6), (1/2, 1/2, 1/6), + (1/2, 1/6, 1/2), (1/6, 1/2, 1/2), (1/2, -1/6, 1/2), (1/6, 1/2, -1/2), + (1/6, -1/2, 1/2), (1/2, -1/2, 1/6), (1/2, -1/2, -1/6), (-1/2, 1/6, -1/2), + (-1/2, -1/2, 1/6), (-1/2, 1/6, 1/2), (-1/2, 1/2, 1/6), (-1/6, 1/2, 1/2), + (-1/2, 1/2, -1/6), (-1/6, 1/2, -1/2), (-1/2, -1/6, 1/2), (-1/6, -1/2, 1/2), + (-1/2, -1/2, -1/6), (-1/6, -1/2, -1/2), (-1/2, -1/6, -1/2)]) .. end of output @@ -64,37 +64,37 @@ the latex presentation, there is a method for that! sage: Nice_repr = TCube.Hrepresentation_str() sage: print(Nice_repr) + -6*x0 - 6*x1 - 6*x2 >= -7 + -6*x0 - 6*x1 + 6*x2 >= -7 + -6*x0 + 6*x1 - 6*x2 >= -7 + -6*x0 + 6*x1 + 6*x2 >= -7 -2*x0 >= -1 -2*x1 >= -1 - -6*x0 + 6*x1 - 6*x2 >= -7 - 2*x0 >= -1 + -2*x2 >= -1 + 6*x0 + 6*x1 + 6*x2 >= -7 + 2*x2 >= -1 2*x1 >= -1 + 2*x0 >= -1 6*x0 - 6*x1 - 6*x2 >= -7 - 6*x0 + 6*x1 - 6*x2 >= -7 6*x0 - 6*x1 + 6*x2 >= -7 - 6*x0 + 6*x1 + 6*x2 >= -7 - 2*x2 >= -1 - -2*x2 >= -1 - -6*x0 + 6*x1 + 6*x2 >= -7 - -6*x0 - 6*x1 + 6*x2 >= -7 - -6*x0 - 6*x1 - 6*x2 >= -7 + 6*x0 + 6*x1 - 6*x2 >= -7 sage: print(TCube.Hrepresentation_str(latex=True)) \begin{array}{rcl} + -6 \, x_{0} - 6 \, x_{1} - 6 \, x_{2} & \geq & -7 \\ + -6 \, x_{0} - 6 \, x_{1} + 6 \, x_{2} & \geq & -7 \\ + -6 \, x_{0} + 6 \, x_{1} - 6 \, x_{2} & \geq & -7 \\ + -6 \, x_{0} + 6 \, x_{1} + 6 \, x_{2} & \geq & -7 \\ -2 \, x_{0} & \geq & -1 \\ -2 \, x_{1} & \geq & -1 \\ - -6 \, x_{0} + 6 \, x_{1} - 6 \, x_{2} & \geq & -7 \\ - 2 \, x_{0} & \geq & -1 \\ + -2 \, x_{2} & \geq & -1 \\ + 6 \, x_{0} + 6 \, x_{1} + 6 \, x_{2} & \geq & -7 \\ + 2 \, x_{2} & \geq & -1 \\ 2 \, x_{1} & \geq & -1 \\ + 2 \, x_{0} & \geq & -1 \\ 6 \, x_{0} - 6 \, x_{1} - 6 \, x_{2} & \geq & -7 \\ - 6 \, x_{0} + 6 \, x_{1} - 6 \, x_{2} & \geq & -7 \\ 6 \, x_{0} - 6 \, x_{1} + 6 \, x_{2} & \geq & -7 \\ - 6 \, x_{0} + 6 \, x_{1} + 6 \, x_{2} & \geq & -7 \\ - 2 \, x_{2} & \geq & -1 \\ - -2 \, x_{2} & \geq & -1 \\ - -6 \, x_{0} + 6 \, x_{1} + 6 \, x_{2} & \geq & -7 \\ - -6 \, x_{0} - 6 \, x_{1} + 6 \, x_{2} & \geq & -7 \\ - -6 \, x_{0} - 6 \, x_{1} - 6 \, x_{2} & \geq & -7 + 6 \, x_{0} + 6 \, x_{1} - 6 \, x_{2} & \geq & -7 \end{array} sage: Latex_repr = LatexExpr(TCube.Hrepresentation_str(latex=True)) diff --git a/src/doc/en/thematic_tutorials/index.rst b/src/doc/en/thematic_tutorials/index.rst index 6041c18f5af..01ae01f4f1d 100644 --- a/src/doc/en/thematic_tutorials/index.rst +++ b/src/doc/en/thematic_tutorials/index.rst @@ -30,9 +30,7 @@ This documentation is licensed under a `Creative Commons Attribution-Share Alike Introduction to Sage -------------------- -* `Logging on to a Sage Server and Creating a Worksheet (PREP) <../prep/Logging-On.html>`_ * `Introductory Sage Tutorial (PREP) <../prep/Intro-Tutorial.html>`_ -* :ref:`tutorial-notebook-and-help-long` * `Sage's main tutorial <../tutorial/>`_ .. _programming_design: @@ -121,8 +119,6 @@ Advanced Programming Documentation ============= -* :ref:`sws2srt` - .. Sage development .. ---------------- diff --git a/src/doc/en/thematic_tutorials/sandpile.rst b/src/doc/en/thematic_tutorials/sandpile.rst index 7ee8f86d080..8339a314f3a 100644 --- a/src/doc/en/thematic_tutorials/sandpile.rst +++ b/src/doc/en/thematic_tutorials/sandpile.rst @@ -3995,6 +3995,7 @@ OUTPUT: SandpileDivisor EXAMPLES:: + sage: S = sandpiles.Cycle(3) sage: D = SandpileDivisor(S, [1,2,3]) sage: D.dualize() diff --git a/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst b/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst index 13f1a83a2b6..4726bce061a 100644 --- a/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst +++ b/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst @@ -721,8 +721,6 @@ derive from the one that follows. .. CODE-BLOCK:: python - :class:`sage.coding.repetition_code.BinaryRepetitionCode ` - #the line above creates a link to the class in the html documentation of coding theory library from sage.coding.repetition_code import BinaryRepetitionCode ``encoders_catalog.py`` (continued): diff --git a/src/doc/en/thematic_tutorials/sws2rst.rst b/src/doc/en/thematic_tutorials/sws2rst.rst deleted file mode 100644 index 1a6552b2518..00000000000 --- a/src/doc/en/thematic_tutorials/sws2rst.rst +++ /dev/null @@ -1,99 +0,0 @@ -.. _sws2srt: - -==================================== -Creating a Tutorial from a Worksheet -==================================== - -Sage has a number of `thematic tutorials `_ and contains everything -needed to turn a worksheet created in the `Sage notebook -`_ (sagenb) into a tutorial. - -.. WARNING:: - - The following will only work if Sage is built using Python 2 rather - than Python 3. As of version 9.0, the default is to build Sage with - Python 3. So either use an older version of Sage, or build a new - version of Sage with Python 2 by obtaining a Sage tarball and doing - - .. CODE-BLOCK:: shell-session - - $ make configure - $ ./configure --with-python=2 - $ make - -* Once you have created a worksheet and are satisfied with the text and - computations, download it to a directory. - -We will assume here that the worksheet is called ``Tutorial.sws`` -and the directory is called ``make_tutorial``. We also assume that -``sage`` is your Sage command; if it is not in your ``PATH`` then replace -this with the path to your Sage installation, such as -``/Applications/Sage-6.2.app/Contents/Resources/sage/sage`` if you are -using the Mac app and have placed it in your Applications directory. - -* Next, you will need an optional package to parse your worksheet. Use the - command: - - .. CODE-BLOCK:: shell-session - - $ sage --pip install beautifulsoup4 - - to install it (or, in the Mac app, use the ``Terminal Session`` advanced - menu with ``--pip install beautifulsoup4``). - -* Then we will use the ``sws2rst`` script to turn the worksheet into - a document in the `ReStructuredText `_ - format. Be sure you are in the same directory as the worksheet: - - .. CODE-BLOCK:: shell-session - - $ sage --sws2rst Tutorial.sws - - This will create an ``.rst`` file along with a subdirectory of image - files (which may be empty if there are no images). - - You can find help for ``sws2rst`` with the command - ``sage --sws2rst -h`` once you have installed beautifulsoup4. - -* In principle, such a file could be added directly to Sage's documentation (see - the `developer's manual <../developer/index.html>`_). However, you probably - want to check whether it looks right first. So next we will compile this file - to html documentation. - - * Follow the instructions of ``sage --sws2rst --sphinxify``. First, - we will open a Sage shell session, where all appropriate Sage - references already work properly: - - .. CODE-BLOCK:: shell-session - - $ sage --sh - - From here, you should be able to just type: - - .. CODE-BLOCK:: shell-session - - $ sphinx-quickstart - - and then respond to prompts for turning your ``.rst`` file into - documentation. For most of them you can just hit enter/return to - accept the defaults. However, you will probably want to - - * Enter a name for the project - * Enter a name for you - * Type ``y`` for the question about using MathJax - - Keep note of the instructions; the main other thing to do is add - your file's name to ``index.rst``, and then just do: - - .. CODE-BLOCK:: shell-session - - $ make html - - and wait while magic happens. To see the results, open the file - ``make_tutorial/_build/html/Tutorial.html`` with a browser, or - use your graphical file system to navigate to the same place. - -* Now you can modify the ``.rst`` file more and repeat the steps - of compiling it until it is ready for inclusion, or just for distribution - among other Sage users as an HTML file. (Do ``make pdf`` for a PDF - version.) diff --git a/src/doc/en/thematic_tutorials/toctree.rst b/src/doc/en/thematic_tutorials/toctree.rst index d5b272291a9..18d35b70fb1 100644 --- a/src/doc/en/thematic_tutorials/toctree.rst +++ b/src/doc/en/thematic_tutorials/toctree.rst @@ -6,7 +6,6 @@ Thematic tutorial document tree :maxdepth: 2 algebraic_combinatorics - tutorial-notebook-and-help-long sandpile group_theory lie @@ -25,5 +24,4 @@ Thematic tutorial document tree cython_interface numerical_sage/index explicit_methods_in_number_theory/index - sws2rst profiling diff --git a/src/doc/en/thematic_tutorials/tutorial-comprehensions.rst b/src/doc/en/thematic_tutorials/tutorial-comprehensions.rst index f2168ba287c..d7dc8a9b4f0 100644 --- a/src/doc/en/thematic_tutorials/tutorial-comprehensions.rst +++ b/src/doc/en/thematic_tutorials/tutorial-comprehensions.rst @@ -236,15 +236,12 @@ not Sage integers. The behaviour of the functions :func:`map` and :func:`filter` has changed between Python 2 and Python 3. In Python 3, they return an -iterator. If you want to use this new behaviour in Python 2, and keep -your code compatible with Python3, you can use the compatibility -library ``six`` as follows:: +iterator. If you want to return a list like in Python 2 you need to explicitly +wrap them in :func:`list`:: - sage: from six.moves import map sage: list(map(lambda z: z.cycle_type(), Permutations(3))) [[1, 1, 1], [2, 1], [2, 1], [3], [3], [2, 1]] - sage: from six.moves import filter sage: list(filter(lambda z: z.has_pattern([1,2]), Permutations(3))) [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2]] diff --git a/src/doc/en/thematic_tutorials/tutorial-notebook-and-help-long.rst b/src/doc/en/thematic_tutorials/tutorial-notebook-and-help-long.rst deleted file mode 100644 index 54e3c162248..00000000000 --- a/src/doc/en/thematic_tutorials/tutorial-notebook-and-help-long.rst +++ /dev/null @@ -1,425 +0,0 @@ -.. _tutorial-notebook-and-help-long: - -============================================================================== -Tutorial: Using the Sage notebook, navigating the help system, first exercises -============================================================================== - -.. linkall - -This worksheet is based on William Stein's `JPL09__intro_to_sage.sws -`_ -worksheet and the `Sage days 20.5_demo `_ -worksheet and aims to be an interactive introduction to Sage through exercises. -You will learn how to use the notebook and call the help. - -Making this help page into a worksheet -====================================== - -If you are browsing this document as a static web page, you can see all the -examples; however you need to copy-paste them one by one to experiment with them. -Use the ``Upload worksheet`` button of the notebook and copy-paste the URL of -this page to obtain an editable copy in your notebook. - -If you are browsing this document as part of Sage's live documentation, you can -play with the examples directly here; however your changes will be lost when -you close this page. Use ``Copy worksheet`` from the ``File...`` menu at the -top of this page to get an editable copy in your notebook. - -Both in the live tutorial and in the notebook, you can clear all output by -selecting ``Delete All Output`` from the ``Action...`` menu next to the -``File...`` menu at the top of the worksheet. - -Entering, Editing and Evaluating Input -====================================== - -To *evaluate code* in the Sage Notebook, type the code into an input cell and -press ``shift-enter`` or click the ``evaluate`` link. Try it now with a simple -expression (e.g., `2+3`). The first time you evaluate a cell takes longer -than subsequent times since a new Sage process is started:: - - sage: 2 + 3 - 5 - - sage: # edit here - - sage: # edit here - -To create *new input cells*, click the blue line that appears between -cells when you move your mouse around. Try it now:: - - sage: 1 + 1 - 2 - - sage: # edit here - -You can *go back* and edit any cell by clicking in it (or using the -arrow keys on your keyboard to move up or down). Go back and change -your `2+3` above to `3+3` and re-evaluate it. An empty cell can be -*deleted* with backspace. - -You can also *edit this text* right here by double clicking on it, -which will bring up the TinyMCE Javascript text editor. You can even -put embedded mathematics like this $\sin(x) - y^3$ by using dollar signs -just like in TeX or LaTeX. - -Help systems -============ - -There are various ways of getting help in Sage. - -- navigate through the documentation (there is a link ``Help`` at the top right - of the worksheet), -- ``tab`` completion, -- contextual help. - -We detail below the latter two methods through examples. - -Completion and contextual documentation -======================================= - -Start typing something and press the ``tab`` key. The interface tries to -complete it with a command name. If there is more than one completion, then -they are all presented to you. Remember that Sage is case sensitive, i.e. it -differentiates upper case from lower case. Hence the ``tab`` completion of -``klein`` won't show you the ``KleinFourGroup`` command that builds the group -`\ZZ/2 \times \ZZ/2` as a permutation group. Try it on the next cells: - -.. skip - -:: - - sage: klein - - sage: Klein - -To see documentation and examples for a command, type a question mark ``?`` at -the end of the command name and press the ``tab`` key as in: - -.. skip - -:: - - sage: KleinFourGroup? - -:: - - sage: # edit here - -.. TOPIC:: Exercise A - - What is the largest prime factor of `600851475143`? - - .. skip - - :: - - sage: factor? - - :: - - sage: # edit here - -In the above manipulations we have not stored any data for -later use. This can be done in Sage with the ``=`` symbol as in:: - - sage: a = 3 - sage: b = 2 - sage: a+b - 5 - -This can be understood as Sage evaluating the expression to the right -of the ``=`` sign and creating the appropriate object, and then -associating that object with a label, given by the left-hand side (see -the foreword of :ref:`tutorial-objects-and-classes` for -details). Multiple assignments can be done at once:: - - sage: a,b = 2,3 - sage: a - 2 - sage: b - 3 - -This allows us to swap the values of two variables directly:: - - sage: a,b = 2,3 - sage: a,b = b,a - sage: a,b - (3, 2) - -We can also assign a common value to several variables simultaneously:: - - sage: c = d = 1 - sage: c, d - (1, 1) - sage: d = 2 - sage: c, d - (1, 2) - -Note that when we use the word *variable* in the computer-science sense we -mean "a label attached to some data stored by Sage". Once an object is -created, some *methods* apply to it. This means *functions* but instead of -writing **f(my_object)** you write **my_object.f()**:: - - sage: p = 17 - sage: p.is_prime() - True - -See :ref:`tutorial-objects-and-classes` for details. -To know all methods of an object you can once more use tab-completion. Write the -name of the object followed by a dot and then press ``tab``: - -.. skip - -:: - - sage: a. - - sage: # edit here - -.. TOPIC:: Exercise B - - Create the permutation 51324 and assign it to the variable ``p``. - - .. skip - - :: - - sage: Permutation? - - :: - - sage: # edit here - - - What is the ``inverse`` of ``p``? - - .. skip - - :: - - sage: p.inv - - sage: # edit here - - Does ``p`` have the ``pattern`` 123? What about 1234? And 312? (even if you don't - know what a pattern is, you should be able to find a command that does this). - - .. skip - - :: - - sage: p.pat - - sage: # edit here - -Some linear algebra -=================== - -.. TOPIC:: Exercise C - - Use the :func:`matrix` command to create the following matrix. - - .. MATH:: - - M = \left(\begin{array}{rrrr} - 10 & 4 & 1 & 1 \\ - 4 & 6 & 5 & 1 \\ - 1 & 5 & 6 & 4 \\ - 1 & 1 & 4 & 10 - \end{array}\right) - - .. skip - - :: - - sage: matrix? - - :: - - sage: # edit here - - Then, using methods of the matrix, - - 1. Compute the determinant of the matrix. - 2. Compute the echelon form of the matrix. - 3. Compute the eigenvalues of the matrix. - 4. Compute the kernel of the matrix. - 5. Compute the LLL decomposition of the matrix (and lookup the - documentation for what LLL is if needed!) - - :: - - sage: # edit here - - sage: # edit here - - Now that you know how to access the different methods of matrices, - - 6. Create the vector `v = (1,-1,-1,1)`. - 7. Compute the two products: `M\cdot v` and `v\cdot M`. What mathematically - borderline operation is Sage doing implicitly? - - .. skip - - :: - - sage: vector? - - :: - - sage: # edit here - -.. NOTE:: - - Vectors in Sage are row vectors. A method such as ``eigenspaces`` might not - return what you expect, so it is best to specify ``eigenspaces_left`` or - ``eigenspaces_right`` instead. Same thing for kernel (``left_kernel`` or - ``right_kernel``), and so on. - - -Some Plotting -============= - -The :func:`plot` command allows you to draw plots of functions. Recall -that you can access the documentation by pressing the ``tab`` key -after writing ``plot?`` in a cell: - -.. skip - -:: - - sage: plot? - -:: - - sage: # edit here - -Here is a simple example:: - - sage: var('x') # make sure x is a symbolic variable - x - sage: plot(sin(x^2), (x,0,10)) - Graphics object consisting of 1 graphics primitive - -Here is a more complicated plot. Try to change every single input to the plot -command in some way, evaluating to see what happens:: - - sage: P = plot(sin(x^2), (x,-2,2), rgbcolor=(0.8,0,0.2), thickness=3, linestyle='--', fill='axis') - sage: show(P, gridlines=True) - -Above we used the :func:`show` command to show a plot after it was created. You can -also use ``P.show`` instead:: - - sage: P.show(gridlines=True) - -Try putting the cursor right after ``P.show(`` and pressing tab to get a list of -the options for how you can change the values of the given inputs. - -.. skip - -:: - - sage: P.show( - -Plotting multiple functions at once is as easy as adding them together:: - - sage: P1 = plot(sin(x), (x,0,2*pi)) - sage: P2 = plot(cos(x), (x,0,2*pi), rgbcolor='red') - sage: P1 + P2 - Graphics object consisting of 2 graphics primitives - -Symbolic Expressions -==================== - -Here is an example of a symbolic function:: - - sage: f(x) = x^4 - 8*x^2 - 3*x + 2 - sage: f(x) - x^4 - 8*x^2 - 3*x + 2 - - sage: f(-3) - 20 - -This is an example of a function in the *mathematical* variable `x`. When Sage -starts, it defines the symbol `x` to be a mathematical variable. If you want -to use other symbols for variables, you must define them first:: - - sage: x^2 - x^2 - sage: u + v - Traceback (most recent call last): - ... - NameError: name 'u' is not defined - - sage: var('u v') - (u, v) - sage: u + v - u + v - -Still, it is possible to define symbolic functions without first -defining their variables:: - - sage: f(w) = w^2 - sage: f(3) - 9 - -In this case those variables are defined implicitly:: - - sage: w - w - -.. TOPIC:: Exercise D - - Define the symbolic function `f(x) = x \sin(x^2)`. Plot `f` on the - domain `[-3,3]` and color it red. Use the :func:`find_root` method to - numerically approximate the root of `f` on the interval `[1,2]`:: - - sage: # edit here - - Compute the tangent line to `f` at `x=1`:: - - sage: # edit here - - Plot `f` and the tangent line to `f` at `x=1` in one image:: - - sage: # edit here - -.. TOPIC:: Exercise E (Advanced) - - Solve the following equation for `y`: - - .. MATH:: - - y = 1 + x y^2 - - There are two solutions, take the one for which `\lim_{x\to0}y(x)=1`. - (Don't forget to create the variables `x` and `y`!). - - :: - - sage: # edit here - - Expand `y` as a truncated Taylor series around `0` and containing - `n=10` terms. - - :: - - sage: # edit here - - Do you recognize the coefficients of the Taylor series expansion? You might - want to use the `On-Line Encyclopedia of Integer Sequences - `_, or better yet, Sage's class :class:`OEIS` which - queries the encyclopedia: - - .. skip - - :: - - - sage: oeis? - - :: - - sage: # edit here - -Congratulations for completing your first Sage tutorial! diff --git a/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_advanced.rst b/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_advanced.rst index ed4bf792b4b..54157db8e45 100644 --- a/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_advanced.rst +++ b/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_advanced.rst @@ -29,7 +29,9 @@ Cartesian coordinates `(x,y,z)`:: manifold endowed with a positive definite metric tensor:: sage: E.category() - Category of smooth manifolds over Real Field with 53 bits of precision + Join of + Category of smooth manifolds over Real Field with 53 bits of precision and + Category of complete metric spaces sage: E.base_field() is RR True sage: E.metric() diff --git a/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_plane.rst b/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_plane.rst index 06ee9ba2fab..5b7a911d01e 100644 --- a/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_plane.rst +++ b/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_plane.rst @@ -491,7 +491,9 @@ Then we have:: manifold endowed with a positive definite metric tensor:: sage: E.category() - Category of smooth manifolds over Real Field with 53 bits of precision + Join of + Category of smooth manifolds over Real Field with 53 bits of precision and + Category of complete metric spaces sage: E.base_field() is RR True diff --git a/src/doc/en/tutorial/interactive_shell.rst b/src/doc/en/tutorial/interactive_shell.rst index eb9e71f5a61..d1407f09d38 100644 --- a/src/doc/en/tutorial/interactive_shell.rst +++ b/src/doc/en/tutorial/interactive_shell.rst @@ -12,10 +12,10 @@ Sage, you get output similar to the following: .. CODE-BLOCK:: text - ---------------------------------------------------------------------- - | SAGE Version 3.1.1, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: @@ -172,10 +172,10 @@ file). .. CODE-BLOCK:: shell-session was@form:~$ sage - ---------------------------------------------------------------------- - | SAGE Version 3.0.2, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: logstart setup Activating auto-logging. Current session state plus future input saved. @@ -191,10 +191,10 @@ file). sage: Exiting SAGE (CPU time 0m0.61s, Wall time 0m50.39s). was@form:~$ sage - ---------------------------------------------------------------------- - | SAGE Version 3.0.2, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: load("setup") Loading log file one line at a time... @@ -945,119 +945,3 @@ Each saved variable is again available. Moreover, the variable sage: a 389 - - -.. _section-notebook: - -The legacy Notebook Interface -============================= - -This section refers to the legacy Sage notebook, or "sagenb". - -SageMath is transitioning to using the -`Jupyter notebook -`_ -as a default, which has a different structure. The most important -difference for users is that individual worksheets in Jupyter -are saved on your local system just like any other file, whereas -in the Sage notebook the main point of access is in the files -described below via the server. - - -Legacy SageNB Notebook ----------------------- - -The Sage notebook is run by typing - -.. skip - -:: - - sage: notebook() - -on the command line of Sage. This starts the Sage notebook and -opens your default web browser to view it. The server's state files -are stored in ``$HOME/.sage/sage\_notebook.sagenb``. - -Other options include: - -.. skip - -:: - - sage: notebook("directory") - -which starts a new notebook server using files in the given -directory ``directory.sagenb``, instead of the default directory -``$HOME/.sage/sage_notebook``. This can be useful if you want to -have a collection of worksheets associated with a specific project, -or run several separate notebook servers at the same time. - -When you start the notebook, it first creates the following files -in ``$HOME/.sage/sage_notebook.sagenb``: - -.. CODE-BLOCK:: text - - conf.pickle - openid.pickle - twistedconf.tac - sagenb.pid - users.pickle - home/admin/ (a directory for the admin user) - home/guest/ (a directory for guests) - home/pub/ (a directory for published worksheets) - -After creating the above files, the notebook starts a web server. - -A "notebook" is a collection of user accounts, each of which can -have any number of worksheets. When you create a new worksheet, the -data that defines it is stored in the ``home/username/number`` -directories. In each such directory there is a plain text file -``worksheet.html`` - if anything ever happens to your worksheets, or Sage, -or whatever, that human-readable file contains everything needed to -reconstruct your worksheet. Each worksheet also has, at a minimum, -the files/folders: - -.. CODE-BLOCK:: text - - cells/ - worksheet.html - data/ - worksheet_conf.pickle - - -From within Sage, type ``notebook?`` for much more about how to start a -notebook server. - -The following diagram illustrates the architecture of the Sage -Notebook: - -.. CODE-BLOCK:: text - - ---------------------- - | | - | | - | firefox/safari | - | | - | javascript | - | program | - | | - | | - ---------------------- - | ^ - | AJAX | - V | - ---------------------- - | | - | sage | SAGE process 1 - | web | ------------> SAGE process 2 (Python processes) - | server | pexpect SAGE process 3 - | | . - | | . - ---------------------- . - -For help on a Sage command, ``cmd``, in the notebook browser box, -type ``cmd?`` and now hit ```` (not ````). - -For help on the keyboard shortcuts available in the notebook -interface, click on the ``Help`` link. diff --git a/src/doc/en/tutorial/introduction.rst b/src/doc/en/tutorial/introduction.rst index 10f929fc971..15a9c3138da 100644 --- a/src/doc/en/tutorial/introduction.rst +++ b/src/doc/en/tutorial/introduction.rst @@ -71,12 +71,8 @@ computer. Here we merely make a few comments. words, although Sage uses Python, IPython, PARI, GAP, Singular, Maxima, NTL, GMP, and so on, you do not need to install them separately as they are included with the Sage distribution. - However, to use certain Sage features, e.g., Macaulay or KASH, you must - install the relevant optional package or at least have the relevant - programs installed on your computer already. Macaulay and KASH are - Sage packages (for a list of available optional packages, type - ``sage -optional``, or browse the "Download" page on the Sage - website). + However, to use certain Sage features, e.g., Macaulay or KASH, you + must have the relevant programs installed on your computer already. #. The pre-compiled binary version of Sage (found on the Sage web site) may be easier and quicker to install than the source code @@ -103,8 +99,8 @@ Ways to Use Sage You can use Sage in several ways. -- **Notebook graphical interface:** see the section on the - Notebook in the reference manual and :ref:`section-notebook` below, +- **Notebook graphical interface:** run `sage -n jupyter`; see + `the Jupyter documentation on-line `_, - **Interactive command line:** see :ref:`chapter-interactive_shell`, diff --git a/src/doc/en/tutorial/latex.rst b/src/doc/en/tutorial/latex.rst index 46498619539..e2bc6816008 100644 --- a/src/doc/en/tutorial/latex.rst +++ b/src/doc/en/tutorial/latex.rst @@ -491,21 +491,3 @@ mathematics examination can maintain a correct correspondence between questions and answers by using sagetex to have Sage compute one from the other. See :ref:`sec-sagetex` for more information. - - -tex2sws begins with a LaTeX document, but defines extra -environments for the placement of Sage code. When processed with -the right tools, the result is a Sage worksheet, with content -properly formatted for MathJax and the Sage code incorporated as -input cells. So a textbook or article can be authored in -LaTeX, blocks of Sage code included, and the whole -document can be transformed into a Sage worksheet where the -mathematical text is nicely formatted and the blocks of Sage code -are "live." Currently in development, see `tex2sws @ BitBucket -`_ for more information. - -sws2tex reverses the process by beginning with a Sage worksheet -and converting it to legitimate LaTeX for subsequent -processing with all the tools available for LaTeX -documents. Currently in development, see `sws2tex @ BitBucket -`_ for more information. diff --git a/src/doc/en/tutorial/tour_linalg.rst b/src/doc/en/tutorial/tour_linalg.rst index ae444c63a5c..a38b6e1ac72 100644 --- a/src/doc/en/tutorial/tour_linalg.rst +++ b/src/doc/en/tutorial/tour_linalg.rst @@ -249,4 +249,4 @@ Note that Python is case sensitive: sage: M = MatrixSpace(QQ, 10,10, Sparse=True) Traceback (most recent call last): ... - TypeError: __classcall__() got an unexpected keyword argument 'Sparse' + TypeError: __init__() got an unexpected keyword argument 'Sparse' diff --git a/src/doc/es/tutorial/introduction.rst b/src/doc/es/tutorial/introduction.rst index 999e26cb4b7..65020d0599c 100644 --- a/src/doc/es/tutorial/introduction.rst +++ b/src/doc/es/tutorial/introduction.rst @@ -3,9 +3,9 @@ Introducción ************ Completar este tutorial debería llevarte unas 3 o 4 horas. Puedes leerlo en versión HTML o PDF, o desde el -notebook (interfaz interactiva vía web) de Sage (Haz click en ``Help``, luego haz click en ``Tutorial`` para trabajar interactivamente en el tutorial desde dentro de Sage). +notebook (interfaz interactiva vía web) de Sage (haz click en ``Help``, luego haz click en ``Tutorial`` para trabajar interactivamente en el tutorial desde dentro de Sage). -Aunque gran parte de Sage está implementado usando el lenguaje de programación +Aunque gran parte de Sage está implementada usando el lenguaje de programación Python, no es necesario ningún conocimiento previo de Python para poder leer este tutorial. En algún punto seguramente querrás aprender Python (¡un lenguaje muy divertido!), y hay muchos recursos gratuitos excelentes para hacerlo, incluyendo [PyT]_ y [Dive]_. @@ -56,7 +56,7 @@ Instalación ============ Si no tienes instalado Sage en tu computador y sólo quieres -probar algunos comandos, usa la versión en linea en http://sagecell.sagemath.org. +probar algunos comandos, usa la versión en línea en http://sagecell.sagemath.org. Mira la Guía De Instalación Para Sage en la sección de documentación de la página web principal de [Sage]_ para obtener instrucciones sobre cómo instalar @@ -68,12 +68,8 @@ Sage en tu computador. Aquí hacemos simplemente dos comentarios: Maxima, NTL, GMP, etc., no necesitas instalarlos por separado pues ya están incluidos con la distribución de Sage. Sin embargo, para utilizar ciertas características de Sage, por ejemplo, - Macaulay o KASH, debes - instalar el paquete opcional relevante o al menos tener los programas - pertinentes ya instalados en tu computador. Macaulay y KASH son - paquetes opcionales de Sage (para una lista de los paquetes opcionales - disponibles, teclea ``sage -optional``, o navega por la página de descarga - "Download" en el sitio web de Sage). + Macaulay o KASH, debes tener los programas + pertinentes ya instalados en tu computador. #. La versión binaria precompilada de Sage (que se encuentra en el sitio web de Sage) puede ser más rápida y fácil de instalar que la @@ -84,7 +80,7 @@ Sage en tu computador. Aquí hacemos simplemente dos comentarios: los resultados de tus cálculos con Sage en un archivo LaTeX), necesitarás hacerle conocer SageTeX a tu distribución de TeX. Para hacer esto, consulta la sección - "Haciendo que TeX conozca a SageTeX" en la guía de intalación de Sage + "Haciendo que TeX conozca a SageTeX" en la guía de instalación de Sage `Sage installation guide `_ (`Este enlace <../../en/installation/index.html>`_ debería llevarte a tu copia @@ -104,9 +100,8 @@ Formas de usar Sage Puedes usar Sage de varias maneras. -- **Interfáz gráfico del Notebook:** Permite usar Sage en forma interactiva - desde el navegador web. Véase la sección que trata sobre el - Notebook en el manual de referencia, +- **Interfaz gráfico del Notebook:** iniciar `sage -n jupyter`; leer + `Jupyter documentation on-line `_, - **Línea de comandos interactiva:**, @@ -133,7 +128,7 @@ Metas a largo plazo de Sage libremente disponible, de modo que los usuarios puedan entender qué está haciendo realmente el sistema y así poder extenderlo fácilmente. Tal como los matemáticos logran un entendimiento más profundo de un teorema al leerlo cuidadosamente o, por lo - ménos, al echarle una ojeada a la prueba, la gente que efectúa cálculos debe ser capaz de comprender + menos, al echarle una ojeada a la prueba, la gente que efectúa cálculos debe ser capaz de comprender cómo funcionan los cálculos leyendo el código fuente documentado. Si utilizas Sage para hacer cálculos en un artículo que vas a publicar, puedes estar seguro que tus lectores siempre tendrán libre acceso @@ -156,7 +151,7 @@ Metas a largo plazo de Sage - **Extensible:** Debe ser posible definir nuevos tipos de datos o derivar de tipos incorporados y utilizar código escrito en una amplia gama de lenguajes. -- **Fácil de usar**: Debe de ser fácil comprender qué +- **Fácil de usar**: Debe de ser fácil comprender cual funcionalidad se ha provisto para un objeto dado y examinar la documentación y el código fuente, así como alcanzar un alto nivel de soporte al usuario. diff --git a/src/doc/fr/tutorial/interactive_shell.rst b/src/doc/fr/tutorial/interactive_shell.rst index 2ce7898db9b..b8158786ab4 100644 --- a/src/doc/fr/tutorial/interactive_shell.rst +++ b/src/doc/fr/tutorial/interactive_shell.rst @@ -16,10 +16,10 @@ le shell Sage affiche un message de ce genre : :: - ---------------------------------------------------------------------- - | SAGE Version 3.1.1, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: @@ -50,7 +50,7 @@ Votre session Sage Une session est la suite des entrées et sorties qui interviennent entre le moment où vous démarrez Sage et celui où vous le quittez. Sage enregistre un journal de toutes les entrées via IPython. Si vous -utilisez le shell interactif (par opposition à l'interface *notebook*), +utilisez le shell interactif (par opposition à l'interface *jupyter*), vous pouvez taper ``%history`` (ou ``%hist``) à n'importe quel moment pour obtenir la liste de toutes les lignes de commandes entrées depuis le début de la @@ -64,9 +64,9 @@ du clavier). Les variables GLOBALES suivantes existent toujours (ne les :: - _ : dernière entrée (shell et notebook) - __ : avant-dernière entrée (shell uniquement) - _oh : liste de toutes les entrées précédentes (shell uniquement) + _ : dernière entrée + __ : avant-dernière entrée + _oh : liste de toutes les entrées précédentes Voici un exemple : @@ -78,7 +78,7 @@ Voici un exemple : _1 = 2^2 * 5^2 sage: kronecker_symbol(3,5) _2 = -1 - sage: %hist #Fonctionne depuis le shell mais pas depuis le notebook. + sage: %hist #Fonctionne depuis le shell mais pas depuis le bloc-note. 1: factor(100) 2: kronecker_symbol(3,5) 3: %hist @@ -181,10 +181,10 @@ session future (en rechargeant le fichier journal). :: was@form:~$ sage - ---------------------------------------------------------------------- - | SAGE Version 3.0.2, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: logstart setup Activating auto-logging. Current session state plus future input saved. @@ -200,10 +200,10 @@ session future (en rechargeant le fichier journal). sage: Exiting SAGE (CPU time 0m0.61s, Wall time 0m50.39s). was@form:~$ sage - ---------------------------------------------------------------------- - | SAGE Version 3.0.2, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: load("setup") Loading log file one line at a time... @@ -273,7 +273,7 @@ calculs, le *wall time* peut être nettement plus important que le temps processeur. Chronométrons maintenant le calcul de la même puissance avec le type -Integer de Sage, qui est implémenté (en Cython) en utilisant la +``Integer`` de Sage, qui est implémenté (en Cython) en utilisant la bibliothèque GMP : .. skip @@ -383,7 +383,7 @@ Trucs et astuces IPython Comme signalé plus haut, Sage utilise l'interpréteur de commandes IPython, et met donc à votre disposition toutes les commandes et fonctionnalités de celui-ci. Vous voudrez peut-être consulter la `documentation complète de IPython -`_. Voici en attendant quelques +`_. Voici en attendant quelques astuces utiles -- qui reposent sur ce que IPython appelle des « commandes magiques » : @@ -450,9 +450,9 @@ Pour plus d'information, entrez la commande ``%quickref`` pour un résumé des possibilités de IPython. Au moment où cette documentation est écrite (avril 2011), Sage emploie IPython 0.9.1. La `documentation des commandes magiques -`_ +`_ est disponible en ligne, et divers aspects un peu plus avancés de leur -fonctionnement sont décrits `ici `_. +fonctionnement sont décrits `ici `_. Erreurs et exceptions ===================== @@ -813,8 +813,8 @@ contient les 100000 premiers :math:`a_n`. sage: v = E.anlist(100000) # instantané ! (En Python, les sauvegardes et rechargements s'effectuent à l'aide du -module ``cPickle``. En particulier, on peut sauver un objet Sage ``x`` -par la commande ``cPickle.dumps(x, 2)``. Attention au ``2`` !) +module ``pickle``. En particulier, on peut sauver un objet Sage ``x`` +par la commande ``pickle.dumps(x, 2)``. Attention au ``2`` !) Sage n'est pas capable de sauvegarder les objets créés dans d'autres systèmes de calcul formel comme GAP, Singular, Maxima etc. : au rechargement, ils @@ -963,108 +963,3 @@ la variable ``b`` n'a pas été écrasée. sage: a 389 - - -.. _section-notebook: - -L'ancienne interface *notebook* -=============================== - -Cette section concerne l'ancien bloc-note de Sage (“sagenb”). - -SageMath est en cours de transition vers l'utilisation par défaut du -`bloc-note Jupyter -`_, -qui a une structure différente. Pour les utilisateurs, -la différence majeure est le fait que les feuilles de calcul sont -sauvegardées dans le système de fichiers local comme n'importe quel autre -fichier, alors que pour l'ancien bloc-note de Sage le principal accès aux -feuilles de calcul passait par le serveur comme expliqué ci-dessous. - -Ancien bloc-note SageNB ------------------------ - -Pour démarrer le *notebook* Sage, tapez - -.. skip - -:: - - sage: notebook() - -sur la ligne de commande Sage. Cela démarre le serveur du *notebook* et -ouvre votre navigateur web par défaut sur la page correspondante. Les -fichiers d'état du serveur sont placés dans ``$HOME/.sage/sage\_notebook.sagenb``. - -La variante - -.. skip - -:: - - sage: notebook("repertoire") - -lance un nouveau serveur *notebook* en utilisant les fichiers du -``repertoire.sagenb`` donné à la place de ``$HOME/.sage/sage_notebook``. Cela peut -être utile si vous voulez gérer une collection de feuilles de travail -attachées à un projet spécifique, ou encore lancer plusieurs instances -du serveur en même temps. - -Au démarrage, le *notebook* commence par créer les fichiers suivants -dans ``$HOME/.sage/sage_notebook.sagenb`` : - -:: - - conf.pickle - openid.pickle - twistedconf.tac - sagenb.pid - users.pickle - home/admin/ (sous-répertoire de l'utilisateur principal) - home/guest/ - home/pub/ - -Une fois ces fichiers créés, le *notebook* démarre un serveur web. - -Un « *notebook* » est une collection de comptes utilisateur, qui peuvent -chacun posséder un nombre quelconque de feuilles de travail. Quand vous -créez une nouvelle feuille de travail, les données correspondantes sont -stockées dans un répertoire de la forme -``home/utilisateur/numéro``. Dans chacun de ces répertoires se trouve un -fichier texte brut ``worksheet.html`` qui contient tout ce qu'il faut -pour reconstituer la feuille de travail s'il lui arrive quelque -chose, si Sage rencontre un problème, ou quoi que ce soit de ce genre. - -Dans Sage, vous pouvez taper ``notebook?`` pour beaucoup plus -d'informations sur comment démarrer un serveur. - -Le schéma suivant présente l'architecture du *Notebook* Sage : - -:: - - ---------------------- - | | - | | - | firefox/safari | - | | - | programme | - | javascript | - | | - | | - ---------------------- - | ^ - | AJAX | - V | - ---------------------- - | | - | serveur | processus SAGE 1 - | web | ------------> processus SAGE 2 (processus Python) - | sage | pexpect processus SAGE 3 - | | . - | | . - ---------------------- . - -Dans le *notebook*, pour consulter l'aide d'une commande Sage ``cmd``, -tapez ``cmd?`` dans le champ d'entrée des commandes puis tapez ```` -(et non ````). - diff --git a/src/doc/fr/tutorial/introduction.rst b/src/doc/fr/tutorial/introduction.rst index 3937ed183fe..befa558fb39 100644 --- a/src/doc/fr/tutorial/introduction.rst +++ b/src/doc/fr/tutorial/introduction.rst @@ -73,12 +73,8 @@ Nous nous limiterons ici à quelques remarques. Singular, Maxima, NTL, GMP, etc., vous n'avez pas besoin de les installer séparément, ils sont fournis dans la distribution Sage. En revanche, pour utiliser certaines des fonctionnalités de Sage, par - exemple Macaulay ou KASH, il vous faudra d'abord installer le paquet - optionnel correspondant, ou du moins avoir le logiciel correspondant - installé sur votre ordinateur. Macaulay et KASH sont disponibles pour - SAGE sous forme de modules optionnels (pour une liste de tous les - paquets optionnels disponibles, tapez ``sage -optional`` ou visitez - la page *Download* (Téléchargement) du site web de Sage). + exemple Macaulay ou KASH, il vous faudra d'abord avoir le logiciel correspondant + installé sur votre ordinateur. #. La version binaire pré-compilée de Sage (disponible sur le site web) est souvent plus facile et plus rapide à installer que la @@ -108,9 +104,8 @@ Les différentes manières d'utiliser Sage Il y a plusieurs façons d'utiliser Sage. -- **Interface graphique (« notebook ») :** voir la section sur le - *Notebook* du manuel de référence, et :ref:`section-notebook` - ci-dessous ; +- **Interface graphique (« notebook ») :** démarrer `sage -n jupyter`; lire + `Jupyter documentation on-line `_ ; - **Ligne de commande :** voir :ref:`chapter-interactive_shell` ; diff --git a/src/doc/fr/tutorial/latex.rst b/src/doc/fr/tutorial/latex.rst index 5082872a0ca..a66eea92e7c 100644 --- a/src/doc/fr/tutorial/latex.rst +++ b/src/doc/fr/tutorial/latex.rst @@ -460,21 +460,3 @@ on peut imaginer de maintenir la correspondance entre questions et réponses dans un sujet d'examen en utilisant Sage pour calculer les unes à partir des autres. Sagetex est décrit plus en détail en section :ref:`sec-sagetex` de ce document. - -tex2sws est un convertisseur LaTeX vers feuille de travail Sage. Il prend lui -aussi en entrée un document LaTeX contenant du code Sage dans des -environnements spécifiques. Après traitement convenable, on obtient une feuille -de travail pour le bloc-notes, dans laquelle les formules du document de départ -sont affichées avec MathJax et le code Sage repris dans des cellules d'entrée. -Ainsi, un manuel ou un article initialement rédigé avec LaTeX qui contient du -code Sage peut être transformé en une page web interactive où les formules -mathématiques restent formatées correctement tandis que les blocs de code Sage -deviennent exécutables. Cet outil est en cours de développement, on consultera -la page `tex2sws @ BitBucket `_ pour -plus d'information. - -sws2tex fait l'inverse : il part d'une feuille de travail Sage, qu'il convertit -en document LaTeX pour permettre de la traiter ensuite avec tous les outils -disponibles pour les documents LaTeX. sws2tex est en cours de développement, on -pourra se référer à la page `sws2tex @ BitBucket -`_ pour plus d'information. diff --git a/src/doc/fr/tutorial/tour_linalg.rst b/src/doc/fr/tutorial/tour_linalg.rst index f5daea0bf11..906793390ad 100644 --- a/src/doc/fr/tutorial/tour_linalg.rst +++ b/src/doc/fr/tutorial/tour_linalg.rst @@ -252,4 +252,4 @@ Notez que Python distingue les majuscules des minuscules : sage: M = MatrixSpace(QQ, 10,10, Sparse=True) Traceback (most recent call last): ... - TypeError: __classcall__() got an unexpected keyword argument 'Sparse' + TypeError: __init__() got an unexpected keyword argument 'Sparse' diff --git a/src/doc/ja/tutorial/interactive_shell.rst b/src/doc/ja/tutorial/interactive_shell.rst index f0d0dd92a11..6abce17c4b6 100644 --- a/src/doc/ja/tutorial/interactive_shell.rst +++ b/src/doc/ja/tutorial/interactive_shell.rst @@ -13,10 +13,10 @@ Sageを起動すると,すぐに次のような画面が現れる: :: - ---------------------------------------------------------------------- - | SAGE Version 3.1.1, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: @@ -162,10 +162,10 @@ Sageセッションのロギングと,セッションの保存(:ref:`section-s :: was@form:~$ sage - ---------------------------------------------------------------------- - | SAGE Version 3.0.2, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: logstart setup Activating auto-logging. Current session state plus future input saved. @@ -181,10 +181,10 @@ Sageセッションのロギングと,セッションの保存(:ref:`section-s sage: Exiting SAGE (CPU time 0m0.61s, Wall time 0m50.39s). was@form:~$ sage - ---------------------------------------------------------------------- - | SAGE Version 3.0.2, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: load("setup") Loading log file one line at a time... @@ -886,95 +886,3 @@ Sageは,セッション全体を保存し再ロードするための非常に sage: a 389 - - -.. _section-notebook: - -ノートブックインターフェイス -================================== - -Sageノートブックを起動するには、Sageコマンドライン上で - -.. skip - -:: - - sage: notebook() - -と実行する. -これでSageノートブックが起動すると同時に,閲覧用のデフォルトWebブラウザが開かれる. -ノートブックサーバが使用する状態ファイル群は, ``$HOME/.sage/sage\_notebook.sagenb`` に保存される. - - -起動時に指定できるオプションとして - -.. skip - - -:: - - sage: notebook("ディレクトリ名") - - -とすると,標準ディレクトリ ``$HOME/.sage/sage_notebook.sagenb`` ではなく指定した ``ディレクトリ名.sagenb`` のディレクトリにある状態ファイル群を使って新しくノートブックサーバを起動する. -このオプションは,特定のプロジェクトに多数のワークシート群がぶら下がっていたり,同時に複数のノートブックサーバを動かしたい場合に便利だ. - -ノートブックを起動すると、まず ``$HOME/.sage/sage_notebook.sagenb`` 内に以下のようなファイル群が生成される: - - -:: - - conf.pickle - openid.pickle - twistedconf.tac - sagenb.pid - users.pickle - home/admin/ - home/guest/ - home/pub/ - -上のファイル群の作成後,ノートブックはWebサーバの起動を行なう. - - -「ノートブック」(notebook)とはユーザーアカウントの集合であって,各ノートブックにはワークシートを好きな数だけ保持することができる. -ワークシートを新規に作成すると,そのワークシートを定義するデータが ``home/username/number`` ディレクトリに保存される. -これらのディレクトリに必ず出来ているのがファイル ``worksheet.html`` である. -このプレーンテキストからなるファイルには,ワークシートやSageその他に何によらず変更が加えられた場合,元のワークシートを復元するために必要な全情報が可読形式で保存されている. - - -Sage上で ``notebook?`` と入力すると,ノートブックサーバを起動する方法に関する詳しい情報が得られる. - - -次の図を見ると,Sageノートブックの構造が分る: - -:: - - ---------------------- - | | - | | - | firefox/safari | - | | - | javascript | - | program | - | | - | | - ---------------------- - | ^ - | AJAX | - V | - ---------------------- - | | - | sage | SAGE process 1 - | web | ------------> SAGE process 2 (Python processes) - | server | pexpect SAGE process 3 - | | . - | | . - ---------------------- . - - - -ノートブックからSageコマンド ``コマンド名`` のヘルプを見たければ,ブラウザ表示画面内入力ボックスで ``コマンド名?`` と入力し, ```` を押せばよい(```` ではない). - - -ノートブック上で通用するキーボードショートカットを確認するには, ``Help`` リンクをクリックする. - diff --git a/src/doc/ja/tutorial/introduction.rst b/src/doc/ja/tutorial/introduction.rst index 98cddfa9b7f..004c67dcad1 100644 --- a/src/doc/ja/tutorial/introduction.rst +++ b/src/doc/ja/tutorial/introduction.rst @@ -83,7 +83,8 @@ Sageの使いかた Sageを使うには以下のようなやり方がある. -- **ノートブック グラフィカル インターフェイス:** レファレンスマニュアルのノートブックに関する節,および以下の :ref:`section-notebook` 節を参照. +- **ノートブック グラフィカル インターフェイス:** `sage -n jupyter` を起動します。 + `Jupyter documentation on-line `_ を読む. - **対話的コマンドライン:** :ref:`chapter-interactive_shell` 節を参照. diff --git a/src/doc/ja/tutorial/latex.rst b/src/doc/ja/tutorial/latex.rst index 39573f38bce..12162959f3b 100644 --- a/src/doc/ja/tutorial/latex.rst +++ b/src/doc/ja/tutorial/latex.rst @@ -402,14 +402,3 @@ TeXとSageのさらなる統合運用に役立つプログラムが三つある LaTeX文書のコンパイル処理過程で,Sageの演算やLaTeXによるフォーマット支援などの全ての機能も自動的に実行されるのである. sagetexを使えば,例えば数学試験作成において,問題の計算そのものをSageに実行させて対応する解答を正確に維持管理することなどが可能になる. 詳細は :ref:`sec-sagetex` 節を参照してほしい. - - -tex2swsはLaTeX文書にSageコードを組込むための環境を定義している. -これをしかるべきツールで処理すると,MathJaxで適切に表示される本文と入力セル経由で動作するSageコードが組込まれたSageワークシートが出来上がる. -tex2sws環境を使えばLaTeXで教科書や記事をSageコードのブロックを含んだ形で執筆することができるが,これを変換すると数式混じりの本文は美しく整形され,かつSageコード部分が機能するSageワークシートになるわけである. -現在も開発進行中で,詳細は `tex2sws @ BitBucket `_ を見てほしい. - - -これとは逆に,sws2texはSageワークシートをLaTeX形式に変換してLaTeX関連ツールによる処理を可能にする. -現在も開発中で,詳細は `sws2tex @ BitBucket `_ を見てほしい. - diff --git a/src/doc/ja/tutorial/tour_linalg.rst b/src/doc/ja/tutorial/tour_linalg.rst index d147a28e0da..769dbb32006 100644 --- a/src/doc/ja/tutorial/tour_linalg.rst +++ b/src/doc/ja/tutorial/tour_linalg.rst @@ -266,4 +266,4 @@ Pythonでは,大文字小文字が区別されることに注意: sage: M = MatrixSpace(QQ, 10,10, Sparse=True) Traceback (most recent call last): ... - TypeError: __classcall__() got an unexpected keyword argument 'Sparse' + TypeError: __init__() got an unexpected keyword argument 'Sparse' diff --git a/src/doc/pt/tutorial/interactive_shell.rst b/src/doc/pt/tutorial/interactive_shell.rst index 869f4644692..29e2e395331 100644 --- a/src/doc/pt/tutorial/interactive_shell.rst +++ b/src/doc/pt/tutorial/interactive_shell.rst @@ -15,10 +15,10 @@ obtém o seguinte: :: - ---------------------------------------------------------------------- - | SAGE Version 3.1.1, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: Para sair do Sage pressione Ctrl-D ou digite ``quit`` ou ``exit``. @@ -173,10 +173,10 @@ arquivo log). :: was@form:~$ sage - ---------------------------------------------------------------------- - | SAGE Version 3.0.2, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: logstart setup Activating auto-logging. Current session state plus future input saved. @@ -192,10 +192,10 @@ arquivo log). sage: Exiting SAGE (CPU time 0m0.61s, Wall time 0m50.39s). was@form:~$ sage - ---------------------------------------------------------------------- - | SAGE Version 3.0.2, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: load "setup" Loading log file one line at a time... @@ -936,106 +936,3 @@ variável ``b`` não foi redefinida. sage: a 389 - - -.. _section-notebook: - -A Interface do Notebook -======================= - -Esta seção refere-se ao notebook Sage legado, ou "sagenb". - -SageMath está em transição para uso do -`Jupyter `_ -como padrão, que tem uma -estrutura diferente. A diferença mais importante para os usuários é que -as planilhas individuais no Jupyter são salvas no seu sistema local -(como qualquer outro arquivo é salvo), enquanto que no tipo de notebook -anterior Sage (ou sagenb) o principal ponto de acesso está nos arquivos -descritos abaixo através do servidor. - -Notebook Sage legado --------------------- - -O Sage Notebook é iniciado digitando - -.. skip - -:: - - sage: notebook() - -na linha de comando do Sage. Isso inicia o Notebook e abre o seu -navegador padrão para visualizá-lo. Os arquivos de estado do servidor -são armazenados em ``$HOME/.sage/sage\_notebook.sagenb``. - -Outras opções incluem: - -.. skip - -:: - - sage: notebook("directory") - -a qual inicia um novo servidor para o Notebook usando arquivos em um -dado diretório ``directory.sagenb``, em vez do diretório padrão -``$HOME/.sage/sage_notebook.sagenb``. Isso pode ser útil se você quiser ter -uma coleção de folhas de trabalho (worksheets) associadas com um -projeto específico, ou executar vários Notebooks separadamente ao -mesmo tempo. - -Quando você inicia o Notebook, ele primeiro cria os seguintes arquivos -em ``$HOME/.sage/sage_notebook.sagenb``: - -:: - - conf.pickle - openid.pickle - twistedconf.tac - sagenb.pid - users.pickle - home/admin/ - home/guest/ - home/pub/ - -Após criar os arquivos acima, o Notebook inicia o servidor web. - -Um "Notebook" é uma coleção de contas de usuário, cada qual pode ter -várias folhas de trabalho (worksheets). Quando você cria uma nova -folha de trabalho, os dados dela são armazenados no diretórios -``home/username/number``. Em cada diretório desse há um arquivo -texto ``worksheet.html`` - se algum problema ocorrer com as suas -folhas de trabalho, ou com o Sage, esse arquivo texto contém toda -informação necessária para reconstruir a folha de trabalho. - -A partir do Sage, digite ``notebook?`` para mais informações sobre -como iniciar um servidor. - -O seguinte diagrama ilustra a arquitetura do Notebook Sage: - -:: - - ---------------------- - | | - | | - | firefox/safari | - | | - | javascript | - | program | - | | - | | - ---------------------- - | ^ - | AJAX | - V | - ---------------------- - | | - | sage | SAGE process 1 - | web | ------------> SAGE process 2 (Python processes) - | server | pexpect SAGE process 3 - | | . - | | . - ---------------------- . - -Para ajuda sobre as teclas de atalho disponíveis no Notebook, clique -no link ``Help``. diff --git a/src/doc/pt/tutorial/introduction.rst b/src/doc/pt/tutorial/introduction.rst index 3446f4f5720..175039d79ed 100644 --- a/src/doc/pt/tutorial/introduction.rst +++ b/src/doc/pt/tutorial/introduction.rst @@ -73,11 +73,8 @@ computador. Aqui faremos apenas alguns comentários. Singular, Maxima, NTL, GMP, e uma série de outros programas, você não precisa instalá-los separadamente pois eles estão incluídos no Sage. Todavia, para usar alguns recursos, por exemplo, o Macaulay - ou o KASH, você precisa instalar pacotes de software adicionais ou - ter os programas necessários já instalados no seu computador. O - Macaulay e o KASH estão disponíveis como pacotes adicionais do Sage - (para uma lista de pacotes adicionais, digite ``sage -optional``, - ou visite a seção "Download" na página do Sage na internet). + ou o KASH, você precisa + ter os programas necessários já instalados no seu computador. #. A versão pré-compilada do Sage (disponível na página do Sage na internet) pode ser mais fácil e rápida para instalar do que a @@ -103,8 +100,8 @@ Formas de usar o Sage Você pode usar o Sage de diversas formas. -- **Interface gráfica Notebook:** veja a seção sobre o Notebook em - :ref:`section-notebook`, +- **Interface gráfica Notebook:** inicie `sage -n jupyter`; leia + `Jupyter documentation on-line `_, - **Linha de comando interativa:** veja :ref:`chapter-interactive_shell`, diff --git a/src/doc/pt/tutorial/latex.rst b/src/doc/pt/tutorial/latex.rst index 3bc919e14e1..b31804422e9 100644 --- a/src/doc/pt/tutorial/latex.rst +++ b/src/doc/pt/tutorial/latex.rst @@ -492,21 +492,3 @@ ser executados automaticamente. Como um exemplo, um exame matemático pode manter uma correspondência entre questões e respostas usando o sagetex para fazer cálculos com o Sage. Veja :ref:`sec-sagetex` para mais informações. - -O tex2sws começa com um documento LaTeX, mas define ambientes -adicionais para inserir código em Sage. Quando processado com as -ferramentas adequadas, o resultado é uma folha de trabalho do Sage, -com conteúdo apropriadamente formatado para o MathJax e com código em -Sage incorporado como células de entrada. Então um livro texto ou -artigo pode ser criado em LaTeX, ter blocos de código em Sage -incluídos, e o documento todo pode ser transformado em uma folha de -trabalho do Sage onde o texto matemático é bem formatado e os blocos -de código em Sage podem ser facilmente executados. Atualmente em -desenvolvimento, veja `tex2sws @ BitBucket -`_ para mais informações. - -O sws2tex reverte o processo partindo de uma folha de trabalho do Sage -e convertendo o conteúdo para LaTeX para ser posteriormente processado -com as ferramentas disponíveis para documentos em LaTeX. Atualmente em -desenvolvimento, veja `sws2tex @ BitBucket -`_ para mais informações. diff --git a/src/doc/pt/tutorial/tour_linalg.rst b/src/doc/pt/tutorial/tour_linalg.rst index 7cec4914492..e7dd77f36dd 100644 --- a/src/doc/pt/tutorial/tour_linalg.rst +++ b/src/doc/pt/tutorial/tour_linalg.rst @@ -230,4 +230,4 @@ Note que o Python é sensível a maiúsculas e minúsculas: sage: M = MatrixSpace(QQ, 10,10, Sparse=True) Traceback (most recent call last): ... - TypeError: __classcall__() got an unexpected keyword argument 'Sparse' + TypeError: __init__() got an unexpected keyword argument 'Sparse' diff --git a/src/doc/ru/tutorial/interactive_shell.rst b/src/doc/ru/tutorial/interactive_shell.rst index 82c7c5f69dd..7e0b3f43795 100644 --- a/src/doc/ru/tutorial/interactive_shell.rst +++ b/src/doc/ru/tutorial/interactive_shell.rst @@ -15,11 +15,10 @@ Sage вы увидите вывод, похожий на следующий: :: - ---------------------------------------------------------------------- - | SAGE Version 3.1.1, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- - + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: @@ -166,10 +165,10 @@ Notebook), то вы можете ввести ``%hist``, чтобы вывес :: was@form:~$ sage - ---------------------------------------------------------------------- - | SAGE Version 3.0.2, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: logstart setup Activating auto-logging. Current session state plus future input saved. @@ -185,10 +184,10 @@ Notebook), то вы можете ввести ``%hist``, чтобы вывес sage: Exiting SAGE (CPU time 0m0.61s, Wall time 0m50.39s). was@form:~$ sage - ---------------------------------------------------------------------- - | SAGE Version 3.0.2, Release Date: 2008-05-24 | - | Type notebook() for the GUI, and license() for information. | - ---------------------------------------------------------------------- + ┌────────────────────────────────────────────────────────────────────┐ + │ SageMath version 9.0, Release Date: 2020-01-01 │ + │ Using Python 3.7.3. Type "help()" for help. │ + └────────────────────────────────────────────────────────────────────┘ sage: load("setup") Loading log file one line at a time... @@ -841,94 +840,3 @@ Sage обладает очень гибкими возможностями со sage: a 389 - - -.. _section-notebook: - -Интерфейс Notebook -================== - -Sage notebook запускается с помощью следующей команды - -.. skip - -:: - - sage: notebook() - -введенной в командной строке. Она запустит Sage notebook и откроет его в -браузере по умолчанию. Файлы состояния сервера хранятся в -``$HOME/.sage/sage\_notebook.sagenb``. - -Другие параметры включают в себя: - -.. skip - -:: - - sage: notebook("directory") - -Этот параметр позволяет запустить сервер Notebook, используя файлы в заданной -директории ``directory.sagenb``, вместо использования директории по умолчанию -``$HOME/.sage/sage_notebook.sagenb``. Это может оказаться полезным, если вы хотите -иметь коллекцию рабочих листов, связанных с конкретным проектом, или если вы -хотите запускать несколько отдельных серверов Notebook в одно время. - -Когда вы запускаете Notebook, вначале он создает следующие файлы в директории -``$HOME/.sage/sage_notebook.sagenb``: - -:: - - conf.pickle - openid.pickle - twistedconf.tac - sagenb.pid - users.pickle - home/admin/ - home/guest/ - home/pub/ - -После создания этих файлов, Notebook запускает веб-сервер. - -Notebook — это коллекция учетных записей пользователей (аккаунтов), каждый -из которых может иметь любое количество рабочих листов. Когда вы создаете -новый рабочий лист, информация, которая описывает его, сохраняется в директории -``home/username/number``. В каждой такой директории находится простой -текстовый файл ``worksheet.html``; если что-то случится с вашими рабочими листами, -или с Sage, или что-нибудь еще пойдет не так, то текcтовый файл, который легко -читается, поможет восстановить ваш лист полностью. - -В Sage введите ``notebook?`` для получения подробной информации о том, как -запустить сервер Notebook. - -Следующая диаграмма иллюстрирует архитектуру Sage Notebook: - -:: - - ---------------------- - | | - | | - | firefox/safari | - | | - | javascript | - | program | - | | - | | - ---------------------- - | ^ - | AJAX | - V | - ---------------------- - | | - | sage | SAGE process 1 - | web | ------------> SAGE process 2 (Python processes) - | server | pexpect SAGE process 3 - | | . - | | . - ---------------------- . - -Для получения справки о команде Sage, ``cmd``, в notebook введите, ``cmd?`` и -нажмите ```` (не ````). - -Для получения справки о горячих клавишах интерфейса notebook нажмите ссылку -``Help``. diff --git a/src/doc/ru/tutorial/introduction.rst b/src/doc/ru/tutorial/introduction.rst index 8489fd51c85..591b72564ad 100644 --- a/src/doc/ru/tutorial/introduction.rst +++ b/src/doc/ru/tutorial/introduction.rst @@ -72,9 +72,7 @@ Sage в разделе документации: [SA]_ Здесь мы прив требуется, так как они уже включены. Однако, для использования некоторых функций Sage таких, как Macaulay или KASH, вы должны установить требующиеся файлы или иметь соответствующие программы на - вашем компьютере. Macaulay и KASH являются пакетами Sage (для просмотра - списка доступных пакетов введите ``sage -optional`` или изучите раздел - “Download” на веб-сайте Sage). + вашем компьютере. #. Предварительно скомпилированную бинарную версию Sage, которую также можно найти на веб-сайте, будет легче установить, чем версию в исходном коде. @@ -98,8 +96,8 @@ Sage в разделе документации: [SA]_ Здесь мы прив Работа в Sage может быть осуществлена несколькими путями: -- **Notebook (графический интерфейс):** см. раздел о Notebook в справочном - руководстве, а также :ref:`section-notebook` ниже; +- **Notebook (графический интерфейс):** запустите `sage -n jupyter`; читайте + `Jupyter documentation on-line `_, - **Интерактивная командная строка:** см. :ref:`chapter-interactive_shell`; diff --git a/src/doc/ru/tutorial/tour_linalg.rst b/src/doc/ru/tutorial/tour_linalg.rst index 82b7b74d71c..7b5cf65426d 100644 --- a/src/doc/ru/tutorial/tour_linalg.rst +++ b/src/doc/ru/tutorial/tour_linalg.rst @@ -224,4 +224,4 @@ Sage поддерживает разреженную линейную алгеб sage: M = MatrixSpace(QQ, 10,10, Sparse=True) Traceback (most recent call last): ... - TypeError: __classcall__() got an unexpected keyword argument 'Sparse' + TypeError: __init__() got an unexpected keyword argument 'Sparse' diff --git a/src/ext/images/corner.png b/src/ext/images/corner.png deleted file mode 100644 index f4b96d28f6f..00000000000 Binary files a/src/ext/images/corner.png and /dev/null differ diff --git a/src/ext/images/evaluate.png b/src/ext/images/evaluate.png deleted file mode 100644 index a3436fa983e..00000000000 Binary files a/src/ext/images/evaluate.png and /dev/null differ diff --git a/src/ext/images/evaluate_over.png b/src/ext/images/evaluate_over.png deleted file mode 100644 index 46fc67204d7..00000000000 Binary files a/src/ext/images/evaluate_over.png and /dev/null differ diff --git a/src/ext/images/favicon.ico b/src/ext/images/favicon.ico deleted file mode 100644 index 3cefb468d16..00000000000 Binary files a/src/ext/images/favicon.ico and /dev/null differ diff --git a/src/ext/images/sagelogo.png b/src/ext/images/sagelogo.png deleted file mode 100644 index 5b9fad9a657..00000000000 Binary files a/src/ext/images/sagelogo.png and /dev/null differ diff --git a/src/fpickle_setup.py b/src/fpickle_setup.py deleted file mode 100644 index 1c800a7f012..00000000000 --- a/src/fpickle_setup.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -The purpose of this file is to allow instancemethods to be pickable. -This is needed in setup.py only and not installed anywhere, thus is not -a library file. It solves the issue from :trac:`11874`. -""" -import types -from six.moves import copyreg -from six import get_method_function, get_method_self - -# The following four methods are taken from twisted.persisted.styles -# into sage.misc.fpickle, and then here. It was needed due to -# chicken vs egg issue, as we cannot use sage.misc.fpickle in -# setup.py of sage-***.spkg - - -def pickleMethod(method): - 'support function for copyreg to pickle method refs' - return unpickleMethod, (get_method_function(method).__name__, - get_method_self(method), - get_method_self(method).__class__) - - -def unpickleMethod(im_name, - im_self, - im_class): - 'support function for copyreg to unpickle method refs' - try: - unbound = getattr(im_class, im_name) - if im_self is None: - return unbound - bound = types.MethodType(get_method_function(unbound), - im_self) - return bound - except AttributeError: - # assert im_self is not None,"No recourse: no instance to guess from." - # Attempt a common fix before bailing -- if classes have - # changed around since we pickled this method, we may still be - # able to get it by looking on the instance's current class. - unbound = getattr(im_self.__class__, im_name) - if im_self is None: - return unbound - bound = types.MethodType(get_method_function(unbound), - im_self) - return bound - -copyreg.pickle(types.MethodType, - pickleMethod, - unpickleMethod) - -oldModules = {} - - -def pickleModule(module): - 'support function for copyreg to pickle module refs' - return unpickleModule, (module.__name__,) - - -def unpickleModule(name): - 'support function for copyreg to unpickle module refs' - if name in oldModules: - name = oldModules[name] - return __import__(name, {}, {}, 'x') - - -copyreg.pickle(types.ModuleType, - pickleModule, - unpickleModule) diff --git a/src/lib/pkgconfig/.gitignore b/src/lib/pkgconfig/.gitignore deleted file mode 100644 index 1b806dad94b..00000000000 --- a/src/lib/pkgconfig/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# .pc files created at configure time -blas.pc -cblas.pc -gsl.pc -lapack.pc diff --git a/src/mac-app/AppController.m b/src/mac-app/AppController.m index 7b9a41154f8..be8a19b538c 100644 --- a/src/mac-app/AppController.m +++ b/src/mac-app/AppController.m @@ -147,8 +147,8 @@ -(IBAction)startJupyter:(id)sender{ // We have to run it through a shell so that the default arguments are parsed properly NSString *command = [NSString stringWithFormat: @"'%@' --notebook=jupyter %@ 2>&1 | tee -a '%@' |" - " grep -i --context 1 'Notebook is running at' |" - " grep -o http://.*", + " grep -i --line-buffered --context=1 'Notebook is running at' |" + " grep --line-buffered -o http://.*", escSageBin, // default args are ready to be (defArgs == nil) ? @"" : defArgs, @@ -852,9 +852,9 @@ -(BOOL)isTigerOrLess{ // TODO: make installing packages easy -- stringByLaunchingPath:withArguments:error: // TODO: maybe this should be written in py-objc so that we can call into sage directly (but then we would have to worry about environment etc.) -// TODO: make some services (search for NSSendTypes) -- pack/unpack spkg, extract sws from pdf, crap/fixdoctests/preparse/Test/coverage/pkg/pkg_nc/etc. +// TODO: make some services (search for NSSendTypes) -- pack/unpack spkg, crap/fixdoctests/preparse/Test/coverage/pkg/pkg_nc/etc. -// TODO: open files such as .sws, .sage, .py, .spkg, -- .pdf (and extract sws from them), .htm, whatever else I can handle +// TODO: open files such as .sage, .py, .spkg, -- .pdf, .htm, whatever else I can handle // TODO: quicklook generator, spotlight importer -- use UTI // NOTE: http://developer.apple.com/mac/library/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html // TODO: icons for files -- they need some help with the alpha channel. I clearly don't know what I'm doing. I should really make them all from by script... diff --git a/src/mac-app/sage-README-macOS.txt b/src/mac-app/sage-README-macOS.txt index 8f4a7bc8e8b..c0d346ec040 100644 --- a/src/mac-app/sage-README-macOS.txt +++ b/src/mac-app/sage-README-macOS.txt @@ -33,14 +33,7 @@ instead you see a folder called "sage", proceed as follows. 5) Sage should pop up in a window. -6) For the graphical notebook, type - notebook() - You might have to open Firefox or Safari (your choice) - to the URL - http://localhost:8000 - to use Sage on your computer. - -7) Email +6) Email http://groups.google.com/group/sage-support with any questions. diff --git a/src/mac-app/start-sage.sh b/src/mac-app/start-sage.sh index 6c60965755b..85fe6b3f7d6 100755 --- a/src/mac-app/start-sage.sh +++ b/src/mac-app/start-sage.sh @@ -17,6 +17,7 @@ NB_TYPE="$3" # Read environment variables cd $(dirname $SAGE_EXECUTABLE) +source local/bin/sage-env-config source local/bin/sage-env diff --git a/src/module_list.py b/src/module_list.py index a535785d23d..6f6395f6efc 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -1,3 +1,11 @@ +######################################################### +### +### OBSOLETE FILE - DO NOT ADD TO IT +### +### See https://trac.sagemath.org/ticket/29701 +### +######################################################### + import os from distutils.extension import Extension from sage.env import SAGE_LOCAL @@ -63,48 +71,9 @@ except ValueError: pass -######################################################### -### Library order -######################################################### - -# This list defines the *order* of linking libraries. A library should -# be put *before* any library it links to. Cython allows -# defining libraries using "# distutils: libraries = LIB". However, if -# there are multiple libraries, the order is undefined so we need to -# manually reorder the libraries according to this list. The order is -# important in particular for Cygwin. Any libraries which are not -# listed here will be added at the end of the list (without changing -# their relative order). There is one exception: stdc++ is always put -# at the very end of the list. from sage.env import cython_aliases aliases = cython_aliases() - arb_dylib_name = aliases["ARB_LIBRARY"] -library_order_list = aliases["SINGULAR_LIBRARIES"] + [ - "ec", "ecm", -] + aliases["LINBOX_LIBRARIES"] + aliases["FFLASFFPACK_LIBRARIES"] + aliases["GSL_LIBRARIES"] + [ - "pari", "flint", "ratpoints", "ecl", "glpk", "ppl", - arb_dylib_name, "mpfi", "mpfr", "mpc", "gmp", "gmpxx", - "brial", - "brial_groebner", - "m4rie", -] + m4ri_libs + [ - "zn_poly", "gap", -] + gd_libs + png_libs + [ - "m", "readline", "Lfunction" , -] + cblas_libs + zlib_libs - -# Make a dict with library:order pairs, where the order are negative -# integers sorted according to library_order_list. When sorting, -# unlisted libraries have order 0, so they appear after the libraries -# in library_order_list. -n = len(library_order_list) -library_order = {} -for i in range(n): - lib = library_order_list[i] - library_order[lib] = i-n - -library_order["stdc++"] = 1000 ############################################################# ### List of modules @@ -116,14 +85,6 @@ ############################################################# from sage_setup.optional_extension import OptionalExtension -UNAME = os.uname() - -def uname_specific(name, value, alternative): - if name in UNAME[0]: - return value - else: - return alternative - ext_modules = [ @@ -133,22 +94,7 @@ def uname_specific(name, value, alternative): ## ################################ - Extension('sage.algebras.quatalg.quaternion_algebra_element', - sources = ['sage/algebras/quatalg/quaternion_algebra_element.pyx'], - language='c++', - libraries = ["gmp", "m", "ntl"]), - - Extension('*', sources = ['sage/algebras/letterplace/*.pyx']), - - Extension('*', sources = ['sage/algebras/finite_dimensional_algebras/*.pyx']), - - Extension('sage.algebras.quatalg.quaternion_algebra_cython', - sources = ['sage/algebras/quatalg/quaternion_algebra_cython.pyx'], - language='c++', - libraries = ["gmp", "m", "ntl"]), - - Extension('sage.algebras.lie_algebras.lie_algebra_element', - sources = ["sage/algebras/lie_algebras/lie_algebra_element.pyx"]), + Extension('*', ['sage/algebras/**/*.pyx']), ################################ ## @@ -183,6 +129,9 @@ def uname_specific(name, value, alternative): Extension('sage.coding.codecan.codecan', sources = ['sage/coding/codecan/codecan.pyx']), + Extension('sage.coding.kasami_codes', + sources = ['sage/coding/kasami_codes.pyx']), + Extension('*', ['sage/coding/**/*.pyx']), ################################ @@ -279,46 +228,7 @@ def uname_specific(name, value, alternative): ## ################################ - Extension('sage.geometry.point_collection', - sources = ['sage/geometry/point_collection.pyx']), - - Extension('sage.geometry.toric_lattice_element', - sources = ['sage/geometry/toric_lattice_element.pyx']), - - Extension('sage.geometry.integral_points', - sources = ['sage/geometry/integral_points.pyx']), - - Extension('sage.geometry.triangulation.base', - sources = ['sage/geometry/triangulation/base.pyx', - 'sage/geometry/triangulation/functions.cc', - 'sage/geometry/triangulation/data.cc', - 'sage/geometry/triangulation/triangulations.cc'], - depends = ['sage/geometry/triangulation/functions.h', - 'sage/geometry/triangulation/data.h', - 'sage/geometry/triangulation/triangulations.h'], - language="c++"), - - Extension('sage.geometry.polyhedron.combinatorial_polyhedron.base', - 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'], - extra_compile_args=['-std=c++11']), - - Extension('sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator', - sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx']), - - Extension('sage.geometry.polyhedron.combinatorial_polyhedron.polyhedron_face_lattice', - sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx']), - - Extension('sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face', - sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx']), - - Extension('sage.geometry.polyhedron.combinatorial_polyhedron.conversions', - sources = ['sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx']), + Extension('*', ['sage/geometry/**/*.pyx']), ################################ ## @@ -341,6 +251,9 @@ def uname_specific(name, value, alternative): Extension('sage.graphs.independent_sets', sources = ['sage/graphs/independent_sets.pyx']), + Extension('sage.graphs.isoperimetric_inequalities', + sources = ['sage/graphs/isoperimetric_inequalities.pyx']), + Extension('sage.graphs.graph_decompositions.fast_digraph', sources = ['sage/graphs/graph_decompositions/fast_digraph.pyx']), @@ -375,8 +288,7 @@ def uname_specific(name, value, alternative): sources = ['sage/graphs/base/static_dense_graph.pyx']), Extension('sage.graphs.base.static_sparse_graph', - sources = ['sage/graphs/base/static_sparse_graph.pyx'], - language = 'c++'), + sources = ['sage/graphs/base/static_sparse_graph.pyx']), Extension('sage.graphs.base.static_sparse_backend', sources = ['sage/graphs/base/static_sparse_backend.pyx']), @@ -395,25 +307,20 @@ def uname_specific(name, value, alternative): OptionalExtension("sage.graphs.mcqd", ["sage/graphs/mcqd.pyx"], - language = "c++", package = 'mcqd'), OptionalExtension("sage.graphs.bliss", ["sage/graphs/bliss.pyx"], - language = "c++", - libraries = ['bliss'], package = 'bliss'), Extension('sage.graphs.planarity', - sources = ['sage/graphs/planarity.pyx'], - libraries=['planarity']), + sources = ['sage/graphs/planarity.pyx']), Extension('sage.graphs.strongly_regular_db', sources = ['sage/graphs/strongly_regular_db.pyx']), Extension('sage.graphs.graph_decompositions.rankwidth', - sources = ['sage/graphs/graph_decompositions/rankwidth.pyx'], - libraries=['rw']), + sources = ['sage/graphs/graph_decompositions/rankwidth.pyx']), Extension('sage.graphs.graph_decompositions.bandwidth', sources = ['sage/graphs/graph_decompositions/bandwidth.pyx']), @@ -423,7 +330,6 @@ def uname_specific(name, value, alternative): OptionalExtension('sage.graphs.graph_decompositions.tdlib', sources = ['sage/graphs/graph_decompositions/tdlib.pyx'], - language="c++", package = 'tdlib'), Extension('sage.graphs.graph_decompositions.clique_separators', @@ -433,8 +339,7 @@ def uname_specific(name, value, alternative): sources = ['sage/graphs/spanning_tree.pyx']), Extension('sage.graphs.path_enumeration', - sources = ['sage/graphs/path_enumeration.pyx'], - language = 'c++'), + sources = ['sage/graphs/path_enumeration.pyx']), Extension('sage.graphs.connectivity', sources = ['sage/graphs/connectivity.pyx']), @@ -449,8 +354,7 @@ def uname_specific(name, value, alternative): sources = ['sage/graphs/hyperbolicity.pyx']), Extension('sage.graphs.base.c_graph', - sources = ['sage/graphs/base/c_graph.pyx'], - language = 'c++'), + sources = ['sage/graphs/base/c_graph.pyx']), Extension('sage.graphs.base.sparse_graph', sources = ['sage/graphs/base/sparse_graph.pyx']), @@ -509,9 +413,6 @@ def uname_specific(name, value, alternative): OptionalExtension('sage.libs.coxeter3.coxeter', sources = ['sage/libs/coxeter3/coxeter.pyx'], - include_dirs = [os.path.join(SAGE_INC, 'coxeter')], - language="c++", - libraries = ['coxeter3'], package = 'coxeter3'), Extension('sage.libs.ecl', @@ -519,21 +420,16 @@ def uname_specific(name, value, alternative): OptionalExtension("sage.libs.fes", ["sage/libs/fes.pyx"], - language = "c", - libraries = ['fes'], package = 'fes'), Extension('sage.libs.flint.flint', - sources = ["sage/libs/flint/flint.pyx"], - extra_compile_args = ["-D_XPG6"]), + sources = ["sage/libs/flint/flint.pyx"]), Extension('sage.libs.flint.fmpz_poly', - sources = ["sage/libs/flint/fmpz_poly.pyx"], - extra_compile_args = ["-D_XPG6"]), + sources = ["sage/libs/flint/fmpz_poly.pyx"]), Extension('sage.libs.flint.arith', - sources = ["sage/libs/flint/arith.pyx"], - extra_compile_args = ["-D_XPG6"]), + sources = ["sage/libs/flint/arith.pyx"]), Extension("sage.libs.glpk.error", ["sage/libs/glpk/error.pyx"]), @@ -542,62 +438,47 @@ def uname_specific(name, value, alternative): sources = ['sage/libs/gmp/pylong.pyx']), Extension('sage.libs.braiding', - sources = ["sage/libs/braiding.pyx"], - libraries = ["braiding"], - language = 'c++'), + sources = ["sage/libs/braiding.pyx"]), Extension('sage.libs.homfly', - sources = ["sage/libs/homfly.pyx"], - libraries = ["homfly", "gc"]), + sources = ["sage/libs/homfly.pyx"]), OptionalExtension('sage.libs.sirocco', sources = ["sage/libs/sirocco.pyx"], - libraries = ["sirocco"], - package="sirocco", - language = 'c++'), + package="sirocco"), Extension('*', ['sage/libs/linbox/*.pyx']), Extension('sage.libs.lcalc.lcalc_Lfunction', - sources = ['sage/libs/lcalc/lcalc_Lfunction.pyx'], - libraries = ['m', 'ntl', 'Lfunction'], - extra_compile_args=["-O3", "-ffast-math"], - language = 'c++'), + sources = ['sage/libs/lcalc/lcalc_Lfunction.pyx']), Extension('sage.libs.libecm', - sources = ['sage/libs/libecm.pyx'], - libraries = ['ecm'], - extra_link_args = uname_specific("Linux", ["-Wl,-z,noexecstack"], - [])), + sources = ['sage/libs/libecm.pyx']), Extension('sage.libs.lrcalc.lrcalc', sources = ["sage/libs/lrcalc/lrcalc.pyx"]), OptionalExtension("sage.libs.meataxe", sources = ['sage/libs/meataxe.pyx'], - libraries = ['mtx'], package = 'meataxe'), Extension('*', ['sage/libs/pari/*.pyx']), Extension('sage.libs.ppl', - sources = ['sage/libs/ppl.pyx', 'sage/libs/ppl_shim.cc']), + sources = ['sage/libs/ppl.pyx']), Extension('*', ['sage/libs/pynac/*.pyx']), Extension('sage.libs.ratpoints', - sources = ["sage/libs/ratpoints.pyx"], - libraries = ["ratpoints"]), + sources = ["sage/libs/ratpoints.pyx"]), Extension('sage.libs.readline', - sources = ['sage/libs/readline.pyx'], - libraries = ['readline']), + sources = ['sage/libs/readline.pyx']), Extension('*', sources = ['sage/libs/singular/*.pyx']), Extension('sage.libs.symmetrica.symmetrica', - sources = ["sage/libs/symmetrica/symmetrica.pyx"], - libraries = ["symmetrica"]), + sources = ["sage/libs/symmetrica/symmetrica.pyx"]), Extension('sage.libs.mpmath.utils', sources = ["sage/libs/mpmath/utils.pyx"]), @@ -649,110 +530,7 @@ def uname_specific(name, value, alternative): ## ################################### - Extension('sage.libs.ntl.convert', - sources = ["sage/libs/ntl/convert.pyx"], - libraries = ["ntl", "gmp"], - language='c++'), - - Extension('sage.libs.ntl.error', - sources = ["sage/libs/ntl/error.pyx"], - libraries = ["ntl", "gmp"], - language='c++'), - - Extension('sage.libs.ntl.ntl_GF2', - sources = ["sage/libs/ntl/ntl_GF2.pyx"], - libraries = ["ntl", "gmp"], - language='c++'), - - Extension('sage.libs.ntl.ntl_GF2E', - sources = ["sage/libs/ntl/ntl_GF2E.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_GF2EContext', - sources = ["sage/libs/ntl/ntl_GF2EContext.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_GF2EX', - sources = ["sage/libs/ntl/ntl_GF2EX.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_GF2X', - sources = ["sage/libs/ntl/ntl_GF2X.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_lzz_p', - sources = ["sage/libs/ntl/ntl_lzz_p.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_lzz_pContext', - sources = ["sage/libs/ntl/ntl_lzz_pContext.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_lzz_pX', - sources = ["sage/libs/ntl/ntl_lzz_pX.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_mat_GF2', - sources = ["sage/libs/ntl/ntl_mat_GF2.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_mat_GF2E', - sources = ["sage/libs/ntl/ntl_mat_GF2E.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_mat_ZZ', - sources = ["sage/libs/ntl/ntl_mat_ZZ.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_ZZ', - sources = ["sage/libs/ntl/ntl_ZZ.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_ZZX', - sources = ["sage/libs/ntl/ntl_ZZX.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_ZZ_p', - sources = ["sage/libs/ntl/ntl_ZZ_p.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_ZZ_pContext', - sources = ["sage/libs/ntl/ntl_ZZ_pContext.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_ZZ_pE', - sources = ["sage/libs/ntl/ntl_ZZ_pE.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_ZZ_pEContext', - sources = ["sage/libs/ntl/ntl_ZZ_pEContext.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_ZZ_pEX', - sources = ["sage/libs/ntl/ntl_ZZ_pEX.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), - - Extension('sage.libs.ntl.ntl_ZZ_pX', - sources = ["sage/libs/ntl/ntl_ZZ_pX.pyx"], - libraries = ["ntl", "gmp", "m"], - language='c++'), + Extension('*', ["sage/libs/ntl/*.pyx"]), ################################ ## @@ -788,16 +566,13 @@ def uname_specific(name, value, alternative): sources = ['sage/matrix/matrix2.pyx']), Extension("sage.matrix.matrix_complex_ball_dense", - ["sage/matrix/matrix_complex_ball_dense.pyx"], - libraries=[arb_dylib_name]), + ["sage/matrix/matrix_complex_ball_dense.pyx"]), Extension('sage.matrix.matrix_complex_double_dense', sources = ['sage/matrix/matrix_complex_double_dense.pyx']), Extension('sage.matrix.matrix_cyclo_dense', - sources = ['sage/matrix/matrix_cyclo_dense.pyx'], - language = "c++", - libraries=['ntl']), + sources = ['sage/matrix/matrix_cyclo_dense.pyx']), Extension('sage.matrix.matrix_gap', sources = ['sage/matrix/matrix_gap.pyx']), @@ -815,43 +590,22 @@ def uname_specific(name, value, alternative): sources = ['sage/matrix/matrix_generic_sparse.pyx']), Extension('sage.matrix.matrix_integer_dense', - sources = ['sage/matrix/matrix_integer_dense.pyx'], - extra_compile_args = m4ri_extra_compile_args, - libraries = ['iml', 'ntl', 'gmp', 'm'] + cblas_libs, - library_dirs = cblas_library_dirs, - include_dirs = cblas_include_dirs), + sources = ['sage/matrix/matrix_integer_dense.pyx']), Extension('sage.matrix.matrix_integer_sparse', sources = ['sage/matrix/matrix_integer_sparse.pyx']), Extension('sage.matrix.matrix_mod2_dense', - sources = ['sage/matrix/matrix_mod2_dense.pyx'], - libraries = m4ri_libs + gd_libs + png_libs + zlib_libs, - library_dirs = m4ri_library_dirs + gd_library_dirs + png_library_dirs + zlib_library_dirs, - include_dirs = m4ri_include_dirs + gd_include_dirs + png_include_dirs + zlib_include_dirs, - extra_compile_args = m4ri_extra_compile_args), + sources = ['sage/matrix/matrix_mod2_dense.pyx']), Extension('sage.matrix.matrix_gf2e_dense', - sources = ['sage/matrix/matrix_gf2e_dense.pyx'], - libraries = ['m4rie'] + m4ri_libs + ['m'], - library_dirs = m4ri_library_dirs, - include_dirs = m4ri_include_dirs, - extra_compile_args = m4ri_extra_compile_args), + sources = ['sage/matrix/matrix_gf2e_dense.pyx']), Extension('sage.matrix.matrix_modn_dense_float', - sources = ['sage/matrix/matrix_modn_dense_float.pyx'], - language="c++", - libraries = cblas_libs, - library_dirs = cblas_library_dirs, - include_dirs = cblas_include_dirs), + sources = ['sage/matrix/matrix_modn_dense_float.pyx']), Extension('sage.matrix.matrix_modn_dense_double', - sources = ['sage/matrix/matrix_modn_dense_double.pyx'], - language="c++", - libraries = cblas_libs, - library_dirs = cblas_library_dirs, - include_dirs = cblas_include_dirs, - extra_compile_args = ["-D_XPG6"]), + sources = ['sage/matrix/matrix_modn_dense_double.pyx']), Extension('sage.matrix.matrix_modn_sparse', sources = ['sage/matrix/matrix_modn_sparse.pyx']), @@ -863,11 +617,7 @@ def uname_specific(name, value, alternative): sources = ['sage/matrix/matrix_polynomial_dense.pyx']), Extension('sage.matrix.matrix_rational_dense', - sources = ['sage/matrix/matrix_rational_dense.pyx'], - extra_compile_args = ["-D_XPG6"] + m4ri_extra_compile_args, - libraries = ['iml', 'ntl', 'm'] + cblas_libs, - library_dirs = cblas_library_dirs, - include_dirs = cblas_include_dirs), + sources = ['sage/matrix/matrix_rational_dense.pyx']), Extension('sage.matrix.matrix_rational_sparse', sources = ['sage/matrix/matrix_rational_sparse.pyx']), @@ -886,7 +636,6 @@ def uname_specific(name, value, alternative): OptionalExtension("sage.matrix.matrix_gfpn_dense", sources = ['sage/matrix/matrix_gfpn_dense.pyx'], - libraries = ['mtx'], package = 'meataxe'), Extension('sage.matrix.misc', @@ -918,6 +667,9 @@ def uname_specific(name, value, alternative): ################################ Extension('*', ['sage/misc/*.pyx']), + Extension('sage.misc.sage_ostools', + sources = ['sage/misc/sage_ostools.pyx'], + libraries = ['sqlite3']), ################################ ## @@ -925,45 +677,7 @@ def uname_specific(name, value, alternative): ## ################################ - Extension('sage.modular.arithgroup.congroup', - sources = ['sage/modular/arithgroup/congroup.pyx']), - - Extension('sage.modular.arithgroup.farey_symbol', - sources = ['sage/modular/arithgroup/farey_symbol.pyx']), - - Extension('sage.modular.arithgroup.arithgroup_element', - sources = ['sage/modular/arithgroup/arithgroup_element.pyx']), - - Extension('sage.modular.hypergeometric_misc', - sources = ['sage/modular/hypergeometric_misc.pyx']), - - Extension('sage.modular.modform.eis_series_cython', - sources = ['sage/modular/modform/eis_series_cython.pyx']), - - Extension('sage.modular.modform.l_series_gross_zagier_coeffs', - sources = ['sage/modular/modform/l_series_gross_zagier_coeffs.pyx']), - - Extension('sage.modular.modsym.apply', - sources = ['sage/modular/modsym/apply.pyx'], - extra_compile_args=["-D_XPG6"]), - - Extension('sage.modular.modsym.manin_symbol', - sources = ['sage/modular/modsym/manin_symbol.pyx']), - - Extension('sage.modular.modsym.relation_matrix_pyx', - sources = ['sage/modular/modsym/relation_matrix_pyx.pyx']), - - Extension('sage.modular.modsym.heilbronn', - sources = ['sage/modular/modsym/heilbronn.pyx'], - extra_compile_args=["-D_XPG6"]), - - Extension('sage.modular.modsym.p1list', - sources = ['sage/modular/modsym/p1list.pyx']), - - Extension('sage.modular.pollack_stevens.dist', - sources = ['sage/modular/pollack_stevens/dist.pyx'], - libraries = ["gmp", "zn_poly"], - extra_compile_args = ["-D_XPG6"]), + Extension('*', ['sage/modular/**/*.pyx']), ################################ ## @@ -971,51 +685,15 @@ def uname_specific(name, value, alternative): ## ################################ - Extension('sage.modules.vector_rational_sparse', - sources = ['sage/modules/vector_rational_sparse.pyx']), - - Extension('sage.modules.vector_integer_sparse', - sources = ['sage/modules/vector_integer_sparse.pyx']), - - Extension('sage.modules.vector_modn_sparse', - sources = ['sage/modules/vector_modn_sparse.pyx']), - - Extension('sage.modules.finite_submodule_iter', - sources = ['sage/modules/finite_submodule_iter.pyx']), - - Extension('sage.modules.free_module_element', - sources = ['sage/modules/free_module_element.pyx']), + Extension('*', ['sage/modules/**/*.pyx']), - Extension('sage.modules.module', - sources = ['sage/modules/module.pyx']), - - Extension('sage.modules.vector_complex_double_dense', - ['sage/modules/vector_complex_double_dense.pyx']), - - Extension('sage.modules.vector_double_dense', - ['sage/modules/vector_double_dense.pyx']), - - Extension('sage.modules.vector_integer_dense', - sources = ['sage/modules/vector_integer_dense.pyx']), - - Extension('sage.modules.vector_modn_dense', - sources = ['sage/modules/vector_modn_dense.pyx']), - - Extension('sage.modules.vector_mod2_dense', - sources = ['sage/modules/vector_mod2_dense.pyx'], - libraries = m4ri_libs + gd_libs + png_libs, - library_dirs = m4ri_library_dirs + gd_library_dirs + png_library_dirs, - include_dirs = m4ri_include_dirs + gd_include_dirs + png_include_dirs, - extra_compile_args = m4ri_extra_compile_args), - - Extension('sage.modules.vector_rational_dense', - sources = ['sage/modules/vector_rational_dense.pyx']), - - Extension('sage.modules.vector_real_double_dense', - ['sage/modules/vector_real_double_dense.pyx']), + ################################ + ## + ## sage.monoids + ## + ################################ - Extension('sage.modules.with_basis.indexed_element', - sources = ['sage/modules/with_basis/indexed_element.pyx']), + Extension('*', ['sage/monoids/**/*.pyx']), ################################ ## @@ -1105,34 +783,19 @@ def uname_specific(name, value, alternative): ################################ Extension('sage.rings.sum_of_squares', - sources = ['sage/rings/sum_of_squares.pyx'], - libraries = ['m']), + sources = ['sage/rings/sum_of_squares.pyx']), Extension('sage.rings.bernmm', - sources = ['sage/rings/bernmm.pyx', - 'sage/rings/bernmm/bern_modp.cpp', - 'sage/rings/bernmm/bern_modp_util.cpp', - 'sage/rings/bernmm/bern_rat.cpp'], - libraries = ['ntl', 'pthread', 'gmp'], - depends = ['sage/rings/bernmm/bern_modp.h', - 'sage/rings/bernmm/bern_modp_util.h', - 'sage/rings/bernmm/bern_rat.h'], - language = 'c++', - define_macros=[('USE_THREADS', '1'), - ('THREAD_STACK_SIZE', '4096')]), + sources = ['sage/rings/bernmm.pyx']), Extension('sage.rings.bernoulli_mod_p', - sources = ['sage/rings/bernoulli_mod_p.pyx'], - libraries=['ntl', 'gmp'], - language = 'c++'), + sources = ['sage/rings/bernoulli_mod_p.pyx']), Extension("sage.rings.complex_arb", ["sage/rings/complex_arb.pyx"]), Extension('sage.rings.complex_double', - sources = ['sage/rings/complex_double.pyx'], - extra_compile_args = ["-D_XPG6"], - libraries = ['m']), + sources = ['sage/rings/complex_double.pyx']), Extension('sage.rings.complex_interval', sources = ['sage/rings/complex_interval.pyx']), @@ -1141,12 +804,10 @@ def uname_specific(name, value, alternative): sources = ['sage/rings/complex_number.pyx']), Extension('sage.rings.integer', - sources = ['sage/rings/integer.pyx'], - libraries=['ntl']), + sources = ['sage/rings/integer.pyx']), Extension('sage.rings.integer_ring', - sources = ['sage/rings/integer_ring.pyx'], - libraries=['ntl']), + sources = ['sage/rings/integer_ring.pyx']), Extension('sage.rings.factorint', sources = ['sage/rings/factorint.pyx']), @@ -1158,9 +819,7 @@ def uname_specific(name, value, alternative): sources = ['sage/rings/fraction_field_element.pyx']), Extension('sage.rings.fraction_field_FpT', - sources = ['sage/rings/fraction_field_FpT.pyx'], - libraries = ["gmp", "ntl", "zn_poly"], - language = 'c++'), + sources = ['sage/rings/fraction_field_FpT.pyx']), Extension('sage.rings.laurent_series_ring_element', sources = ['sage/rings/laurent_series_ring_element.pyx']), @@ -1196,8 +855,7 @@ def uname_specific(name, value, alternative): sources = ['sage/rings/puiseux_series_ring_element.pyx']), Extension('sage.rings.rational', - sources = ['sage/rings/rational.pyx'], - libraries=['ntl']), + sources = ['sage/rings/rational.pyx']), Extension('sage.rings.real_double', sources = ['sage/rings/real_double.pyx']), @@ -1253,14 +911,10 @@ def uname_specific(name, value, alternative): sources = ['sage/rings/finite_rings/integer_mod.pyx']), Extension('sage.rings.finite_rings.element_givaro', - sources = ["sage/rings/finite_rings/element_givaro.pyx"], - libraries = ['givaro', 'ntl', 'gmp', 'm'], - language='c++'), + sources = ["sage/rings/finite_rings/element_givaro.pyx"]), Extension('sage.rings.finite_rings.element_ntl_gf2e', - sources = ['sage/rings/finite_rings/element_ntl_gf2e.pyx'], - libraries = ['ntl'], - language = 'c++'), + sources = ['sage/rings/finite_rings/element_ntl_gf2e.pyx']), Extension('sage.rings.finite_rings.element_pari_ffelt', sources = ['sage/rings/finite_rings/element_pari_ffelt.pyx']), @@ -1272,9 +926,7 @@ def uname_specific(name, value, alternative): sources = ["sage/rings/finite_rings/hom_prime_finite_field.pyx"]), Extension('sage.rings.finite_rings.hom_finite_field_givaro', - sources = ["sage/rings/finite_rings/hom_finite_field_givaro.pyx"], - libraries = ['givaro', 'ntl', 'gmp', 'm'], - language='c++'), + sources = ["sage/rings/finite_rings/hom_finite_field_givaro.pyx"]), ################################ ## @@ -1295,14 +947,10 @@ def uname_specific(name, value, alternative): sources = ['sage/rings/number_field/number_field_base.pyx']), Extension('sage.rings.number_field.number_field_element', - sources = ['sage/rings/number_field/number_field_element.pyx'], - libraries=['ntl'], - language = 'c++'), + sources = ['sage/rings/number_field/number_field_element.pyx']), Extension('sage.rings.number_field.number_field_element_quadratic', - sources = ['sage/rings/number_field/number_field_element_quadratic.pyx'], - libraries=['ntl'], - language = 'c++'), + sources = ['sage/rings/number_field/number_field_element_quadratic.pyx']), Extension('sage.rings.number_field.number_field_morphisms', sources = ['sage/rings/number_field/number_field_morphisms.pyx']), @@ -1311,8 +959,7 @@ def uname_specific(name, value, alternative): sources = ['sage/rings/number_field/totallyreal.pyx']), Extension('sage.rings.number_field.totallyreal_data', - sources = ['sage/rings/number_field/totallyreal_data.pyx'], - libraries = ['gmp']), + sources = ['sage/rings/number_field/totallyreal_data.pyx']), ################################ ## @@ -1339,9 +986,7 @@ def uname_specific(name, value, alternative): sources = ['sage/rings/padics/padic_floating_point_element.pyx']), Extension('sage.rings.padics.padic_ext_element', - sources = ['sage/rings/padics/padic_ext_element.pyx'], - libraries=['ntl', 'gmp', 'm'], - language='c++'), + sources = ['sage/rings/padics/padic_ext_element.pyx']), Extension('sage.rings.padics.padic_fixed_mod_element', sources = ['sage/rings/padics/padic_fixed_mod_element.pyx']), @@ -1350,49 +995,31 @@ def uname_specific(name, value, alternative): sources = ['sage/rings/padics/padic_generic_element.pyx']), Extension('sage.rings.padics.padic_printing', - sources = ['sage/rings/padics/padic_printing.pyx'], - libraries=['gmp', 'ntl', 'm'], - language='c++'), + sources = ['sage/rings/padics/padic_printing.pyx']), Extension('sage.rings.padics.padic_ZZ_pX_CA_element', - sources = ['sage/rings/padics/padic_ZZ_pX_CA_element.pyx'], - libraries = ['ntl', 'gmp', 'm'], - language='c++'), + sources = ['sage/rings/padics/padic_ZZ_pX_CA_element.pyx']), Extension('sage.rings.padics.padic_ZZ_pX_CR_element', - sources = ['sage/rings/padics/padic_ZZ_pX_CR_element.pyx'], - libraries=['ntl', 'gmp', 'm'], - language='c++'), + sources = ['sage/rings/padics/padic_ZZ_pX_CR_element.pyx']), Extension('sage.rings.padics.padic_ZZ_pX_element', - sources = ['sage/rings/padics/padic_ZZ_pX_element.pyx'], - libraries=['ntl', 'gmp', 'm'], - language='c++'), + sources = ['sage/rings/padics/padic_ZZ_pX_element.pyx']), Extension('sage.rings.padics.padic_ZZ_pX_FM_element', - sources = ['sage/rings/padics/padic_ZZ_pX_FM_element.pyx'], - libraries=['ntl', 'gmp', 'm'], - language='c++'), + sources = ['sage/rings/padics/padic_ZZ_pX_FM_element.pyx']), Extension('sage.rings.padics.pow_computer', - sources = ['sage/rings/padics/pow_computer.pyx'], - libraries = ["ntl", "gmp", "m"], - language='c++'), + sources = ['sage/rings/padics/pow_computer.pyx']), Extension('sage.rings.padics.pow_computer_ext', - sources = ['sage/rings/padics/pow_computer_ext.pyx'], - libraries = ["ntl", "gmp", "m"], - language='c++'), + sources = ['sage/rings/padics/pow_computer_ext.pyx']), Extension('sage.rings.padics.pow_computer_flint', - sources = ['sage/rings/padics/pow_computer_flint.pyx'], - libraries = ["gmp", "ntl"], - language='c++'), + sources = ['sage/rings/padics/pow_computer_flint.pyx']), Extension('sage.rings.padics.pow_computer_relative', - sources = ['sage/rings/padics/pow_computer_relative.pyx'], - libraries = ["ntl", "gmp", "m"], - language='c++'), + sources = ['sage/rings/padics/pow_computer_relative.pyx']), Extension('sage.rings.padics.qadic_flint_CR', sources = ['sage/rings/padics/qadic_flint_CR.pyx']), @@ -1404,8 +1031,7 @@ def uname_specific(name, value, alternative): sources = ['sage/rings/padics/qadic_flint_FM.pyx']), Extension('sage.rings.padics.qadic_flint_FP', - sources = ['sage/rings/padics/qadic_flint_FP.pyx'], - libraries = ["flint"]), + sources = ['sage/rings/padics/qadic_flint_FP.pyx']), Extension('sage.rings.padics.relative_ramified_FM', sources = ['sage/rings/padics/relative_ramified_FM.pyx']), @@ -1425,10 +1051,11 @@ def uname_specific(name, value, alternative): Extension('sage.rings.polynomial.cyclotomic', sources = ['sage/rings/polynomial/cyclotomic.pyx']), - Extension('sage.rings.polynomial.evaluation', - libraries = ["ntl"], - sources = ['sage/rings/polynomial/evaluation.pyx'], - language = 'c++'), + Extension('sage.rings.polynomial.evaluation_flint', + sources = ['sage/rings/polynomial/evaluation_flint.pyx']), + + Extension('sage.rings.polynomial.evaluation_ntl', + sources = ['sage/rings/polynomial/evaluation_ntl.pyx']), Extension('sage.rings.polynomial.laurent_polynomial', sources = ['sage/rings/polynomial/laurent_polynomial.pyx']), @@ -1467,51 +1094,32 @@ def uname_specific(name, value, alternative): sources = ['sage/rings/polynomial/polynomial_element.pyx']), Extension('sage.rings.polynomial.polynomial_gf2x', - sources = ['sage/rings/polynomial/polynomial_gf2x.pyx'], - libraries = ['gmp', 'ntl'], - extra_compile_args = m4ri_extra_compile_args, - language = 'c++'), + sources = ['sage/rings/polynomial/polynomial_gf2x.pyx']), Extension('sage.rings.polynomial.polynomial_zz_pex', - sources = ['sage/rings/polynomial/polynomial_zz_pex.pyx'], - libraries = ['ntl', 'gmp'], - language = 'c++'), + sources = ['sage/rings/polynomial/polynomial_zz_pex.pyx']), Extension('sage.rings.polynomial.polynomial_zmod_flint', - sources = ['sage/rings/polynomial/polynomial_zmod_flint.pyx'], - libraries = ["gmp", "ntl", "zn_poly"], - language = 'c++'), + sources = ['sage/rings/polynomial/polynomial_zmod_flint.pyx']), Extension('sage.rings.polynomial.polynomial_integer_dense_flint', - sources = ['sage/rings/polynomial/polynomial_integer_dense_flint.pyx'], - language = 'c++', - libraries = ["ntl", "gmp"]), + sources = ['sage/rings/polynomial/polynomial_integer_dense_flint.pyx']), Extension('sage.rings.polynomial.polynomial_integer_dense_ntl', - sources = ['sage/rings/polynomial/polynomial_integer_dense_ntl.pyx'], - libraries = ['ntl', 'gmp'], - language = 'c++'), + sources = ['sage/rings/polynomial/polynomial_integer_dense_ntl.pyx']), Extension('sage.rings.polynomial.polynomial_rational_flint', - sources = ['sage/rings/polynomial/polynomial_rational_flint.pyx'], - libraries = ["ntl", "gmp"], - language = 'c++'), + sources = ['sage/rings/polynomial/polynomial_rational_flint.pyx']), Extension('sage.rings.polynomial.polynomial_modn_dense_ntl', - sources = ['sage/rings/polynomial/polynomial_modn_dense_ntl.pyx'], - libraries = ['ntl', 'gmp'], - language = 'c++'), + sources = ['sage/rings/polynomial/polynomial_modn_dense_ntl.pyx']), Extension('sage.rings.polynomial.polynomial_ring_homomorphism', sources = ['sage/rings/polynomial/polynomial_ring_homomorphism.pyx']), Extension('sage.rings.polynomial.pbori', sources = ['sage/rings/polynomial/pbori.pyx'], - libraries=['brial', 'brial_groebner'] + m4ri_libs + png_libs, - library_dirs = m4ri_library_dirs + png_library_dirs, - include_dirs = m4ri_include_dirs + png_include_dirs, - depends = [SAGE_INC + "/polybori/" + hd + ".h" for hd in ["polybori", "config"]], - extra_compile_args = m4ri_extra_compile_args), + depends = [SAGE_INC + "/polybori/" + hd + ".h" for hd in ["polybori", "config"]]), Extension('sage.rings.polynomial.polynomial_real_mpfr_dense', sources = ['sage/rings/polynomial/polynomial_real_mpfr_dense.pyx']), @@ -1525,9 +1133,18 @@ def uname_specific(name, value, alternative): Extension('sage.rings.polynomial.symmetric_reduction', sources = ['sage/rings/polynomial/symmetric_reduction.pyx']), + Extension('sage.rings.polynomial.ore_polynomial_element', + sources = ['sage/rings/polynomial/ore_polynomial_element.pyx']), + Extension('sage.rings.polynomial.skew_polynomial_element', sources = ['sage/rings/polynomial/skew_polynomial_element.pyx']), + Extension('sage.rings.polynomial.skew_polynomial_finite_order', + sources = ['sage/rings/polynomial/skew_polynomial_finite_order.pyx']), + + Extension('sage.rings.polynomial.skew_polynomial_finite_field', + sources = ['sage/rings/polynomial/skew_polynomial_finite_field.pyx']), + # Note that weil_polynomials includes distutils directives in order to support # conditional OpenMP compilation (by uncommenting lines) Extension('sage.rings.polynomial.weil.weil_polynomials', @@ -1558,28 +1175,7 @@ def uname_specific(name, value, alternative): ## ################################ - Extension('sage.schemes.elliptic_curves.descent_two_isogeny', - sources = ['sage/schemes/elliptic_curves/descent_two_isogeny.pyx'], - libraries = ['ratpoints']), - - Extension('sage.schemes.elliptic_curves.period_lattice_region', - sources = ['sage/schemes/elliptic_curves/period_lattice_region.pyx']), - - Extension('sage.schemes.hyperelliptic_curves.hypellfrob', - sources = ['sage/schemes/hyperelliptic_curves/hypellfrob.pyx', - 'sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp', - 'sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.cpp', - 'sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp'], - libraries = ['gmp', 'ntl', 'zn_poly'], - depends = ['sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h', - 'sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.h', - 'sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.h'], - language = 'c++', - include_dirs = ['sage/libs/ntl/', - 'sage/schemes/hyperelliptic_curves/hypellfrob/']), - - Extension('sage.schemes.toric.divisor_class', - sources = ['sage/schemes/toric/divisor_class.pyx']), + Extension('*', ['sage/schemes/**/*.pyx']), ################################ ## @@ -1595,25 +1191,7 @@ def uname_specific(name, value, alternative): ## ################################ - Extension('sage.stats.hmm.util', - sources = ['sage/stats/hmm/util.pyx']), - - Extension('sage.stats.hmm.distributions', - sources = ['sage/stats/hmm/distributions.pyx']), - - Extension('sage.stats.hmm.hmm', - sources = ['sage/stats/hmm/hmm.pyx']), - - Extension('sage.stats.hmm.chmm', - sources = ['sage/stats/hmm/chmm.pyx']), - - Extension('sage.stats.intlist', - sources = ['sage/stats/intlist.pyx']), - - Extension('sage.stats.distributions.discrete_gaussian_integer', - sources = ['sage/stats/distributions/discrete_gaussian_integer.pyx', 'sage/stats/distributions/dgs_gauss_mp.c', 'sage/stats/distributions/dgs_gauss_dp.c', 'sage/stats/distributions/dgs_bern.c'], - depends = ['sage/stats/distributions/dgs_gauss.h', 'sage/stats/distributions/dgs_bern.h', 'sage/stats/distributions/dgs_misc.h'], - extra_compile_args = ["-D_XOPEN_SOURCE=600"]), + Extension('*', ['sage/stats/**/*.pyx']), ################################ ## @@ -1621,13 +1199,6 @@ def uname_specific(name, value, alternative): ## ################################ - # Compile this with -Os because it works around a bug with - # GCC-4.7.3 + Cython 0.19 on Itanium, see Trac #14452. Moreover, it - # actually results in faster code than -O3. - Extension('sage.structure.element', - sources = ['sage/structure/element.pyx'], - extra_compile_args=["-Os"]), - Extension('*', ['sage/structure/*.pyx']), ################################ @@ -1644,10 +1215,6 @@ def uname_specific(name, value, alternative): ## ################################ - Extension('sage.tests.stl_vector', - sources = ['sage/tests/stl_vector.pyx'], - language = 'c++'), + Extension('*', ['sage/tests/**/*.pyx']) - Extension('sage.tests.cython', - sources = ['sage/tests/cython.pyx']), ] diff --git a/src/sage/__init__.py b/src/sage/__init__.py index e2ae5b8cebc..5be0bace31a 100644 --- a/src/sage/__init__.py +++ b/src/sage/__init__.py @@ -4,6 +4,7 @@ # to many other Python packages. from sage.version import version as __version__ +import sys # Make sure that the correct zlib library is loaded. This is needed # to prevent the system zlib to be loaded instead of the Sage one. # See https://trac.sagemath.org/ticket/23122 @@ -66,3 +67,40 @@ def isfunction(obj): pass # Python 2 else: del ExtensionFileLoader.get_source + + +# Work around a Cygwin-specific bug caused by sqlite3; see +# https://trac.sagemath.org/ticket/30157 and the docstring for +# fix_for_ticket_30157 +# Here we monkey-patch the sqlite3 module to ensure the fix is +# applied the very first time a connection is made to a sqlite3 +# database +if sys.platform == 'cygwin': + def patch_sqlite3(): + try: + from sage.misc.sage_ostools import fix_for_ticket_30157 + except ImportError: + # The module might not have been re-built yet; don't worry about it + # then + return + + import sqlite3, functools + orig_sqlite3_connect = sqlite3.connect + + @functools.wraps(orig_sqlite3_connect) + def connect(*args, **kwargs): + if fix_for_ticket_30157(): + raise RuntimeError( + 'patch for Trac ticket #30157 failed; please report this ' + 'bug to https://trac.sagemath.org') + + # Undo the monkey-patch + try: + return orig_sqlite3_connect(*args, **kwargs) + finally: + sqlite3.connect = orig_sqlite3_connect + + sqlite3.connect = connect + + patch_sqlite3() + del patch_sqlite3 diff --git a/src/sage/algebras/askey_wilson.py b/src/sage/algebras/askey_wilson.py index b84069ca9ff..a8e71ae2448 100644 --- a/src/sage/algebras/askey_wilson.py +++ b/src/sage/algebras/askey_wilson.py @@ -26,6 +26,7 @@ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.sets.non_negative_integers import NonNegativeIntegers + class AskeyWilsonAlgebra(CombinatorialFreeModule): r""" The (universal) Askey-Wilson algebra. @@ -270,14 +271,13 @@ def _repr_term(self, t): sage: AW._repr_term((0,1,0,3,7,2)) 'B*a^3*b^7*g^2' """ - ret = '' - def exp(l,e): + def exp(l, e): if e == 0: return '' if e == 1: return '*' + l return '*' + l + '^{}'.format(e) - ret = ''.join(exp(l,e) for l,e in zip(['A','B','C','a','b','g'], t)) + ret = ''.join(exp(l, e) for l, e in zip(['A','B','C','a','b','g'], t)) if not ret: return '1' if ret[0] == '*': @@ -300,15 +300,14 @@ def _latex_term(self, t): """ if sum(t) == 0: return '1' - ret = '' - def exp(l,e): + def exp(l, e): if e == 0: return '' if e == 1: return l return l + '^{{{}}}'.format(e) - var_names = ['A','B','C','\\alpha','\\beta','\\gamma'] - return ''.join(exp(l,e) for l,e in zip(var_names, t)) + var_names = ['A', 'B', 'C', '\\alpha', '\\beta', '\\gamma'] + return ''.join(exp(l, e) for l, e in zip(var_names, t)) def _repr_(self): r""" @@ -758,7 +757,9 @@ def loop_representation(self): mu = la + inv nu = (self._q**2 + self._q**-2) * mu + mu**2 nuI = M(nu) - category = Algebras(Rings().Commutative()) + # After #29374 is fixed, the category can become + # Algebras(Rings().Commutative()) as it was before #29399. + category = Rings() return AlgebraMorphism(self, [q*A + q**-1*Ai, q*B + q**-1*Bi, q*C + q**-1*Ci, nuI, nuI, nuI], codomain=M, category=category) diff --git a/src/sage/algebras/catalog.py b/src/sage/algebras/catalog.py index a4423f44267..76a59c355cc 100644 --- a/src/sage/algebras/catalog.py +++ b/src/sage/algebras/catalog.py @@ -9,7 +9,10 @@ Let ```` indicate pressing the tab key. So begin by typing ``algebras.`` to the see the currently implemented named algebras. +- :class:`algebras.ArikiKoike + ` - :class:`algebras.AskeyWilson ` +- :class:`algebras.Blob ` - :class:`algebras.Brauer ` - :class:`algebras.Clifford ` - :class:`algebras.ClusterAlgebra ` @@ -91,6 +94,7 @@ lazy_import('sage.algebras.shuffle_algebra', 'ShuffleAlgebra', 'Shuffle') lazy_import('sage.algebras.schur_algebra', 'SchurAlgebra', 'Schur') lazy_import('sage.algebras.commutative_dga', 'GradedCommutativeAlgebra', 'GradedCommutative') +lazy_import('sage.algebras.hecke_algebras.ariki_koike_algebra', 'ArikiKoikeAlgebra', 'ArikiKoike') lazy_import('sage.algebras.rational_cherednik_algebra', 'RationalCherednikAlgebra', 'RationalCherednik') lazy_import('sage.algebras.yokonuma_hecke_algebra', 'YokonumaHeckeAlgebra', 'YokonumaHecke') lazy_import('sage.combinat.posets.incidence_algebras', 'IncidenceAlgebra', 'Incidence') @@ -99,6 +103,7 @@ lazy_import('sage.combinat.diagram_algebras', 'PartitionAlgebra', 'Partition') lazy_import('sage.combinat.diagram_algebras', 'PlanarAlgebra', 'PlanarPartition') lazy_import('sage.combinat.diagram_algebras', 'TemperleyLiebAlgebra', 'TemperleyLieb') +lazy_import('sage.combinat.blob_algebra', 'BlobAlgebra', 'Blob') lazy_import('sage.combinat.posets.moebius_algebra', 'MoebiusAlgebra', 'Moebius') lazy_import('sage.combinat.free_prelie_algebra', 'FreePreLieAlgebra', 'FreePreLie') lazy_import('sage.combinat.free_dendriform_algebra', 'FreeDendriformAlgebra', 'FreeDendriform') diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 2c4f5c9b03c..dfb3ff479b6 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -16,9 +16,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six import iteritems -from sage.misc.six import with_metaclass from sage.misc.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation from copy import copy @@ -124,7 +122,7 @@ def _mul_(self, other): # the dictionary describing the element # ``e[i]`` * (the element described by the dictionary ``cur``) # (where ``e[i]`` is the ``i``-th standard basis vector). - for mr,cr in iteritems(cur): + for mr,cr in cur.items(): # Commute the factor as necessary until we are in order pos = 0 for j in mr: @@ -160,7 +158,7 @@ def _mul_(self, other): cur = next # Add the distributed terms to the total - for index,coeff in iteritems(cur): + for index,coeff in cur.items(): d[index] = d.get(index, zero) + cl * coeff if d[index] == zero: del d[index] @@ -734,15 +732,13 @@ def _element_constructor_(self, x): if x in self.free_module(): R = self.base_ring() if x.parent().base_ring() is R: - return self.element_class(self, {(i,): c for i,c in iteritems(x)}) - return self.element_class(self, {(i,): R(c) for i,c in iteritems(x) if R(c) != R.zero()}) + return self.element_class(self, {(i,): c for i,c in x.items()}) + return self.element_class(self, {(i,): R(c) for i,c in x.items() if R(c) != R.zero()}) - if isinstance(x, CliffordAlgebraElement): - if x.parent() is self: - return x - if self.has_coerce_map_from(x.parent()): - R = self.base_ring() - return self.element_class(self, {i: R(c) for i,c in x if R(c) != R.zero()}) + if (isinstance(x, CliffordAlgebraElement) + and self.has_coerce_map_from(x.parent())): + R = self.base_ring() + return self.element_class(self, {i: R(c) for i,c in x if R(c) != R.zero()}) return super(CliffordAlgebra, self)._element_constructor_(x) @@ -1255,24 +1251,10 @@ def center_basis(self): for m,c in (Bi*Bj - Bj*Bi): d[(a, K.index(m)+k*b)] = c m = Matrix(R, d, nrows=k, ncols=k*k, sparse=True) - from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in iteritems(x)), + from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in x.items()), distinct=True) return tuple(map( from_vector, m.kernel().basis() )) - # Dense version - # R = self.base_ring() - # B = self.basis() - # K = list(B.keys()) - # eqns = [[] for dummy in range(k)] - # for a,i in enumerate(K): - # for b,j in enumerate(K): - # v = B[i]*B[j] - B[j]*B[i] - # eqns[a].extend([v[k] for k in K]) - # m = Matrix(R, eqns) - # from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in iteritems(x)), - # distinct=True) - # return tuple(map( from_vector, m.kernel().basis() )) - # Same as center except for superalgebras @cached_method def supercenter_basis(self): @@ -1352,24 +1334,10 @@ def supercenter_basis(self): for m,c in supercommutator: d[(a, K.index(m)+k*b)] = c m = Matrix(R, d, nrows=k, ncols=k*k, sparse=True) - from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in iteritems(x)), + from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in x.items()), distinct=True) return tuple(map( from_vector, m.kernel().basis() )) - # Dense version - # R = self.base_ring() - # B = self.basis() - # K = list(B.keys()) - # eqns = [[] for dummy in range(k)] - # for a,i in enumerate(K): - # for b,j in enumerate(K): - # v = B[i].supercommutator(B[j]) # or better an if-loop as above - # eqns[a].extend([v[k] for k in K]) - # m = Matrix(R, eqns) - # from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in iteritems(x)), - # distinct=True) - # return tuple(map( from_vector, m.kernel().basis() )) - Element = CliffordAlgebraElement class ExteriorAlgebra(CliffordAlgebra): @@ -2254,10 +2222,8 @@ def scalar(self, other): ##################################################################### ## Differentials -class ExteriorAlgebraDifferential(with_metaclass( - InheritComparisonClasscallMetaclass, - ModuleMorphismByLinearity, UniqueRepresentation - )): +class ExteriorAlgebraDifferential(ModuleMorphismByLinearity, + UniqueRepresentation, metaclass=InheritComparisonClasscallMetaclass): r""" Internal class to store the data of a boundary or coboundary of an exterior algebra `\Lambda(L)` defined by the structure @@ -2296,13 +2262,13 @@ def __classcall__(cls, E, s_coeff): """ d = {} - for k,v in iteritems(dict(s_coeff)): + for k, v in dict(s_coeff).items(): if not v: # Strip terms with 0 continue if isinstance(v, dict): R = E.base_ring() - v = E._from_dict({(i,): R(c) for i,c in iteritems(v)}) + v = E._from_dict({(i,): R(c) for i,c in v.items()}) else: # Make sure v is in ``E`` v = E(v) @@ -2740,7 +2706,7 @@ def __init__(self, E, s_coeff): self._cos_coeff = {} zero = E.zero() B = E.basis() - for k, v in iteritems(dict(s_coeff)): + for k, v in dict(s_coeff).items(): k = B[k] for m,c in v: self._cos_coeff[m] = self._cos_coeff.get(m, zero) + c * k diff --git a/src/sage/algebras/cluster_algebra.py b/src/sage/algebras/cluster_algebra.py index 4054bd5d9a5..770dd68b442 100644 --- a/src/sage/algebras/cluster_algebra.py +++ b/src/sage/algebras/cluster_algebra.py @@ -353,8 +353,6 @@ # **************************************************************************** from __future__ import absolute_import -from six.moves import range, map - from copy import copy from sage.categories.homset import Hom @@ -1065,7 +1063,7 @@ def mutate(self, direction, **kwargs): raise ValueError('cannot mutate in direction ' + str(k)) # store new mutation path - if to_mutate._path != [] and to_mutate._path[-1] == k: + if to_mutate._path and to_mutate._path[-1] == k: to_mutate._path.pop() else: to_mutate._path.append(k) diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index 754fe9158f6..3c71f07c980 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -72,9 +72,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six import string_types -from sage.misc.six import with_metaclass from sage.structure.unique_representation import UniqueRepresentation, CachedRepresentation from sage.structure.sage_object import SageObject from sage.misc.cachefunc import cached_method @@ -139,8 +137,9 @@ def sorting_keys(element): V = CR.V() return list(CR(V(x.basis_coefficients()))) -class Differential(with_metaclass( - InheritComparisonClasscallMetaclass, UniqueRepresentation, Morphism)): + +class Differential(UniqueRepresentation, Morphism, + metaclass=InheritComparisonClasscallMetaclass): r""" Differential of a commutative graded algebra. @@ -942,7 +941,7 @@ def __classcall__(cls, base, names=None, degrees=None, R=None, I=None): else: n = len(degrees) names = tuple('x{}'.format(i) for i in range(n)) - elif isinstance(names, string_types): + elif isinstance(names, str): names = tuple(names.split(',')) n = len(names) else: @@ -1244,6 +1243,12 @@ def _element_constructor_(self, x, coerce=True): sage: A. = GradedCommutativeAlgebra(GF(5)) sage: A({(1,3,0,1): 2, (2,2,1,2): 3}) 0 + + TESTS:: + + sage: B = A.cdg_algebra({}) + sage: B(x, coerce=False) + x """ if isinstance(x, QuotientRingElement): if x.parent() is self: @@ -2239,7 +2244,7 @@ def cohomology(self, n): TESTS: - Check that the issue discovered in :trac:`28155`is solved:: + Check that the issue discovered in :trac:`28155` is solved:: sage: A. = GradedCommutativeAlgebra(QQ) sage: B = A.cdg_algebra({e5:e1*e2+e3*e4}) @@ -2527,7 +2532,7 @@ def minimal_model(self, i=3, max_iterations=3): def extend(phi, ndegrees, ndifs, nimags, nnames): """ - Extend phi to a new algebra with new genererators, labeled by nnames + Extend phi to a new algebra with new generators, labeled by nnames """ B = phi.domain() names = [str(g) for g in B.gens()] @@ -2561,8 +2566,8 @@ def extendx(phi, degree): QI = CS.quotient(phico.image()) self._numerical_invariants[degree] = [QI.dimension()] if QI.dimension() > 0: - nnames = ['x{}_{}'.format(degree, j) for j in - range(QI.dimension())] + nnames = ['x{}_{}'.format(degree, j) + for j in range(QI.dimension())] nbasis = [] bbasis = self.basis(degree) for v in QI.basis(): @@ -2570,8 +2575,8 @@ def extendx(phi, degree): g = sum(bbasis[j] * vl[j] for j in range(len(bbasis))) nbasis.append(g) nimags = nbasis - ndegrees = [degree for j in nbasis] - return extend(phi, ndegrees, [B.zero() for nimag in nimags], + ndegrees = [degree for _ in nbasis] + return extend(phi, ndegrees, [B.zero() for _ in nimags], nimags, nnames) return phi @@ -2616,7 +2621,6 @@ def extendy(phi, degree): for j in range(len(nimags))] nnamesy += len(nimags) phi = extend(phi, ndegrees, ndifs, nimags, nnames) - B = phi.domain() if not self._minimalmodels: degnzero = 1 @@ -2631,7 +2635,7 @@ def extendy(phi, degree): names = ['x{}_{}'.format(degnzero, j) for j in range(len(gens))] A = GradedCommutativeAlgebra(self.base_ring(), names, - degrees=[degnzero for j in names]) + degrees=[degnzero for _ in names]) B = A.cdg_algebra(A.differential({})) # Solve case that fails with one generator return B,gens phi = B.hom(gens) diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py index d55fb21cf3c..a83abe8514e 100644 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py @@ -12,7 +12,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import -from six.moves import range from .finite_dimensional_algebra_element import FiniteDimensionalAlgebraElement from .finite_dimensional_algebra_ideal import FiniteDimensionalAlgebraIdeal diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx index bf0a1ffbff8..60148f063c4 100644 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx @@ -1,8 +1,7 @@ """ Elements of Finite Algebras """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2011 Johan Bosman # Copyright (C) 2011, 2013 Peter Bruin # Copyright (C) 2011 Michiel Kosters @@ -11,10 +10,8 @@ Elements of Finite Algebras # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** -from six.moves import range - +# https://www.gnu.org/licenses/ +# **************************************************************************** import re from sage.misc.lazy_attribute import lazy_attribute diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index c24add03fd7..a16cb9b3d29 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -116,7 +116,7 @@ sage: FreeAlgebra(FreeAlgebra(ZZ,2,'ab'), 2, 'x', implementation='letterplace') Traceback (most recent call last): ... - TypeError: The base ring Free Algebra on 2 generators (a, b) over Integer Ring is not a commutative ring + NotImplementedError: polynomials over Free Algebra on 2 generators (a, b) over Integer Ring are not supported in Singular """ #***************************************************************************** @@ -132,9 +132,6 @@ #***************************************************************************** from __future__ import absolute_import -from six.moves import range -from six import integer_types -import six from sage.categories.rings import Rings @@ -289,7 +286,7 @@ def create_key(self, base_ring, arg1=None, arg2=None, return tuple(degrees), R # normalise the generator names from sage.all import Integer - if isinstance(arg1, (Integer,) + integer_types): + if isinstance(arg1, (Integer, int)): arg1, arg2 = arg2, arg1 if not names is None: arg1 = names @@ -591,9 +588,9 @@ def exp_to_monomial(T): if T[i]: out.append((i%ngens,T[i])) return M(out) - return self.element_class(self, {exp_to_monomial(T):c for T,c in six.iteritems(x.letterplace_polynomial().dict())}) + return self.element_class(self, {exp_to_monomial(T):c for T,c in x.letterplace_polynomial().dict().items()}) # ok, not a free algebra element (or should not be viewed as one). - if isinstance(x, six.string_types): + if isinstance(x, str): from sage.all import sage_eval G = self.gens() d = {str(v): G[i] for i,v in enumerate(self.variable_names())} @@ -850,7 +847,7 @@ def g_algebra(self, relations, names=None, order='degrevlex', check=True): for i in range(n): for j in range(i + 1, n): cmat[i,j] = 1 - for (to_commute,commuted) in six.iteritems(relations): + for (to_commute,commuted) in relations.items(): #This is dirty, coercion is broken assert isinstance(to_commute, FreeAlgebraElement), to_commute.__class__ assert isinstance(commuted, FreeAlgebraElement), commuted diff --git a/src/sage/algebras/free_algebra_element.py b/src/sage/algebras/free_algebra_element.py index b7ee988da71..da91eda2cf8 100644 --- a/src/sage/algebras/free_algebra_element.py +++ b/src/sage/algebras/free_algebra_element.py @@ -33,10 +33,8 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function -from six import iteritems -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.monoids.free_monoid_element import FreeMonoidElement from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement from sage.structure.element import AlgebraElement @@ -170,7 +168,7 @@ def extract_from(kwds,g): # I don't start with 0, because I don't want to preclude evaluation with # arbitrary objects (e.g. matrices) because of funny coercion. result = None - for m, c in iteritems(self._monomial_coefficients): + for m, c in self._monomial_coefficients.items(): if result is None: result = c*m(x) else: diff --git a/src/sage/algebras/free_algebra_quotient.py b/src/sage/algebras/free_algebra_quotient.py index 551996793b9..13faa60d992 100644 --- a/src/sage/algebras/free_algebra_quotient.py +++ b/src/sage/algebras/free_algebra_quotient.py @@ -43,7 +43,7 @@ False """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 David Kohel # # Distributed under the terms of the GNU General Public License (GPL) @@ -56,7 +56,7 @@ # is available at: # # https://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** from sage.modules.free_module import FreeModule from sage.algebras.algebra import Algebra @@ -161,7 +161,7 @@ def _element_constructor_(self, x): EXAMPLES:: sage: H, (i,j,k) = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ) - sage: H._element_constructor_(i) is i + sage: H(i) is i True sage: a = H._element_constructor_(1); a 1 @@ -170,8 +170,6 @@ def _element_constructor_(self, x): sage: a = H._element_constructor_([1,2,3,4]); a 1 + 2*i + 3*j + 4*k """ - if isinstance(x, FreeAlgebraQuotientElement) and x.parent() is self: - return x return self.element_class(self,x) def _coerce_map_from_(self,S): @@ -233,8 +231,7 @@ def gen(self, i): raise IndexError("Argument i (= %s) must be between 0 and %s."%(i, n-1)) R = self.base_ring() F = self.__free_algebra.monoid() - n = self.__ngens - return self.element_class(self,{F.gen(i):R(1)}) + return self.element_class(self, {F.gen(i): R.one()}) def ngens(self): """ diff --git a/src/sage/algebras/free_algebra_quotient_element.py b/src/sage/algebras/free_algebra_quotient_element.py index a44d42e8f85..9e291010fa2 100644 --- a/src/sage/algebras/free_algebra_quotient_element.py +++ b/src/sage/algebras/free_algebra_quotient_element.py @@ -21,9 +21,8 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six import integer_types -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.structure.element import RingElement, AlgebraElement from sage.structure.parent_gens import localvars from sage.structure.richcmp import richcmp @@ -33,9 +32,6 @@ from sage.algebras.free_algebra_element import FreeAlgebraElement -import six - - def is_FreeAlgebraQuotientElement(x): """ EXAMPLES:: @@ -79,7 +75,7 @@ def __init__(self, A, x): if isinstance(x, FreeAlgebraQuotientElement) and x.parent() == Q: self.__vector = Q.module()(x.vector()) return - if isinstance(x, (Integer,) + integer_types): + if isinstance(x, (Integer, int)): self.__vector = Q.module().gen(0) * x return elif isinstance(x, FreeModuleElement) and x.parent() is Q.module(): @@ -93,7 +89,7 @@ def __init__(self, A, x): F = A.monoid() B = A.monomial_basis() - if isinstance(x, (Integer,) + integer_types): + if isinstance(x, (Integer, int)): self.__vector = x*M.gen(0) elif isinstance(x, RingElement) and not isinstance(x, AlgebraElement) and x in R: self.__vector = x * M.gen(0) @@ -111,11 +107,11 @@ def __init__(self, A, x): # Need to do more work here to include monomials not # represented in the monomial basis. self.__vector = M(0) - for m, c in six.iteritems(x._FreeAlgebraElement__monomial_coefficients): + for m, c in x._FreeAlgebraElement__monomial_coefficients.items(): self.__vector += c*M.gen(B.index(m)) elif isinstance(x, dict): self.__vector = M(0) - for m, c in six.iteritems(x): + for m, c in x.items(): self.__vector += c*M.gen(B.index(m)) elif isinstance(x, AlgebraElement) and x.parent().ambient_algebra() is A: self.__vector = x.ambient_algebra_element().vector() diff --git a/src/sage/algebras/free_zinbiel_algebra.py b/src/sage/algebras/free_zinbiel_algebra.py index 401de9f35c6..63cd7023e57 100644 --- a/src/sage/algebras/free_zinbiel_algebra.py +++ b/src/sage/algebras/free_zinbiel_algebra.py @@ -23,6 +23,7 @@ IdentityConstructionFunctor) from sage.categories.rings import Rings from sage.categories.functor import Functor +from sage.categories.sets_cat import Sets from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.words.words import Words from sage.combinat.words.alphabet import Alphabet @@ -39,7 +40,7 @@ class FreeZinbielAlgebra(CombinatorialFreeModule): .. MATH:: - a \circ (b \circ c) = a \circ (b \circ c) + a \circ (c \circ b). + (a \circ b) \circ c = a \circ (b \circ c) + a \circ (c \circ b). Zinbiel algebras were first introduced by Loday (see [Lod1995]_ and [LV2012]_) as the Koszul dual to Leibniz algebras (hence the name @@ -113,7 +114,7 @@ class FreeZinbielAlgebra(CombinatorialFreeModule): sage: x*(y*z) + x*(z*y) Z[xyz] + Z[xzy] - We see that the Zinbiel algebra is not associative, nor even + We see that the Zinbiel algebra is not associative, not even power associative:: sage: x*(y*z) @@ -123,7 +124,7 @@ class FreeZinbielAlgebra(CombinatorialFreeModule): sage: (x*x)*x 2*Z[xxx] - We verify that it is a divided powers algebra:: + We verify that it is a divided power algebra:: sage: (x*(x*x)) * (x*(x*(x*x))) 15*Z[xxxxxxx] @@ -143,6 +144,11 @@ class FreeZinbielAlgebra(CombinatorialFreeModule): sage: Z.basis().keys() Finite words over {'x', 'y', 'z'} + sage: A = algebras.FreeZinbiel(QQ,'z2,z3') + sage: x, y = A.gens() + sage: x*y + Z[z2,z3] + REFERENCES: - :wikipedia:`Zinbiel_algebra` @@ -152,7 +158,7 @@ class FreeZinbielAlgebra(CombinatorialFreeModule): - [LV2012]_ """ @staticmethod - def __classcall_private__(cls, R, n=None, names=None): + def __classcall_private__(cls, R, n=None, names=None, prefix=None): """ Standardize input to ensure a unique representation. @@ -169,6 +175,8 @@ def __classcall_private__(cls, R, n=None, names=None): Free Zinbiel algebra on generators (Z[x], Z[y]) over Rational Field sage: algebras.FreeZinbiel(QQ, ('x', 'y')) Free Zinbiel algebra on generators (Z[x], Z[y]) over Rational Field + + sage: Z = algebras.FreeZinbiel(QQ, ZZ) """ if isinstance(n, (list, tuple)): names = n @@ -182,10 +190,14 @@ def __classcall_private__(cls, R, n=None, names=None): n = len(names) if R not in Rings(): raise TypeError("argument R must be a ring") + if prefix is None: + prefix = 'Z' superclass = super(FreeZinbielAlgebra, cls) - return superclass.__classcall__(cls, R, n, tuple(names)) + if names is None: + return superclass.__classcall__(cls, R, n, None, prefix) + return superclass.__classcall__(cls, R, n, tuple(names), prefix) - def __init__(self, R, n, names): + def __init__(self, R, n, names, prefix): """ Initialize ``self``. @@ -194,21 +206,33 @@ def __init__(self, R, n, names): sage: Z. = algebras.FreeZinbiel(QQ) sage: TestSuite(Z).run() + sage: Z = algebras.FreeZinbiel(QQ, ZZ) + sage: G = Z.algebra_generators() + sage: TestSuite(Z).run(elements=[Z.an_element(), G[1], G[1]*G[2]*G[0]]) + TESTS:: sage: Z. = algebras.FreeZinbiel(5) Traceback (most recent call last): ... TypeError: argument R must be a ring + + sage: algebras.FreeZinbiel(QQ, ['x', 'y'], prefix='f') + Free Zinbiel algebra on generators (f[x], f[y]) over Rational Field """ if R not in Rings(): raise TypeError("argument R must be a ring") - indices = Words(Alphabet(n, names=names), infinite=False) + if names is None: + indices = Words(Alphabet(n), infinite=False) + self._n = None + else: + indices = Words(Alphabet(n, names=names), infinite=False) + self._n = n cat = MagmaticAlgebras(R).WithBasis().Graded() - self._n = n - CombinatorialFreeModule.__init__(self, R, indices, prefix='Z', + CombinatorialFreeModule.__init__(self, R, indices, prefix=prefix, category=cat) - self._assign_names(names) + if self._n is not None: + self._assign_names(names) def _repr_term(self, t): """ @@ -231,7 +255,14 @@ def _repr_(self): sage: Z. = algebras.FreeZinbiel(QQ) sage: Z Free Zinbiel algebra on generators (Z[x], Z[y]) over Rational Field + + sage: Z = algebras.FreeZinbiel(QQ, ZZ) + sage: Z + Free Zinbiel algebra on generators indexed by Integer Ring over Rational Field """ + if self._n is None: + return "Free Zinbiel algebra on generators indexed by {} over {}".format( + self._indices.alphabet(), self.base_ring()) return "Free Zinbiel algebra on generators {} over {}".format( self.gens(), self.base_ring()) @@ -246,8 +277,11 @@ def algebra_generators(self): sage: list(Z.algebra_generators()) [Z[x], Z[y], Z[z]] """ - A = self.variable_names() - return Family(A, lambda g: self.monomial(self._indices(g))) + if self._n is None: + A = self._indices.alphabet() + else: + A = self.variable_names() + return Family(A, lambda g: self.monomial(self._indices([g]))) def change_ring(self, R): """ @@ -278,6 +312,8 @@ def gens(self): sage: Z.gens() (Z[x], Z[y], Z[z]) """ + if self._n is None: + return self.algebra_generators() return tuple(self.algebra_generators()) def degree_on_basis(self, t): @@ -317,7 +353,7 @@ def product_on_basis(self, x, y): """ if not x: return self.monomial(y) - x0 = self._indices(x[0]) + x0 = self._indices([x[0]]) return self.sum_of_monomials(x0 + sh for sh in x[1:].shuffle(y)) def _element_constructor_(self, x): @@ -418,23 +454,32 @@ def _coerce_map_from_(self, R): True sage: F._coerce_map_from_(H) True - sage: F._coerce_map_from_(QQ) + sage: F._coerce_map_from_(QQ) is None + True + sage: G._coerce_map_from_(QQ) is None + True + sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False - sage: G._coerce_map_from_(QQ) + + sage: I = algebras.FreeZinbiel(ZZ, ZZ) + sage: F._coerce_map_from_(I) False - sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) + sage: I._coerce_map_from_(F) False """ # free Zinbiel algebras in a subset of variables # over any base that coerces in: - return (isinstance(R, FreeZinbielAlgebra) - and all(x in self.variable_names() for x in R.variable_names()) - and self.base_ring().has_coerce_map_from(R.base_ring())) + if isinstance(R, FreeZinbielAlgebra): + if self._n is None or R._n is None: + return False + return (all(x in self.variable_names() for x in R.variable_names()) + and self.base_ring().has_coerce_map_from(R.base_ring())) + return super(FreeZinbielAlgebra, self)._coerce_map_from_(R) def construction(self): """ Return a pair ``(F, R)``, where ``F`` is a :class:`ZinbielFunctor` - and `R` is a ring, such that ``F(R)`` returns ``self``. + and ``R`` is a ring, such that ``F(R)`` returns ``self``. EXAMPLES:: @@ -571,15 +616,31 @@ def __mul__(self, other): EXAMPLES:: - sage: functor = sage.algebras.free_zinbiel_algebra.ZinbielFunctor + sage: from sage.algebras.free_zinbiel_algebra import ZinbielFunctor as functor sage: F = functor(['x','y']) sage: G = functor(['t']) sage: G * F Zinbiel[x,y,t] + + With an infinite generating set:: + + sage: H = functor(ZZ) + sage: H * G + Traceback (most recent call last): + ... + CoercionException: Unable to determine overlap for infinite sets + sage: G * H + Traceback (most recent call last): + ... + CoercionException: Unable to determine overlap for infinite sets """ if isinstance(other, IdentityConstructionFunctor): return self if isinstance(other, ZinbielFunctor): + def check(x): + return isinstance(x, (list, tuple)) or x in Sets().Finite() + if not check(self.vars) or not check(other.vars): + raise CoercionException("Unable to determine overlap for infinite sets") if set(self.vars).intersection(other.vars): raise CoercionException("Overlapping variables (%s,%s)" % (self.vars, other.vars)) @@ -593,7 +654,7 @@ def __mul__(self, other): def merge(self, other): """ - Merge ``self`` with another construction functor, or return None. + Merge ``self`` with another construction functor, or return ``None``. EXAMPLES:: @@ -605,6 +666,16 @@ def merge(self, other): sage: F.merge(F) Zinbiel[x,y] + With an infinite generating set:: + + sage: H = functor(ZZ) + sage: H.merge(H) is H + True + sage: H.merge(F) is None + True + sage: F.merge(H) is None + True + Now some actual use cases:: sage: R = algebras.FreeZinbiel(ZZ, 'x,y,z') @@ -626,6 +697,10 @@ def merge(self, other): if isinstance(other, ZinbielFunctor): if self.vars == other.vars: return self + def check(x): + return isinstance(x, (list, tuple)) or x in Sets().Finite() + if not check(self.vars) or not check(other.vars): + return None ret = list(self.vars) cur_vars = set(ret) for v in other.vars: @@ -643,3 +718,4 @@ def _repr_(self): Zinbiel[x,y,z,t] """ return "Zinbiel[%s]" % ','.join(self.vars) + diff --git a/src/sage/algebras/hecke_algebras/__init__.py b/src/sage/algebras/hecke_algebras/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/algebras/hecke_algebras/all.py b/src/sage/algebras/hecke_algebras/all.py new file mode 100644 index 00000000000..183ec37afa6 --- /dev/null +++ b/src/sage/algebras/hecke_algebras/all.py @@ -0,0 +1,5 @@ +""" +Hecke Algebras +""" +#from sage.misc.lazy_import import lazy_import + diff --git a/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py b/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py new file mode 100644 index 00000000000..b78b7691e7b --- /dev/null +++ b/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py @@ -0,0 +1,1763 @@ +# -*- coding: utf-8 -*- +r""" +Ariki-Koike Algebras + +The *Ariki-Koike algebras* were introduced by Ariki and Koike [AK1994]_ as +a natural generalization of the Iwahori-Hecke algebras of types `A` and `B` +(see :class:`~sage.algebras.iwahori_hecke_algebra.IwahoriHeckeAlgebra`). +Soon afterwards, Broué and Malle defined analogues of the Hecke +algebras for all complex reflection groups + +Fix non-negative integers `r` an `n`. The Ariki-Koike algebras are +deformations of the group algebra of the complex reflection group +`G(r, 1, n) = \ZZ / r\ZZ \wr \mathfrak{S}_n`. If `R` is a ring containing a +*Hecke parameter* `q` and *cyclotomic parameters* `u_0, \ldots, u_{r-1}` then +the Ariki-Koike algebra `H_n(q, u_1, \ldots, u_r)` is the unital associative +`r`-algebra with generators `T_0, T_1, \ldots, T_{n-1}` an relations: + +.. MATH:: + + \begin{aligned} + \prod_{i=0}^{r-1} (T_0 - u_i) & = 0, \\ + T_i^2 & = (q - 1) T_i + q && \text{for } 1 \leq i < n, \\ + T_0 T_1 T_0 T_1 & = T_1 T_0 T_1 T_0, \\ + T_i T_j & = T_j T_i && \text{if } |i - j| \geq 2, \\ + T_i T_{i+1} T_i & = T_{i+1} T_i T_{i+1} && \text{for } 1 \leq i < n. + \end{aligned} + +AUTHORS: + +- Travis Scrimshaw (2016-04): initial version +- Andrew Mathas (2016-07): improved multiplication code + +REFERENCES: + +- [AK1994]_ +- [BM1993]_ +- [MM1998]_ +""" + +#***************************************************************************** +# Copyright (C) 2016-2018 Travis Scrimshaw +# 2016-2018 Andrew Mathas +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from __future__ import absolute_import, print_function, division + +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.misc_c import prod +from sage.misc.bindable_class import BindableClass +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.algebras import Algebras +from sage.categories.rings import Rings +from sage.categories.realizations import Realizations, Category_realization_of_parent +from sage.categories.cartesian_product import cartesian_product +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing +from sage.rings.integer_ring import ZZ +from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.permutation import Permutations +from sage.sets.family import Family +from sage.data_structures.blas_dict import iaxpy + +# ABC for basis classes +class _Basis(CombinatorialFreeModule, BindableClass): + r""" + Abstract base class for bases of the Ariki-Koike algebra. + """ + def __init__(self, algebra, prefix='AK'): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(2, 3).LT() + sage: TestSuite(LT).run() + """ + self._r = algebra._r + self._n = algebra._n + self._q = algebra._q + self._u = algebra._u + # It seems more efficient to copy this as we need it a lot + self._zero_tuple = tuple([0] * self._n) + self._Pn = Permutations(self._n) + self._one_perm = self._Pn.one() + C = cartesian_product([range(self._r)] * self._n) + indices = cartesian_product([C, self._Pn]) + CombinatorialFreeModule.__init__(self, algebra.base_ring(), indices, + prefix=prefix, + category=algebra._BasesCategory()) + + @cached_method + def one_basis(self): + r""" + Return the index of the basis element of `1`. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(5, 3).LT() + sage: LT.one_basis() + ((0, 0, 0), [1, 2, 3]) + + sage: T = algebras.ArikiKoike(5, 3).T() + sage: T.one_basis() + ((0, 0, 0), [1, 2, 3]) + """ + return (self._zero_tuple, self._one_perm) + +class ArikiKoikeAlgebra(Parent, UniqueRepresentation): + r""" + The Ariki-Koike algebra `H_{r,n}(q, u)`. + + Let `R` be an unital integral domain. + Let `q, u_0, \ldots, u_{r-1} \in R` such that `q^{-1} \in R`. + The *Ariki-Koike algebra* is the unital associative algebra + `H_{r,n}(q, u)` generated by `T_0, \ldots, T_{n-1}` that satisfies + the following relations: + + .. MATH:: + + \begin{aligned} + \prod_{i=0}^{r-1} (T_0 - u_i) & = 0, \\ + T_i^2 & = (q - 1) T_i + q && \text{for } 1 \leq i < n, \\ + T_0 T_1 T_0 T_1 & = T_1 T_0 T_1 T_0, \\ + T_i T_j & = T_j T_i && \text{if } |i - j| \geq 2, \\ + T_i T_{i+1} T_i & = T_{i+1} T_i T_{i+1} && \text{for } 1 \leq i < n. + \end{aligned} + + The parameter `q` is called the *Hecke parameter* and the parameters + `u_0, \ldots, u_{r-1}` are called the *cyclotomic parameters*. + Thus, the Ariki-Koike algebra is a deformation of the group algebra of the + complex reflection group `G(r, 1, n) = \ZZ / r\ZZ \wr \mathfrak{S}_n`. + + Next, we define *Jucys-Murphy elements* + + .. MATH:: + + L_i = q^{-i+1} T_{i-1} \cdots T_1 T_0 T_1 \cdots T_{i-1} + + for `1 \leq i \leq n`. + + .. NOTE:: + + These element differ by a power of `q` from the corresponding + elements in [AK1994]_. However, these elements are more commonly + used because they lead to nicer representation theoretic formulas. + + Ariki and Koike [AK1994]_ showed that `H_{r,n}(q, u)` is a free + `R`-module with a basis given by + + .. MATH:: + + \{ L_1^{c_i} \cdots L_n^{c_n} T_w \mid w \in S_n, 0 \leq c_i < r \}. + + In particular, we have `\dim H_{r,n}(q,u) = r^n n! = |G(r, 1, n)|`. + Moreover, we have `L_i L_j = L_i L_j` for all `1 \leq i, j \leq n`. + + The Ariki-Koike algebra `H_{r,n}(q, u)` can be considered as a quotient + of the group algebra of the braid group for `G(r, 1, n)` by the ideal + generated by `\prod_{i=0}^{r-1} (T_0 - u_i)` and `(T_i - q)(T_i + 1)`. + Furthermore, `H_{r,n}(q, u)` can be constructed as a quotient of the + extended affine Hecke algebra of type `A_{n-1}^{(1)}` by + `\prod_{i=0}^{r-1} (X_1 - u_i)`. + + Since the Ariki-Koike algebra is a quotient of the group + algebra of the braid group of `G(r, 1, n)`, we can recover + the group algebra of `G(r, 1, n)` as follows. Consider + `u = (1, \zeta_r, \ldots, \zeta_r^{r-1})`, where `\zeta_r` + is a primitive `r`-th root of unity, then we have + + .. MATH:: + + R G(r, 1, n) = H_{r,n}(1, u). + + INPUT: + + - ``r`` -- the maximum power of `L_i` + - ``n`` -- the rank `S_n` + - ``q`` -- (optional) an invertible element in a commutative ring; + the default is `q \in R[q,q^{-1}]`, where `R` is the ring containing + the variables ``u`` + - ``u`` -- (optional) the variables `u_1, \ldots, u_r`; the + default is the generators of `\ZZ[u_1, \ldots, u_r]` + - ``R`` -- (optional) a commutative ring containing ``q`` and ``u``; + the default is the parent of `q` and `u_1, \ldots, u_r` + + EXAMPLES: + + We start by constructing an Ariki-Koike algebra where the + values `q, u` are generic and do some computations:: + + sage: H = algebras.ArikiKoike(3, 4) + + Next, we do some computations using the `LT` basis:: + + sage: LT = H.LT() + sage: LT.inject_variables() + Defining L1, L2, L3, L4, T1, T2, T3 + sage: T1 * T2 * T1 * T2 + q*T[2,1] - (1-q)*T[2,1,2] + sage: T1 * L1 * T2 * L3 * T1 * T2 + -(q-q^2)*L2*L3*T[2] + q*L1*L2*T[2,1] - (1-q)*L1*L2*T[2,1,2] + sage: L1^3 + u0*u1*u2 + ((-u0*u1-u0*u2-u1*u2))*L1 + ((u0+u1+u2))*L1^2 + sage: L3 * L2 * L1 + L1*L2*L3 + sage: u = LT.u() + sage: q = LT.q() + sage: (q + 2*u[0]) * (T1 * T2) * L3 + (-2*u0+(2*u0-1)*q+q^2)*L3*T[1] + (-2*u0+(2*u0-1)*q+q^2)*L2*T[2] + + (2*u0+q)*L1*T[1,2] + + We check the defining relations:: + + sage: prod(L1 - val for val in u) == H.zero() + True + sage: L1 * T1 * L1 * T1 == T1 * L1 * T1 * L1 + True + sage: T1 * T2 * T1 == T2 * T1 * T2 + True + sage: T2 * T3 * T2 == T3 * T2 * T3 + True + sage: L2 == q^-1 * T1 * L1 * T1 + True + sage: L3 == q^-2 * T2 * T1 * L1 * T1 * T2 + True + + We construct an Ariki-Koike algebra with `u = (1, \zeta_3, \zeta_3^2)`, + where `\zeta_3` is a primitive third root of unity:: + + sage: F = CyclotomicField(3) + sage: zeta3 = F.gen() + sage: R. = LaurentPolynomialRing(F) + sage: H = algebras.ArikiKoike(3, 4, q=q, u=[1, zeta3, zeta3^2], R=R) + sage: H.LT().inject_variables() + Defining L1, L2, L3, L4, T1, T2, T3 + sage: L1^3 + 1 + sage: L2^3 + 1 - (q^-1-1)*T[1] - (q^-1-1)*L1*L2^2*T[1] - (q^-1-1)*L1^2*L2*T[1] + + Next, we additionally take `q = 1` to obtain the group algebra + of `G(r, 1, n)`:: + + sage: F = CyclotomicField(3) + sage: zeta3 = F.gen() + sage: H = algebras.ArikiKoike(3, 4, q=1, u=[1, zeta3, zeta3^2], R=F) + sage: LT = H.LT() + sage: LT.inject_variables() + Defining L1, L2, L3, L4, T1, T2, T3 + sage: A = ColoredPermutations(3, 4).algebra(F) + sage: s1, s2, s3, s0 = list(A.algebra_generators()) + sage: all(L^3 == LT.one() for L in LT.L()) + True + sage: J = [s0, s3*s0*s3, s2*s3*s0*s3*s2, s1*s2*s3*s0*s3*s2*s1] + sage: all(Ji^3 == A.one() for Ji in J) + True + """ + @staticmethod + def __classcall_private__(cls, r, n, q=None, u=None, R=None): + r""" + Standardize input to ensure a unique representation. + + TESTS:: + + sage: H1 = algebras.ArikiKoike(4, 3) + sage: S = PolynomialRing(ZZ, 'u', 4) + sage: R. = LaurentPolynomialRing(S) + sage: H2 = algebras.ArikiKoike(4, 3, q=q) + sage: H3 = algebras.ArikiKoike(4, 3, q, S.gens(), R) + sage: H1 is H2 + True + sage: H2 is H3 + True + """ + if u is None: + if q is not None: + R = q.parent() + if R is None: + R = PolynomialRing(ZZ, 'u', r) + u = R.gens() + if q is None: + R = LaurentPolynomialRing(R, 'q') + q = R.gen() + else: + u = PolynomialRing(ZZ, 'u', r).gens() + if q is None: + q = 'q' + else: + if not isinstance(u, (list,tuple)): + u = [u]*r + if R is None: + from sage.structure.element import get_coercion_model + cm = get_coercion_model() + if q is None: + R = cm.common_parent(*[val.parent() for val in u]) + R = LaurentPolynomialRing(R, 'q') + q = R.gen() + else: + R = cm.common_parent(q.parent(), *[val.parent() for val in u]) + elif q is None: + q = 'q' + u = [R(val) for val in u] + if R not in Rings().Commutative(): + raise TypeError("base ring must be a commutative ring") + q = R(q) + u = tuple(u) + return super(ArikiKoikeAlgebra, cls).__classcall__(cls, r, n, q, u, R) + + def __init__(self, r, n, q, u, R): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(5, 3) + sage: TestSuite(H).run() + sage: H = algebras.ArikiKoike(1, 4) + sage: TestSuite(H).run() + sage: H = algebras.ArikiKoike(2, 3) + sage: TestSuite(H).run() + sage: H = algebras.ArikiKoike(3, 4) + sage: TestSuite(H).run() # long time + """ + self._r = r + self._n = n + self._q = q + self._u = u + self._category = Algebras(R).FiniteDimensional().WithBasis() + Parent.__init__(self, base=R, category=self._category.WithRealizations()) + + T = self.T() + LT = self.LT() + T.module_morphism(LT._from_T_basis, codomain=LT).register_as_coercion() + LT.module_morphism(T._from_LT_basis, codomain=T).register_as_coercion() + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: algebras.ArikiKoike(5, 2) + Ariki-Koike algebra of rank 5 and order 2 + with q=q and u=(u0, u1, u2, u3, u4) + over Univariate Laurent Polynomial Ring in q + over Multivariate Polynomial Ring in u0, u1, u2, u3, u4 + over Integer Ring + """ + return "Ariki-Koike algebra of rank {} and order {} with q={} and u={} over {}".format( + self._r, self._n, self._q, self._u, self.base_ring()) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(5, 2) + sage: latex(H) + \mathcal{H}_{5,2}(q) + """ + return "\\mathcal{H}_{%s,%s}(%s)"%(self._r, self._n, self._q) + + def hecke_parameter(self): + r""" + Return the Hecke parameter `q` of ``self``. + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(5, 3) + sage: H.hecke_parameter() + q + """ + return self._q + + q = hecke_parameter + + def cyclotomic_parameters(self): + r""" + Return the cyclotomic parameters `u` of ``self``. + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(5, 3) + sage: H.cyclotomic_parameters() + (u0, u1, u2, u3, u4) + """ + return self._u + + u = cyclotomic_parameters + + def a_realization(self): + r""" + Return a realization of ``self``. + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(5, 2) + sage: H.a_realization() + Ariki-Koike algebra of rank 5 and order 2 + with q=q and u=(u0, u1, u2, u3, u4) ... in the LT-basis + """ + return self.LT() + + class _BasesCategory(Category_realization_of_parent): + r""" + The category of bases of a Ariki-Koike algebra. + """ + def __init__(self, base): + r""" + Initialize ``self``. + + INPUT: + + - ``base`` -- a Ariki-Koike algebra + + TESTS:: + + sage: H = algebras.ArikiKoike(5, 2) + sage: bases = H._BasesCategory() + sage: H.T() in bases + True + """ + Category_realization_of_parent.__init__(self, base) + + def super_categories(self): + r""" + The super categories of ``self``. + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(5, 2) + sage: bases = H._BasesCategory() + sage: bases.super_categories() + [Category of realizations of Ariki-Koike algebra of rank 5 and order 2 + with q=q and u=(u0, u1, u2, u3, u4) over ..., + Category of finite dimensional algebras with basis over ...] + + """ + return [Realizations(self.base()), self.base()._category] + + def _repr_(self): + r""" + Return the representation of ``self``. + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(5, 2) + sage: H._BasesCategory() + Category of bases of Ariki-Koike algebra of rank 5 and order 2 + with q=q and u=(u0, u1, u2, u3, u4) over ... + """ + return "Category of bases of %s" % self.base() + + class ParentMethods: + r""" + This class collects code common to all the various bases. In most + cases, these are just default implementations that will get + specialized in a basis. + """ + def _repr_(self): + r""" + Text representation of this basis of Iwahori-Hecke algebra. + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(5, 2) + sage: H.T() + Ariki-Koike algebra of rank 5 and order 2 + with q=q and u=(u0, u1, u2, u3, u4) ... in the T-basis + sage: H.LT() + Ariki-Koike algebra of rank 5 and order 2 + with q=q and u=(u0, u1, u2, u3, u4) ... in the LT-basis + """ + return "%s in the %s-basis"%(self.realization_of(), self._realization_name()) + + def hecke_parameter(self): + r""" + Return the Hecke parameter `q` of ``self``. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(5, 3).LT() + sage: LT.hecke_parameter() + q + """ + return self._q + + q = hecke_parameter + + def cyclotomic_parameters(self): + r""" + Return the cyclotomic parameters `u` of ``self``. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(5, 3).LT() + sage: LT.cyclotomic_parameters() + (u0, u1, u2, u3, u4) + """ + return self._u + + u = cyclotomic_parameters + + @cached_method + def gens(self): + r""" + Return the generators of ``self``. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(5, 3).LT() + sage: LT.gens() + (L1, L2, L3, T[1], T[2]) + """ + return tuple(self.algebra_generators()) + + def dimension(self): + r""" + Return the dimension of ``self``. + + The dimension of `H_{r,n}(q, u)` is `r^n n!`. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(8, 3).LT() + sage: LT.dimension() + 3072 + sage: LT = algebras.ArikiKoike(6, 3).LT() + sage: LT.dimension() + 1296 + sage: LT = algebras.ArikiKoike(3, 5).LT() + sage: LT.dimension() + 29160 + """ + from sage.functions.other import factorial + return self._r**self._n * factorial(self._n) + + def some_elements(self): + r""" + Return a list of elements of ``self``. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(4, 3).LT() + sage: LT.some_elements() + [1 + 2*T[2] + 3*T[1] + T[2,1], + L1, L2, L3, T[1], T[2], L1^2, L2^2] + """ + G = self.algebra_generators() + elts = [self.an_element()] + list(G) + elts += [self.L(1)**2] + if self._n > 1: + elts += [self.L(2)**(self._r//2)] + return elts + + # ----------------------------------------------------- + # Basis classes + # ----------------------------------------------------- + + class LT(_Basis): + r""" + The basis of the Ariki-Koike algebra given by monomials of the + form `L T`, where `L` is product of Jucys-Murphy elements and + `T` is a product of `\{ T_i | 0 < i < n \}`. + + This was the basis defined in [AK1994]_ except using the + renormalized Jucys-Murphy elements. + """ + def __init__(self, algebra): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(5, 3).LT() + sage: TestSuite(LT).run() + sage: LT = algebras.ArikiKoike(1, 4).LT() + sage: TestSuite(LT).run() + sage: LT = algebras.ArikiKoike(2, 3).LT() + sage: TestSuite(LT).run() + sage: LT = algebras.ArikiKoike(3, 4).LT() + sage: TestSuite(LT).run() # long time + """ + _Basis.__init__(self, algebra, prefix='LT') + self._assign_names(self.algebra_generators().keys()) + + def _repr_term(self, m): + r""" + Return a string representation of the basis element indexed by ``m``. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(4, 3).LT() + sage: LT._repr_term( ((1, 0, 2), Permutation([3,2,1])) ) + 'L1*L3^2*T[2,1,2]' + """ + gen_str = lambda e: '' if e == 1 else '^%s'%e + lhs = '*'.join('L%s'%(j+1) + gen_str(i) + for j,i in enumerate(m[0]) if i > 0) + redword = m[1].reduced_word() + if not redword: + if not lhs: + return '1' + return lhs + rhs = 'T[{}]'.format(','.join(str(i) for i in redword)) + if not lhs: + return rhs + return lhs + '*' + rhs + + def _latex_term(self, m): + r""" + Return a latex representation for the basis element indexed by ``m``. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(4, 3).LT() + sage: LT._latex_term( ((1, 0, 2), Permutation([3,2,1])) ) + 'L_{1} L_{3}^{2} T_{2} T_{1} T_{2}' + """ + gen_str = lambda e: '' if e == 1 else '^{%s}'%e + lhs = ' '.join('L_{%s}'%(j+1) + gen_str(i) + for j,i in enumerate(m[0]) if i > 0) + redword = m[1].reduced_word() + if not redword: + if not lhs: + return '1' + return lhs + return lhs + ' ' + ' '.join("T_{%d}"%i for i in redword) + + def _from_T_basis(self, t): + r""" + Return the image of the `T` basis element indexed + by ``t`` in ``self``. + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(3, 3) + sage: LT = H.LT() + sage: T = H.T() + sage: all(LT(Li) == LT.L(i+1) for i,Li in enumerate(T.L())) + True + sage: all(LT(Ti) == LT.T(i) for i,Ti in enumerate(T.T())) + True + sage: all(LT(T(b)) == b for b in LT.basis()) # long time + True + + sage: H = algebras.ArikiKoike(1, 3) + sage: LT = H.LT() + sage: T = H.T() + sage: all(LT(Li) == LT.L(i+1) for i,Li in enumerate(T.L())) + True + sage: all(LT(T(b)) == b for b in LT.basis()) # indirect doctest + True + """ + # Compute the corresponding reduced word for the first part + ret = self.one() + T = list(self._zero_tuple) + one = self.base_ring().one() + for i,k in enumerate(t[0]): + if k == 0: + continue + perm = self._Pn.prod(self._Pn.simple_reflection(j) + for j in range(1,i+1)) + ret = ret * self._from_dict({(self._zero_tuple, perm): one}, + remove_zeros=False, coerce=False) + T[0] = k + ret = ret * self._from_dict({(tuple(T), self._one_perm): one}, + remove_zeros=False, coerce=False) + + return ret * self._from_dict({(self._zero_tuple, t[1]): one}, + remove_zeros=False, coerce=False) + + @cached_method + def algebra_generators(self): + r""" + Return the algebra generators of ``self``. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(5, 3).LT() + sage: dict(LT.algebra_generators()) + {'L1': L1, 'L2': L2, 'L3': L3, 'T1': T[1], 'T2': T[2]} + + sage: LT = algebras.ArikiKoike(1, 4).LT() + sage: dict(LT.algebra_generators()) + {'T1': T[1], 'T2': T[2], 'T3': T[3]} + """ + d = {} + if self._r != 1: + for i in range(self._n): + r = list(self._zero_tuple) # Make a copy + r[i] = 1 + d['L%s'%(i+1)] = self.monomial( (tuple(r), self._one_perm) ) + G = self._Pn.group_generators() + for i in range(1, self._n): + d['T%s'%i] = self.monomial( (self._zero_tuple, G[i]) ) + return Family(sorted(d), lambda i: d[i]) + + def T(self, i=None): + r""" + Return the generator(s) `T_i` of ``self``. + + INPUT: + + - ``i`` -- (default: ``None``) the generator `T_i` or + if ``None``, then the list of all generators `T_i` + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(8, 3).LT() + sage: LT.T(1) + T[1] + sage: LT.T() + [L1, T[1], T[2]] + sage: LT.T(0) + L1 + """ + G = self.algebra_generators() + if i is None: + return [G['L1']] + [G['T%s'%j] for j in range(1, self._n)] + if i == 0: + return G['L1'] + return G['T%s'%i] + + def L(self, i=None): + r""" + Return the generator(s) `L_i`. + + INPUT: + + - ``i`` -- (default: ``None``) the generator `L_i` or + if ``None``, then the list of all generators `L_i` + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(8, 3).LT() + sage: LT.L(2) + L2 + sage: LT.L() + [L1, L2, L3] + + sage: LT = algebras.ArikiKoike(1, 3).LT() + sage: LT.L(2) + u + (-u*q^-1+u)*T[1] + sage: LT.L() + [u, + u + (-u*q^-1+u)*T[1], + u + (-u*q^-1+u)*T[2] + (-u*q^-2+u*q^-1)*T[2,1,2]] + """ + G = self.algebra_generators() + if i is None: + if self._r == 1: + return [self._Li_power(j, 1) for j in range(1, self._n+1)] + return [G['L%s'%j] for j in range(1, self._n+1)] + if self._r == 1: + return self._Li_power(i, 1) + return G['L%s'%i] + + @cached_method + def product_on_basis(self, m1, m2): + r""" + Return the product of the basis elements indexed + by ``m1`` and ``m2``. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(6, 3).LT() + sage: m = ((1, 0, 2), Permutations(3)([2,1,3])) + sage: LT.product_on_basis(m, m) + q*L1*L2*L3^4 + + sage: LT = algebras.ArikiKoike(4, 3).LT() + sage: L1,L2,L3,T1,T2 = LT.algebra_generators() + sage: L1 * T1 * L1^2 * T1 + q*L1*L2^2 + (1-q)*L1^2*L2*T[1] + sage: L1^2 * T1 * L1^2 * T1 + q*L1^2*L2^2 + (1-q)*L1^3*L2*T[1] + sage: L1^3 * T1 * L1^2 * T1 + (-u0*u1*u2*u3+u0*u1*u2*u3*q)*L2*T[1] + + ((u0*u1*u2+u0*u1*u3+u0*u2*u3+u1*u2*u3)+(-u0*u1*u2-u0*u1*u3-u0*u2*u3-u1*u2*u3)*q)*L1*L2*T[1] + + ((-u0*u1-u0*u2-u1*u2-u0*u3-u1*u3-u2*u3)+(u0*u1+u0*u2+u1*u2+u0*u3+u1*u3+u2*u3)*q)*L1^2*L2*T[1] + + ((u0+u1+u2+u3)+(-u0-u1-u2-u3)*q)*L1^3*L2*T[1] + q*L1^3*L2^2 + + sage: L1^2 * T1 * L1^3 * T1 + (-u0*u1*u2*u3+u0*u1*u2*u3*q)*L2*T[1] + + ((u0*u1*u2+u0*u1*u3+u0*u2*u3+u1*u2*u3)+(-u0*u1*u2-u0*u1*u3-u0*u2*u3-u1*u2*u3)*q)*L1*L2*T[1] + + ((-u0*u1-u0*u2-u1*u2-u0*u3-u1*u3-u2*u3)+(u0*u1+u0*u2+u1*u2+u0*u3+u1*u3+u2*u3)*q)*L1^2*L2*T[1] + + q*L1^2*L2^3 + + ((u0+u1+u2+u3)+(-u0-u1-u2-u3)*q)*L1^3*L2*T[1] + + (1-q)*L1^3*L2^2*T[1] + + sage: L1^2 * T1*T2*T1 * L2 * L3 * T2 + (q-2*q^2+q^3)*L1^2*L2*L3 - (1-2*q+2*q^2-q^3)*L1^2*L2*L3*T[2] + - (q-q^2)*L1^3*L3*T[1] + (1-2*q+q^2)*L1^3*L3*T[1,2] + + q*L1^3*L2*T[2,1] - (1-q)*L1^3*L2*T[2,1,2] + + sage: LT = algebras.ArikiKoike(2, 3).LT() + sage: L3 = LT.L(3) + sage: x = LT.an_element() + sage: (x * L3) * L3 == x * (L3 * L3) + True + """ + # Although it is tempting to make this recursive, some care must be + # taken here to ensure that the various "helper" methods return + # linear combinations of "standard" basis elements of the form + # (L,w), where L is an n-tuple and w is a permutation because + # otherwise we may end up in an infinite loop... + + # Product is of the form L1*T1*L2*T2: separate the L's and permutations + L1,T1 = m1 + L2,T2 = m2 + + if sum(L2) == 0: + # Compute and return the product of T1 and T2, whilst fixing L + return self._from_dict(self._product_LTwTv(L1, T1, T2), + remove_zeros=False, coerce=False) + + # If T1 is trivial then we just have L1*L2*T2 we only need to rewrite + # all of the "large" powers that appear in L1*L2. Unfortunately, this + # will almost certainly introduce more T_w's and it will be recursive + # because L_n^r, for example, will introduce many powers of L_k for k 0) + * self.monomial((self._zero_tuple, T2)) + ) + + # If we are still here then both T1 and L2 are non-trivial. Using the + # method _product_Tw_L we expand the product T1*L2 as a linear + # combination of standard basis elements using the method and then, + # recursively, multiply on the left and right by L1 and T2, + # respectively. In other words, we multiply as L1*(T1*L2)*T2. + return ( self.monomial((L1, self._one_perm)) + * self._product_Tw_L(T1, L2) + * self.monomial((self._zero_tuple, T2)) ) + + def _product_LTwTv(self, L, w, v): + r""" + Return the product `L * T_w * Tv` as a linear combinations of + terms of the form `L*T_x`. + + The main point of this method is that it computes the product + `L T_w T_v` and returns it as a linear combination of standard + basis elements. That is, terms of the form `L T_x`. The monomial + ``L`` does not play a role in this calculation and, instead, it + is kept as a place holder for this "L-component" of the product. + + For this calculation the most important point is that + + .. MATH:: + + T_i T_v = \begin{cases} + T_{s_i v}, & \text{if } \ell(s_iv) > \ell(v),\\ + q T_{s_i v} + (q-1)T_v, & \text{if } \ell(s_iv) < \ell(v). + \end{cases} + + This observation is used to rewrite the product `L T_w T_v` + as a linear combination of standard basis elements. + + .. WARNING:: + + This method is not intended to be called directly and, instead, + is used by :meth:`product_on_basis`. + + INPUT: + + - ``L`` -- an `n`-tuple + - ``w`` -- the permutation ``w`` + - ``v`` -- the permutation ``v`` + + OUTPUT: + + The corresponding element represented as a ``dict``. + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(5, 4).LT() + sage: P4 = Permutations(4) + sage: H._from_dict( H._product_LTwTv((0, 3, 2, 4), P4([1,3,2,4]), P4([1,3,2,4])) ) + q*L2^3*L3^2*L4^4 - (1-q)*L2^3*L3^2*L4^4*T[2] + sage: H._from_dict( H._product_LTwTv((0, 3, 2, 4), P4([1,3,2,4]), P4([1,3,4,2])) ) + q*L2^3*L3^2*L4^4*T[3] - (1-q)*L2^3*L3^2*L4^4*T[2,3] + sage: H._from_dict( H._product_LTwTv((0, 3, 2, 4), P4([1,4,3,2]), P4([1,4,3,2])) ) + q^3*L2^3*L3^2*L4^4 - (q^2-q^3)*L2^3*L3^2*L4^4*T[3] + - (q^2-q^3)*L2^3*L3^2*L4^4*T[2] + + (q-2*q^2+q^3)*L2^3*L3^2*L4^4*T[2,3] + + (q-2*q^2+q^3)*L2^3*L3^2*L4^4*T[3,2] + - (1-2*q+2*q^2-q^3)*L2^3*L3^2*L4^4*T[3,2,3] + """ + ret = {v: self.base_ring().one()} + qm1 = self._q - self.base_ring().one() + for i in reversed(w.reduced_word()): + temp = {} # start from 0 + for p in ret: + c = ret[p] + # We have to flip the side due to Sage's + # convention for multiplying permutations + pi = p.apply_simple_reflection(i, side="left") + if p.has_descent(i, side="left"): + iaxpy(1, {p: c * qm1, pi: c * self._q}, temp) + else: + iaxpy(1, {pi: c}, temp) + ret = temp + return {(L, p): ret[p] for p in ret} + + def _product_Tw_L(self, w, L): + r""" + Given a permutation ``w`` and a monomial ``L`` return the product + `T_w L` as a linear combination of terms of the form `L_v T_v`. + + To do this we write `w = s_{i_1} \cdots s_{i_k}` and then push each + `T_{i_a}` past `L` using Lemma 3.2 of [MM1998]_ (cf. Lemma 3.3 and + Proposition 3.4 of [AK1994]_), which says + + .. MATH:: + + T_i L_i^a L_{i+1}^b = L_i^b L_{i+1}^a T_i + \begin{cases} + (1-q) sum_{k=0}^{a-1} L_i^{a+k} L_{i+1}^{b-k}, &\text{if } a \leq b,\\ + (q-1) sum_{k=0}^{b-1} L_i^{b+k} L_{i+1}^{a-k}, &\text{if } a \geq b. + \end{cases} + + Of course, `T_i` commutes with `L_k`, for `k \neq i,i+1`. + + This method is not intended to be called directly and, instead, + is used by :meth:`product_on_basis`. + + INPUT: + + - ``w`` -- a permutation + - ``L`` -- a tuple `(a_1, \ldots, a_n)` + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(5, 4).LT() + sage: P4 = Permutations(4) + sage: H._product_Tw_L(P4([1,3,2,4]), (0,2,2,0)) + L2^2*L3^2*T[2] + sage: H._product_Tw_L(P4([1,3,2,4]), (0,1,3,0)) + -(1-q)*L2*L3^3 - (1-q)*L2^2*L3^2 + L2^3*L3*T[2] + sage: H._product_Tw_L(P4([1,3,2,4]), (0,3,1,0)) + (1-q)*L2*L3^3 + L2*L3^3*T[2] + (1-q)*L2^2*L3^2 + sage: H._product_Tw_L(P4([1,3,2,4]), (2,3,1,3)) + (1-q)*L1^2*L2*L3^3*L4^3 + L1^2*L2*L3^3*L4^3*T[2] + (1-q)*L1^2*L2^2*L3^2*L4^3 + """ + # initialize wL to L: this is what we will eventually return + wL = {(L, self._one_perm): self.base_ring().one()} + q = self._q + one = q.parent().one() + for i in w.reduced_word()[::-1]: + iL = {} # this will become T_i * L, written in standard form + for lv in wL: + c = wL[lv] + L = list(lv[0]) # make a copy + v = lv[1] + a, b = L[i-1], L[i] + L[i-1], L[i] = L[i], L[i-1] # swap L_i=L[i-1] and L_{i+1}=L[i] + # the term L_1^{a_1} ... L_i^{a_{i+1}} L_{i+1}^{a_i} ... L_n^{a_n} T_i T_v + # always appears + iaxpy(c, self._product_LTwTv(tuple(L), self._Pn.simple_reflections()[i], v), iL) # need T_i*T_v + + if a < b: + Ls = [ list(L) for k in range(b-a) ] # make copies of L + for k in range(b-a): + Ls[k][i-1] = a + k + Ls[k][i] = b - k + c *= (q - one) + iaxpy(1, {(tuple(l), v): c for l in Ls}, iL) + + elif a > b: + Ls = [ list(L) for k in range(a-b) ] # make copies of L + for k in range(a-b): + Ls[k][i-1] = b + k + Ls[k][i] = a - k + c *= (one - q) + iaxpy(1, {(tuple(l), v): c for l in Ls}, iL) + + wL = iL # replace wL with iL and repeat + return self._from_dict(wL, remove_zeros=False, coerce=False) + + @cached_method + def _Li_power(self, i, m): + r""" + Return `L_i^m`, where `m \geq 0`. + + To compute `L_i^m` we use Corollary 3.4 of [MM1998]_ which says that + + .. MATH:: + + L_i^m = q^{-1} T_{i-1} L_{i-1}^m T_{i-1} + + (1 - q^{-1}) \sum_{c=1}^{m-1} L_i^c L_{i-1}^{m-c} T_{i-1}. + + .. WARNING:: + + This function is used internally by the multiplication and + may return elements that are not in the basis. However + these will be eventually resolved after the product has + been computed. + + sage: H = algebras.ArikiKoike(3, 2).LT() + sage: L2 = H.L(2) + sage: H._Li_power(2, 4) + ((u0^2*u1*u2+u0*u1^2*u2+u0*u1*u2^2)) + ... + - (q^-1-1)*L1*L2^3*T[1] ... + - (q^-1-1)*L1^3*L2*T[1] + sage: H._Li_power(2, 4) == L2^4 + False + sage: L2 * H._Li_power(2, 4) == L2^5 + True + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(3, 3).LT() + sage: for i in range(1,4): + ....: for m in range(4): + ....: print('L_{}^{} = {}'.format(i,m,H._Li_power(i,m))) + L_1^0 = 1 + L_1^1 = L1 + L_1^2 = L1^2 + L_1^3 = u0*u1*u2 + ((-u0*u1-u0*u2-u1*u2))*L1 + ((u0+u1+u2))*L1^2 + L_2^0 = 1 + L_2^1 = L2 + L_2^2 = L2^2 + L_2^3 = u0*u1*u2 + (-u0*u1*u2*q^-1+u0*u1*u2)*T[1] + + ((-u0*u1-u0*u2-u1*u2))*L2 + ((u0+u1+u2))*L2^2 + + ((u0+u1+u2)*q^-1+(-u0-u1-u2))*L1*L2*T[1] + - (q^-1-1)*L1*L2^2*T[1] - (q^-1-1)*L1^2*L2*T[1] + L_3^0 = 1 + L_3^1 = L3 + L_3^2 = L3^2 + L_3^3 = u0*u1*u2 + (-u0*u1*u2*q^-1+u0*u1*u2)*T[2] + + (-u0*u1*u2*q^-2+u0*u1*u2*q^-1)*T[2,1,2] + + ((-u0*u1-u0*u2-u1*u2))*L3 + ((u0+u1+u2))*L3^2 + + ((u0+u1+u2)*q^-1+(-u0-u1-u2))*L2*L3*T[2] + - (q^-1-1)*L2*L3^2*T[2] - (q^-1-1)*L2^2*L3*T[2] + + ((u0+u1+u2)*q^-2+(-2*u0-2*u1-2*u2)*q^-1+(u0+u1+u2))*L1*L3*T[1,2] + + ((u0+u1+u2)*q^-2+(-u0-u1-u2)*q^-1)*L1*L3*T[2,1,2] + - (q^-2-2*q^-1+1)*L1*L3^2*T[1,2] - (q^-2-q^-1)*L1*L3^2*T[2,1,2] + - (q^-2-2*q^-1+1)*L1*L2*L3*T[1,2] - (q^-2-2*q^-1+1)*L1^2*L3*T[1,2] + - (q^-2-q^-1)*L1^2*L3*T[2,1,2] + """ + # shorthand for returning a tuple of the form (0,...,a,b,...,0) with a,b + # in the (i-1)th and i-th positions, respectively + def Ltuple(a, b): + return tuple([b if j == i else a if j == i-1 else 0 + for j in range(1,self._n+1)]) + + # return "small" powers of the generators without change + if m < self._r: + return self.monomial( (Ltuple(0, m), self._one_perm) ) + + if i > 1: + si = self._Pn.simple_reflections()[i-1] + qsum = self.base_ring().one() - self._q**-1 + # by calling _Li_power we avoid infinite recursion here + return ( self.sum_of_terms( ((Ltuple(c, m-c), si), qsum) for c in range(1, m) ) + + self._q**-1 * self.T(i-1) * self._Li_power(i-1, m) * self.T(i-1) ) + + # now left with the case i = 1 and m >= r + if m > self._r: + return self.monomial((Ltuple(0, 1), self._one_perm)) * self._Li_power(i,m-1) + + z = PolynomialRing(self.base_ring(), 'DUMMY').gen() + p = list(prod(z - val for val in self._u))#[:-1] + p.pop() # remove the highest power + zero = self.base_ring().zero() + return self._from_dict({(Ltuple(0, exp), self._one_perm): -coeff + for exp,coeff in enumerate(p) if coeff != zero}, + remove_zeros=False, coerce=False) + + @cached_method + def inverse_T(self, i): + r""" + Return the inverse of the generator `T_i`. + + From the quadratic relation, we have + + .. MATH:: + + T_i^{-1} = q^{-1} T_i + (q^{-1} - 1). + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(3, 4).LT() + sage: [LT.inverse_T(i) for i in range(1, 4)] + [(q^-1-1) + (q^-1)*T[1], + (q^-1-1) + (q^-1)*T[2], + (q^-1-1) + (q^-1)*T[3]] + + TESTS:: + + sage: LT = algebras.ArikiKoike(4, 4).LT() + sage: all(LT.inverse_T(i) * LT.T(i) == LT.one() for i in range(1, 4)) + True + sage: all(LT.T(i) * LT.inverse_T(i) == LT.one() for i in range(1, 4)) + True + """ + c = ~self._q - self.base_ring().one() + m = self.T(i).leading_support() + return self._from_dict({m: ~self._q, self.one_basis(): c}) + + class Element(CombinatorialFreeModule.Element): + def inverse(self): + r""" + Return the inverse if ``self`` is a basis element. + + EXAMPLES:: + + sage: LT = algebras.ArikiKoike(3, 4).LT() + sage: t = LT.T(1) * LT.T(2) * LT.T(3); t + T[1,2,3] + sage: t.inverse() + (q^-3-3*q^-2+3*q^-1-1) + (q^-3-2*q^-2+q^-1)*T[3] + + (q^-3-2*q^-2+q^-1)*T[2] + (q^-3-q^-2)*T[3,2] + + (q^-3-2*q^-2+q^-1)*T[1] + (q^-3-q^-2)*T[1,3] + + (q^-3-q^-2)*T[2,1] + (q^-3)*T[3,2,1] + """ + if len(self) != 1: + raise NotImplementedError("inverse only implemented for monomials") + l,w = self.support_of_term() + if sum(l) != 0: + raise NotImplementedError("inverse only implemented for monomials in T variables") + H = self.parent() + return ~self[l,w] * H.prod(H.inverse_T(i) for i in reversed(w.reduced_word())) + + __invert__ = inverse + + class T(_Basis): + r""" + The basis of the Ariki-Koike algebra given by monomials of the + generators `\{ T_i | 0 \leq i < n \}`. + + We use the choice of reduced expression given by [BM1997]_: + + .. MATH:: + + T_{1,a_1} \cdots T_{n,a_n} T_w, + + where `T_{i,k} = T_{i-1} \cdots T_2 T_1 T_0^k` (note that + `T_{1,k} = T_0^k`) and `w` is a reduced expression of an + element in `\mathfrak{S}_n`. + """ + def __init__(self, algebra): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: T = algebras.ArikiKoike(5, 3).T() + sage: TestSuite(T).run() + sage: T = algebras.ArikiKoike(1, 4).T() + sage: TestSuite(T).run() + sage: T = algebras.ArikiKoike(2, 3).T() + sage: TestSuite(T).run() + sage: T = algebras.ArikiKoike(3, 4).T() + sage: TestSuite(T).run() # long time + """ + _Basis.__init__(self, algebra, prefix='T') + self._assign_names(['T%s'%i for i in range(self._n)]) + + def _repr_term(self, t): + r""" + Return a string representation of the basis element indexed by ``m``. + + EXAMPLES:: + + sage: T = algebras.ArikiKoike(4, 3).T() + sage: T._repr_term( ((1,0,2), Permutation([3,2,1])) ) + 'T[0,2,1,0,0,2,1,2]' + """ + redword = [] + for i,k in enumerate(t[0]): + if k == 0: + continue + redword += list(reversed(range(1,i+1))) + [0]*k + redword += t[1].reduced_word() + if len(redword) == 0: + return "1" + return (self._print_options['prefix'] + + '[%s]'%','.join('%d'%i for i in redword)) + + def _latex_term(self, t): + r""" + Return a latex representation for the basis element indexed by ``m``. + + EXAMPLES:: + + sage: T = algebras.ArikiKoike(4, 3).T() + sage: T._latex_term( ((1,0,2), Permutation([3,2,1])) ) + 'T_{0}T_{1}T_{0}T_{0}T_{2}T_{1}T_{2}' + """ + redword = [] + for i,k in enumerate(t[0]): + if k == 0: + continue + redword += list(reversed(range(1,i))) + [0]*k + redword += t[1].reduced_word() + if len(redword) == 0: + return "1" + return ''.join("%s_{%d}"%(self._print_options['prefix'], i) + for i in redword) + + def _from_LT_basis(self, m): + r""" + Return the image of the `LT` basis element indexed + by ``m`` in ``self``. + + EXAMPLES:: + + sage: H = algebras.ArikiKoike(4, 2) + sage: LT = H.LT() + sage: T = H.T() + sage: all(T(Li) == T.L(i+1) for i,Li in enumerate(LT.L())) + True + sage: all(T(Ti) == T.T(i) for i,Ti in enumerate(LT.T())) + True + + Check that the products of elements agrees:: + + sage: type_A_words = [p.reduced_word() for p in Permutations(H._n)] + sage: def from_reduced_word(B, w): + ....: t = B.T() + ....: return B.prod(t[i] for i in w) + sage: all(T(from_reduced_word(LT, w)) == from_reduced_word(T, w) + ....: for w in type_A_words) + True + + Check that the composition of the morphisms is the identity:: + + sage: all(T(LT(b)) == b for b in T.basis()) # indirect doctest + True + """ + ret = self.prod(self.L(i+1)**k for i,k in enumerate(m[0])) + return ret * self.monomial( (self._zero_tuple, m[1]) ) + + @cached_method + def algebra_generators(self): + r""" + Return the algebra generators of ``self``. + + EXAMPLES:: + + sage: T = algebras.ArikiKoike(5, 3).T() + sage: dict(T.algebra_generators()) + {0: T[0], 1: T[1], 2: T[2]} + + sage: T = algebras.ArikiKoike(1, 4).T() + sage: dict(T.algebra_generators()) + {1: T[1], 2: T[2], 3: T[3]} + """ + start = 1 if self._r == 1 else 0 + return Family(list(range(start, self._n)), self.T) + + def T(self, i=None): + r""" + Return the generator(s) `T_i` of ``self``. + + INPUT: + + - ``i`` -- (default: ``None``) the generator `T_i` or if ``None``, + then the list of all generators `T_i` + + EXAMPLES:: + + sage: T = algebras.ArikiKoike(8, 3).T() + sage: T.T(1) + T[1] + sage: T.T() + [T[0], T[1], T[2]] + + sage: T = algebras.ArikiKoike(1, 4).T() + """ + if i is None: + return [self.T(j) for j in range(self._n)] + + if i == 0: + return self.monomial( ((1,) + self._zero_tuple[1:], self._one_perm) ) + s = self._Pn.simple_reflections() + return self.monomial( (self._zero_tuple, s[i]) ) + + @cached_method + def L(self, i=None): + r""" + Return the Jucys-Murphy element(s) `L_i`. + + The Jucys-Murphy element `L_i` is defined as + + .. MATH:: + + L_i = q^{-i+1} T_{i-1} \cdots T_1 T_0 T_1 \cdots T_{i-1} + = q^{-1} T_{i-1} L_{i-1} T_{i-1}. + + INPUT: + + - ``i`` -- (default: ``None``) the Jucys-Murphy element `L_i` + or if ``None``, then the list of all `L_i` + + EXAMPLES:: + + sage: T = algebras.ArikiKoike(8, 3).T() + sage: T.L(2) + (q^-1)*T[1,0,1] + sage: T.L() + [T[0], (q^-1)*T[1,0,1], (q^-2)*T[2,1,0,1,2]] + + sage: T0,T1,T2 = T.T() + sage: q = T.q() + sage: T.L(1) == T0 + True + sage: T.L(2) == q^-1 * T1*T0*T1 + True + sage: T.L(3) == q^-2 * T2*T1*T0*T1*T2 + True + + sage: T = algebras.ArikiKoike(1, 3).T() + sage: T.L(2) + u + (-u*q^-1+u)*T[1] + sage: T.L() + [u, + u + (-u*q^-1+u)*T[1], + u + (-u*q^-1+u)*T[2] + (-u*q^-2+u*q^-1)*T[2,1,2]] + + TESTS: + + Check that the Jucys-Murphy elements form a commutative + subring:: + + sage: T = algebras.ArikiKoike(8, 4).T() + sage: L = T.L() + sage: all(x*y == y*x for x in L for y in L) + True + + sage: T = algebras.ArikiKoike(2, 3).T() + sage: L = T.L() + sage: all(x*y == y*x for x in L for y in L) + True + + sage: T = algebras.ArikiKoike(1, 4).T() + sage: L = T.L() + sage: all(x*y == y*x for x in L for y in L) + True + """ + if i is None: + return [self.L(j) for j in range(1, self._n+1)] + + if i == 1: + if self._r == 1: + return self.from_base_ring(self._u[0]) + else: + return self.T(0) + T = self.T() + return self._q**-1 * T[i-1] * self.L(i-1) * T[i-1] + + @cached_method + def product_on_basis(self, m1, m2): + r""" + Return the product of the basis elements indexed + by ``m1`` and ``m2``. + + EXAMPLES:: + + sage: T = algebras.ArikiKoike(2, 3).T() + sage: T0, T1, T2 = T.T() + sage: T.product_on_basis(T0.leading_support(), T1.leading_support()) + T[0,1] + sage: T1 * T2 + T[1,2] + sage: T2 * T1 + T[2,1] + sage: T2 * (T2 * T1 * T0) + -(1-q)*T[2,1,0] + q*T[1,0] + sage: (T1 * T0 * T1 * T0) * T0 + (-u0*u1)*T[1,0,1] + ((u0+u1))*T[0,1,0,1] + sage: (T0 * T1 * T0 * T1) * (T0 * T1) + (-u0*u1*q)*T[1,0] + (u0*u1-u0*u1*q)*T[1,0,1] + + ((u0+u1)*q)*T[0,1,0] + ((-u0-u1)+(u0+u1)*q)*T[0,1,0,1] + sage: T1 * (T0 * T2 * T1 * T0) + T[1,0,2,1,0] + sage: (T1 * T2) * (T2 * T1 * T0) + -(1-q)*T[2,1,0,2] - (q-q^2)*T[1,0] + q^2*T[0] + sage: (T2*T1*T2) * (T2*T1*T0*T1*T2) + -(q-q^2)*T[2,1,0,1,2] + (1-2*q+q^2)*T[2,1,0,2,1,2] + - (q-q^2)*T[1,0,2,1,2] + q^2*T[0,2,1,2] + + We check some relations:: + + sage: T0 * T1 * T0 * T1 == T1 * T0 * T1 * T0 + True + sage: T1 * T2 * T1 == T2 * T1 * T2 + True + sage: (T1 * T0) * T0 == T1 * (T0 * T0) + True + sage: (T.L(1) * T.L(2)) * T.L(2) - T.L(1) * (T.L(2) * T.L(2)) + 0 + sage: (T.L(2) * T.L(3)) * T.L(3) - T.L(2) * (T.L(3) * T.L(3)) + 0 + + TESTS:: + + sage: T = algebras.ArikiKoike(2, 3).T() + sage: T0, T1, T2 = T.T() + sage: (T1 * T0 * T1) * (T0 * T0) + (-u0*u1)*T[1,0,1] + ((u0+u1))*T[0,1,0,1] + sage: T1 * T.L(3) * T2 * T1 * T0 - T1 * (T.L(3) * T2 * T1 * T0) + 0 + + sage: T = algebras.ArikiKoike(3, 3).T() + sage: x = T.T(0) * T.T(1) + sage: (x*x)*x == x*(x*x) + True + + sage: T = algebras.ArikiKoike(3, 4).T() + sage: L1 = T.L(1) + sage: L2 = T.L(2) + sage: (L2 * L1^2) * L2 == L2 * (L1^2 * L2) + True + sage: T1 = T.T(1) + sage: (T1 * L1^2) * T1 * L1 * L1 == (T1 * L1^2) * T1 * L1^2 + True + """ + # We represent T_i for i > 0 as S_i in comments to avoid confusion. + # Product is of the form t1*s1 * t2*s2: separate the T's and permutations. + t1, s1 = m1 + t2, s2 = m2 + one = self.base_ring().one() + q = self._q + qm1 = q - one + + # We first handle the case when s1 == 1 + if s1 == self._one_perm: + if t1 == self._zero_tuple: + # Multiplying 1 * m2 + return self._from_dict({m2: one}, remove_zeros=False) + if t2 == self._zero_tuple: + return self._from_dict({(t1, s2): one}, remove_zeros=False) + k1 = max(k for k,a in enumerate(t1) if a != 0) + k2 = min(k for k,a in enumerate(t2) if a != 0) + if k1 < k2: + T = list(t1) + for k in range(k2, len(t2)): + T[k] = t2[k] + return self._from_dict({(tuple(T), s2): one}, remove_zeros=False) + # This is the most recursive part of the product + M = self._product_TT(k1, t1[k1], k2, t2[k2]) + t1 = list(t1) + t2 = list(t2) + t1[k1] = 0 + t2[k2] = 0 + L = self._from_dict({(tuple(t1), self._one_perm): one}, remove_zeros=False) + R = self._from_dict({(tuple(t2), s2): one}, remove_zeros=False) + return L * M * R + + # The current product of T's and the type A Hecke algebra + tprod = [( [(k, a) for k, a in enumerate(t2) if a != 0], {s2: one} )] + + # s1 through t2 + for i in reversed(s1.reduced_word()): + new_t = [] + for index in range(len(tprod)): + j = i + T, sprod = tprod[index] + absorbed = False + for ind in range(len(T)): + k, a = T[ind] + # -1 from i since k is 0-based but i is 1-based + if j < k: + j += 1 + elif j == k: + absorbed = True + # Quadratic relation: S_k^2 = (q - 1) S_k + q + # So S_{k-1} T_{k,a} = (q-1) T_{k,a} + q T_{k-1,a} + # Make a copy of T since we need to mutate it + new_t.append((list(T), {s: q * sprod[s] for s in sprod})) + new_t[-1][0][ind] = (k-1, a) + for s in sprod: + sprod[s] *= qm1 + break + elif j == k + 1: + absorbed = True + T[ind] = (k+1, a) + break + # elif j > k: pass + if absorbed: + # We do not need to update tprod[index] because we + # have mutated that pair of objects (T, sprod). + continue + + # Do the usual Hecke product of S_j * S + temp = {} # start from 0 + for p in sprod: + c = sprod[p] + # We have to flip the side due to Sage's + # convention for multiplying permutations + pj = p.apply_simple_reflection(j, side="left") + if p.has_descent(j, side="left"): + iaxpy(1, {p: c * qm1, pj: c * self._q}, temp) + else: + iaxpy(1, {pj: c}, temp) + tprod[index] = (T, temp) + tprod.extend(new_t) + + # Compute t1 * T * sprod + def compute(T, sprod): + if not T: # T=1, so just do t1 * sprod, each of which is in order + return self._from_dict({(t1, s): sprod[s] for s in sprod}, + remove_zeros=False, coerce=False) + + s_elt = self._from_dict({(self._zero_tuple, s): sprod[s] for s in sprod}, + remove_zeros=False, coerce=False) + # Break T into basis vectors as much as possible to best take + # advantage of the caching + cur = list(t1) + product = [cur] + if t1 != self._zero_tuple: + K = max(k for k, a in enumerate(t1) if a != 0) + else: + K = -1 + T.reverse() # reverse the list so we can pop off the front + while T: + k, a = T.pop() + if k > K: + cur[k] = a + else: + cur = list(self._zero_tuple) + cur[k] = a + product.append(cur) + K = k + return self.prod(self._from_dict({(tuple(p), self._one_perm): one}, + remove_zeros=False, coerce=False) + for p in product) * s_elt + + return self.sum(compute(T, sprod) for T, sprod in tprod) + + @lazy_attribute + def _T0_polynomial(self): + r""" + Return `p` such that `T0^{r-1} - p = \prod_{i=0}^{r-1} (T_0 - u_i)`. + + OUTPUT: + + A ``dict`` representing the polynomial `p`. + + EXAMPLES:: + + sage: T = algebras.ArikiKoike(4, 2).T() + sage: T._T0_polynomial + ((u0 + u1 + u2 + u3))*DUMMY^3 + + ((-u0*u1 - u0*u2 - u1*u2 - u0*u3 - u1*u3 - u2*u3))*DUMMY^2 + + ((u0*u1*u2 + u0*u1*u3 + u0*u2*u3 + u1*u2*u3))*DUMMY + - u0*u1*u2*u3 + """ + z = PolynomialRing(self.base_ring(), 'DUMMY').gen() + # Remove the highest power + return -prod(z - val for val in self._u).truncate(self._r) + + def _reduced_T0_power(self, exp): + r""" + Return the element `T_0` to the power ``exp`` in terms + of `T_0^k` for `k < r`. + + EXAMPLES:: + + sage: T = algebras.ArikiKoike(2, 3).T() + sage: T._reduced_T0_power(1) + 1 + sage: T._reduced_T0_power(2) + ((u0 + u1))*DUMMY - u0*u1 + sage: T._reduced_T0_power(3) + ((u0^2 + u0*u1 + u1^2))*DUMMY + (-u0^2*u1 - u0*u1^2) + sage: T._reduced_T0_power(4) + ((u0^3 + u0^2*u1 + u0*u1^2 + u1^3))*DUMMY + + (-u0^3*u1 - u0^2*u1^2 - u0*u1^3) + sage: T._reduced_T0_power(5) + ((u0^4 + u0^3*u1 + u0^2*u1^2 + u0*u1^3 + u1^4))*DUMMY + + (-u0^4*u1 - u0^3*u1^2 - u0^2*u1^3 - u0*u1^4) + """ + if exp < self._r: + return self.base_ring().one() + PR = self._T0_polynomial.parent() + z = PR.gen() + cur = z ** exp + while cur.degree() >= self._r: + cur = (PR.sum(coeff * self._T0_polynomial * z**e + for e, coeff in enumerate(cur.list()[self._r:])) + + cur.truncate(self._r)) + return cur + + @cached_method + def _product_TT(self, kp, a, k, b): + r""" + Return the product `T_{k',a} T_{k,b}` with `k' \geq k` in terms + of the basis elements of ``self``. + + From Lemma 2.3 of [BM1997]_, we have + + .. MATH:: + + T_{k',a} T_{k,b} = T_{k-1,b} T_{k',a} T_1 + + (q - 1) \sum_{i=1}^b T_{k-1,a+b-i} T_{k',i} + - T_{k-1,i} T_{k',a+b-i}. + + INPUT: + + - ``kp``, ``k`` -- 0-based indices + - ``a``, ``b`` -- the exponents of the `T_0` generator + + EXAMPLES:: + + sage: T = algebras.ArikiKoike(4, 3).T() + sage: T._product_TT(1, 0, 0, 1) + T[1,0] + sage: T._product_TT(1, 1, 0, 1) + T[1,0,0] + sage: T._product_TT(1, 2, 0, 1) + T[1,0,0,0] + sage: T._product_TT(1, 3, 0, 1) + (-u0*u1*u2*u3)*T[1] + + ((u0*u1*u2+u0*u1*u3+u0*u2*u3+u1*u2*u3))*T[1,0] + + ((-u0*u1-u0*u2-u1*u2-u0*u3-u1*u3-u2*u3))*T[1,0,0] + + ((u0+u1+u2+u3))*T[1,0,0,0] + sage: T._product_TT(1, 2, 0, 2) + (-u0*u1*u2*u3)*T[1] + + ((u0*u1*u2+u0*u1*u3+u0*u2*u3+u1*u2*u3))*T[1,0] + + ((-u0*u1-u0*u2-u1*u2-u0*u3-u1*u3-u2*u3))*T[1,0,0] + + ((u0+u1+u2+u3))*T[1,0,0,0] + sage: T._product_TT(2, 1, 0, 3) + (-u0*u1*u2*u3)*T[2,1] + + ((u0*u1*u2+u0*u1*u3+u0*u2*u3+u1*u2*u3))*T[2,1,0] + + ((-u0*u1-u0*u2-u1*u2-u0*u3-u1*u3-u2*u3))*T[2,1,0,0] + + ((u0+u1+u2+u3))*T[2,1,0,0,0] + + TESTS:: + + sage: H = algebras.ArikiKoike(3, 4) + sage: T = H.T() + sage: T._product_TT(1, 2, 1, 2) + (-u0*u1*u2+u0*u1*u2*q)*T[1,0] + + (u0*u1*u2-u0*u1*u2*q)*T[0,1] + + ((u0+u1+u2)+(-u0-u1-u2)*q)*T[0,1,0,0] + + ((-u0-u1-u2)+(u0+u1+u2)*q)*T[0,0,1,0] + + T[0,0,1,0,0,1] + sage: T._product_TT(2,2,2,2) + (-u0*u1*u2+u0*u1*u2*q)*T[2,1,0,2] + + (u0*u1*u2-u0*u1*u2*q)*T[1,0,2,1] + + ((u0+u1+u2)+(-u0-u1-u2)*q)*T[1,0,2,1,0,0] + + ((-u0-u1-u2)+(u0+u1+u2)*q)*T[1,0,0,2,1,0] + + T[1,0,0,2,1,0,0,1] + sage: T._product_TT(3,2,3,2) + (-u0*u1*u2+u0*u1*u2*q)*T[3,2,1,0,3,2] + + (u0*u1*u2-u0*u1*u2*q)*T[2,1,0,3,2,1] + + ((u0+u1+u2)+(-u0-u1-u2)*q)*T[2,1,0,3,2,1,0,0] + + ((-u0-u1-u2)+(u0+u1+u2)*q)*T[2,1,0,0,3,2,1,0] + + T[2,1,0,0,3,2,1,0,0,1] + """ + # Quadratic relation: S_i^2 - (q - 1) S_i - q == 0 + # [BM1997]_: S_i^2 - (q_1 + q_2) S_i + q_1 q_2 == 0 + # Implies q_1 = q, q_2 = -1 + one = self.base_ring().one() + # Case T_{k',a} T_0^b = T_{k',a+b} + if k == 0: + if a + b < self._r: + T = list(self._zero_tuple) + T[kp] = a + b + return self._from_dict({(tuple(T), self._one_perm): one}, + remove_zeros=False, coerce=False) + def key(exp): + if exp > 0 or kp == 0: + T = list(self._zero_tuple) + T[kp] = exp + return (tuple(T), self._one_perm) + # Note that kp is 0-based, but our 0-index in the T portion + # is the power of T_0 + perm = self._Pn.one() + for j in range(1, kp+1): + perm = perm.apply_simple_reflection_left(j) + return (self._zero_tuple, perm) + p = self._reduced_T0_power(a + b) + zero = self.base_ring().zero() + return self._from_dict({key(exp): coeff + for exp, coeff in enumerate(p) + if coeff != zero}, + remove_zeros=False, coerce=False) + + # Otherwise k > 0 + assert kp >= k + s1 = self._Pn.simple_reflection(1) + qm1 = self._q - one + T = list(self._zero_tuple) + T[k-1] = b + T[kp] = a + ret = {(tuple(T), s1): one} + zero = self.base_ring().zero() + def T_index(exp, ind, i, indp): + T = list(self._zero_tuple) + T[ind] = exp + T[indp] = i + return tuple(T) + for i in range(1, b+1): + if a + b - i == i: + continue + if a + b - i < self._r: + T[k-1] = a + b - i + T[kp] = i + m = (tuple(T), self._one_perm) + T[k-1] = i + T[kp] = a + b - i + mp = (tuple(T), self._one_perm) + iaxpy(1, {m: qm1, mp: -qm1}, ret) + else: + p = self._reduced_T0_power(a + b - i) + temp = {(T_index(exp, k-1, i, kp), self._one_perm): qm1 * coeff + for exp, coeff in enumerate(p) if coeff != zero} + if p[0] != zero and k > 1: + # We need to add back in the permutation for the "T_{k-1,0}" + # in the reduction from T_{k-1,a+b-i} + perm = self._Pn.one() + for j in range(2, k+1): # Recall k is 0-based, we add 1 back from Lemma 2.3(a) + perm = perm.apply_simple_reflection_left(j) + tind = T_index(0, k-1, i, kp) + temp[(tind, perm)] = temp[(tind, self._one_perm)] + del temp[(tind, self._one_perm)] + iaxpy(1, temp, ret) + temp = {(T_index(exp, kp, i, k-1), self._one_perm): -qm1 * coeff + for exp, coeff in enumerate(p) if coeff != zero} + if p[0] != zero: + # We need to add back in the permutation for the "T_{k',0}" + # in the reduction from T_{k',a+b-i} + perm = self._Pn.one() + for j in range(1, kp+1): # Recall kp is 0-based + perm = perm.apply_simple_reflection_left(j) + tind = T_index(0, kp, i, k-1) + temp[(tind, perm)] = temp[(tind, self._one_perm)] + del temp[(tind, self._one_perm)] + iaxpy(1, temp, ret) + + return self._from_dict(ret, remove_zeros=False) + diff --git a/src/sage/algebras/jordan_algebra.py b/src/sage/algebras/jordan_algebra.py index d3dc4eacbd7..aaf712959f6 100644 --- a/src/sage/algebras/jordan_algebra.py +++ b/src/sage/algebras/jordan_algebra.py @@ -234,10 +234,6 @@ def __init__(self, A, names=None): cat = C.Commutative() if A in C.Unital(): cat = cat.Unital() - self._no_generic_basering_coercion = True - # Remove the preceding line once trac #16492 is fixed - # Removing this line will also break some of the input formats, - # see trac #16054 if A in C.WithBasis(): cat = cat.WithBasis() if A in C.FiniteDimensional(): @@ -595,7 +591,6 @@ def __init__(self, R, form, names=None): self._form = form self._M = FreeModule(R, form.ncols()) cat = MagmaticAlgebras(R).Commutative().Unital().FiniteDimensional().WithBasis() - self._no_generic_basering_coercion = True # Remove once 16492 is fixed Parent.__init__(self, base=R, names=names, category=cat) def _repr_(self): @@ -694,6 +689,25 @@ def _element_constructor_(self, *args): raise ValueError("unable to construct an element from the given data") + def _coerce_map_from_base_ring(self): + """ + Return a coercion map from the base ring of ``self``. + + TESTS:: + + sage: J = JordanAlgebra(Matrix([[0, 1], [1, 1]])) + sage: J.coerce_map_from(ZZ) + Coercion map: + From: Integer Ring + To: Jordan algebra over Integer Ring given by the symmetric bilinear form: + [0 1] + [1 1] + """ + # Return a DefaultConvertMap_unique; this can pass additional + # arguments to _element_constructor_, unlike the map returned + # by UnitalAlgebras.ParentMethods._coerce_map_from_base_ring. + return self._generic_coerce_map(self.base_ring()) + @cached_method def basis(self): """ diff --git a/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx b/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx index ba7518818e0..ad863ea0a34 100644 --- a/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx +++ b/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx @@ -18,7 +18,7 @@ AUTHOR: # **************************************************************************** from sage.libs.singular.function import lib, singular_function -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal from cpython.object cimport PyObject_RichCompare diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py index 30e5176fdde..5ab632aa34e 100644 --- a/src/sage/algebras/lie_algebras/classical_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Classical Lie Algebras @@ -14,15 +15,15 @@ - Travis Scrimshaw (2019-07-09): Implemented compact real form """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2013-2017 Travis Scrimshaw # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from collections import OrderedDict from sage.misc.abstract_method import abstract_method @@ -238,7 +239,7 @@ def epsilon(self, i, h): """ return h[i-1,i-1] - # Do we want this to be optional or requried? + # Do we want this to be optional or required? # There probably is a generic implementation we can do. @abstract_method(optional=True) def simple_root(self, i, h): @@ -1094,6 +1095,33 @@ def __init__(self, parent, real, imag): self._imag.set_immutable() self._mc = None + def _combined_matrix(self): + r""" + Return a single matrix representative of ``self``. + + .. NOTE:: + + The resulting base ring is `R[i]`, where `R` is the + base ring of the Lie algebra. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A',2], representation="compact real") + sage: x = L.sum((i+1)/7*b for i,b in enumerate(L.basis())) + sage: M = x._combined_matrix() + sage: M + [ 4/7*i 5/7*i + 1/7 6/7*i + 2/7] + [5/7*i - 1/7 i 8/7*i + 3/7] + [6/7*i - 2/7 8/7*i - 3/7 -11/7*i] + sage: M.parent() + Full MatrixSpace of 3 by 3 sparse matrices over + Univariate Polynomial Ring in i over Rational Field + """ + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + MS = self.parent()._MS + R = PolynomialRing(MS.base_ring(), 'i') + return self._real + R.gen() * self._imag + def _repr_(self): """ Return a string representation of ``self``. @@ -1108,10 +1136,7 @@ def _repr_(self): [ i - 2/7 0 -6/7*i - 1/7 -9/7*i -10/7*i + 4/7] [ 8/7*i - 3/7 10/7*i - 4/7 -8/7*i - 3/7 -10/7*i - 4/7 0] """ - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - MS = self.parent()._MS - R = PolynomialRing(MS.base_ring(), 'i') - return repr(self._real + R.gen()*self._imag) + return repr(self._combined_matrix()) def _latex_(self): r""" @@ -1131,10 +1156,39 @@ def _latex_(self): \end{array}\right) """ from sage.misc.latex import latex - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - MS = self.parent()._MS - R = PolynomialRing(MS.base_ring(), 'i') - return latex(self._real + R.gen()*self._imag) + return latex(self._combined_matrix()) + + def _ascii_art_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A',2], representation="compact real") + sage: x = L.sum((i+1)/7*b for i,b in enumerate(L.basis())) + sage: ascii_art(x) + [ 4/7*i 5/7*i + 1/7 6/7*i + 2/7] + [5/7*i - 1/7 i 8/7*i + 3/7] + [6/7*i - 2/7 8/7*i - 3/7 -11/7*i] + """ + from sage.typeset.ascii_art import ascii_art + return ascii_art(self._combined_matrix()) + + def _unicode_art_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A',2], representation="compact real") + sage: x = L.sum((i+1)/7*b for i,b in enumerate(L.basis())) + sage: unicode_art(x) + ⎛ 4/7*i 5/7*i + 1/7 6/7*i + 2/7⎞ + ⎜5/7*i - 1/7 i 8/7*i + 3/7⎟ + ⎝6/7*i - 2/7 8/7*i - 3/7 -11/7*i⎠ + """ + from sage.typeset.unicode_art import unicode_art + return unicode_art(self._combined_matrix()) def __bool__(self): r""" diff --git a/src/sage/algebras/lie_algebras/free_lie_algebra.py b/src/sage/algebras/lie_algebras/free_lie_algebra.py index 3a547e1a360..81acd327602 100644 --- a/src/sage/algebras/lie_algebras/free_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/free_lie_algebra.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Free Lie Algebras @@ -18,9 +19,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# https://www.gnu.org/licenses/ -# **************************************************************************** -from six import iteritems +# http://www.gnu.org/licenses/ +#***************************************************************************** from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method @@ -116,6 +116,42 @@ def _latex_term(self, x): """ return x._latex_() + def _ascii_art_term(self, x): + r""" + Return an ascii art representation for ``x``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, 'x,y') + sage: H = L.Hall() + sage: x,y = H.gens() + sage: H._ascii_art_term(x.leading_support()) + x + sage: a = H([x, y]).leading_support() + sage: H._ascii_art_term(a) + [x, y] + """ + from sage.typeset.ascii_art import ascii_art + return ascii_art(x) + + def _unicode_art_term(self, x): + r""" + Return a unicode art representation for ``x``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, 'x,y') + sage: H = L.Hall() + sage: x,y = H.gens() + sage: H._unicode_art_term(x.leading_support()) + x + sage: a = H([x, y]).leading_support() + sage: H._unicode_art_term(a) + [x, y] + """ + from sage.typeset.unicode_art import unicode_art + return unicode_art(x) + def _element_constructor_(self, x): """ Convert ``x`` into ``self``. @@ -596,7 +632,7 @@ def _rewrite_bracket(self, l, r): # Rewrite [a, [b, c]] = [b, [a, c]] + [[a, b], c] with a < b < c # Compute the left summand - for m, inner_coeff in iteritems(self._rewrite_bracket(l, r._right)): + for m, inner_coeff in self._rewrite_bracket(l, r._right).items(): if r._left == m: continue elif r._left < m: @@ -604,11 +640,11 @@ def _rewrite_bracket(self, l, r): else: # r._left > m x, y = m, r._left inner_coeff = -inner_coeff - for b_elt, coeff in iteritems(self._rewrite_bracket(x, y)): + for b_elt, coeff in self._rewrite_bracket(x, y).items(): ret[b_elt] = ret.get(b_elt, 0) + coeff * inner_coeff # Compute the right summand - for m, inner_coeff in iteritems(self._rewrite_bracket(l, r._left)): + for m, inner_coeff in self._rewrite_bracket(l, r._left).items(): if m == r._right: continue elif m < r._right: @@ -616,7 +652,7 @@ def _rewrite_bracket(self, l, r): else: # m > r._right x, y = r._right, m inner_coeff = -inner_coeff - for b_elt, coeff in iteritems(self._rewrite_bracket(x, y)): + for b_elt, coeff in self._rewrite_bracket(x, y).items(): ret[b_elt] = ret.get(b_elt, 0) + coeff * inner_coeff return ret @@ -703,7 +739,7 @@ def _rewrite_bracket(self, l, r): # caught us. # For a similar reason, we have b >= c. # Compute the left summand - for m, inner_coeff in iteritems(self._rewrite_bracket(l._right, r)): + for m, inner_coeff in self._rewrite_bracket(l._right, r).items(): if l._left == m: continue elif l._left < m: @@ -711,11 +747,11 @@ def _rewrite_bracket(self, l, r): else: # l._left > m x, y = m, l._left inner_coeff = -inner_coeff - for b_elt, coeff in iteritems(self._rewrite_bracket(x, y)): + for b_elt, coeff in self._rewrite_bracket(x, y).items(): ret[b_elt] = ret.get(b_elt, 0) + coeff * inner_coeff # Compute the right summand - for m, inner_coeff in iteritems(self._rewrite_bracket(l._left, r)): + for m, inner_coeff in self._rewrite_bracket(l._left, r).items(): if m == l._right: continue elif m < l._right: @@ -723,7 +759,7 @@ def _rewrite_bracket(self, l, r): else: # m > l._right x, y = l._right, m inner_coeff = -inner_coeff - for b_elt, coeff in iteritems(self._rewrite_bracket(x, y)): + for b_elt, coeff in self._rewrite_bracket(x, y).items(): ret[b_elt] = ret.get(b_elt, 0) + coeff * inner_coeff return ret diff --git a/src/sage/algebras/lie_algebras/heisenberg.py b/src/sage/algebras/lie_algebras/heisenberg.py index 70249546d1a..3f2b8082d16 100644 --- a/src/sage/algebras/lie_algebras/heisenberg.py +++ b/src/sage/algebras/lie_algebras/heisenberg.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Heisenberg Algebras @@ -139,6 +140,25 @@ def _latex_term(self, m): return m return "%s_{%s}"%(m[0], m[1:]) # else it is of length at least 2 + def _unicode_art_term(self, m): + r""" + Return a unicode art representation of the term indexed by ``m``. + + EXAMPLES:: + + sage: H = lie_algebras.Heisenberg(QQ, 10) + sage: H._unicode_art_term('p1') + p₁ + sage: H._unicode_art_term('z') + z + sage: unicode_art(H.p(10)) + p₁₀ + """ + from sage.typeset.unicode_art import unicode_art, unicode_subscript + if len(m) == 1: + return unicode_art(m) + return unicode_art(str(m[0]) + unicode_subscript(m[1:])) # else it is of length at least 2 + def step(self): r""" Return the nilpotency step of ``self``. diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index a75ab2c3dec..ad1a76d123f 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -16,9 +16,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range -from six import iteritems - from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.structure.indexed_generators import standardize_names_index_set @@ -713,9 +710,9 @@ def _from_dict(self, d, coerce=False, remove_zeros=True): assert isinstance(d, dict) if coerce: R = self.base_ring() - d = {key: R(coeff) for key,coeff in iteritems(d)} + d = {key: R(coeff) for key,coeff in d.items()} if remove_zeros: - d = {key: coeff for key, coeff in iteritems(d) if coeff} + d = {key: coeff for key, coeff in d.items() if coeff} return self.element_class(self, d) def monomial(self, i): diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx index d519d37b618..9bd9b668f0e 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx @@ -20,7 +20,7 @@ AUTHORS: from copy import copy from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.combinat.free_module import CombinatorialFreeModule from sage.structure.element cimport have_same_parent, parent from sage.structure.coerce cimport coercion_model @@ -231,76 +231,35 @@ cdef class LieAlgebraElementWrapper(ElementWrapper): True sage: L.zero() < 0 False - """ - - def _repr_(self): - """ - Return a string representation of ``self``. - - EXAMPLES:: - - sage: R = FreeAlgebra(QQ, 3, 'x,y,z') - sage: L. = LieAlgebra(associative=R.gens()) - sage: x + y - x + y - """ - return repr(self.value) - - def _latex_(self): - r""" - Return a `\LaTeX` representation of ``self``. - - EXAMPLES:: - - sage: R = FreeAlgebra(QQ, 3, 'x') - sage: L. = LieAlgebra(associative=R.gens()) - sage: latex(x0 + x1) - x_{0} + x_{1} - """ - from sage.misc.latex import latex - return latex(self.value) - - def _ascii_art_(self): - """ - Return an ascii art representation of ``self``. - - EXAMPLES:: - - sage: s = SymmetricFunctions(QQ).s() - sage: L = LieAlgebra(associative=s) - sage: P = Partition([4,2,2,1]) - sage: x = L.basis()[P] - sage: ascii_art(x) - s - **** - ** - ** - * - """ - from sage.typeset.ascii_art import ascii_art - return ascii_art(self.value) - - def _unicode_art_(self): - """ - Return a unicode art representation of ``self``. - - EXAMPLES:: - - sage: s = SymmetricFunctions(QQ).s() - sage: L = LieAlgebra(associative=s) - sage: P = Partition([4,2,2,1]) - sage: x = L.basis()[P] - sage: unicode_art(x) - s - ┌┬┬┬┐ - ├┼┼┴┘ - ├┼┤ - ├┼┘ - └┘ - """ - from sage.typeset.unicode_art import unicode_art - return unicode_art(self.value) + We check the display of elements:: + + sage: R = FreeAlgebra(QQ, 3, 'x') + sage: L. = LieAlgebra(associative=R.gens()) + sage: elt = l0 + l1 + sage: elt + x0 + x1 + sage: latex(elt) + x_{0} + x_{1} + + sage: s = SymmetricFunctions(QQ).s() + sage: L = LieAlgebra(associative=s) + sage: P = Partition([4,2,2,1]) + sage: x = L.basis()[P] + sage: ascii_art(x) + s + **** + ** + ** + * + sage: unicode_art(x) + s + ┌┬┬┬┐ + ├┼┼┴┘ + ├┼┤ + ├┼┘ + └┘ + """ def __nonzero__(self): """ Return if ``self`` is non-zero. @@ -403,7 +362,7 @@ cdef class LieAlgebraElementWrapper(ElementWrapper): right = ( right).lift() return left * right - def __div__(self, x): + def __truediv__(self, x): """ Division by coefficients. @@ -766,6 +725,37 @@ cdef class StructureCoefficientsElement(LieAlgebraMatrixWrapper): repr_monomial=self._parent._latex_term, is_latex=True, strip_one=True) + def _ascii_art_(self): + r""" + Return an ascii art representation of ``self``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) + sage: ascii_art(x - 3/2 * y) + x - 3/2*y + """ + from sage.typeset.ascii_art import ascii_art + return ascii_art(repr_lincomb(self._sorted_items_for_printing(), + scalar_mult=ascii_art(self._parent._print_options['scalar_mult']), + repr_monomial=ascii_art, + strip_one=True)) + + def _unicode_art_(self): + r""" + Return a unicode art representation of ``self``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) + sage: unicode_art(x - 3/2 * y) + x - 3/2·y + """ + from sage.typeset.unicode_art import unicode_art + return unicode_art(repr_lincomb(self._sorted_items_for_printing(), + scalar_mult='·', + strip_one=True)) + cpdef bracket(self, right): """ Return the Lie bracket ``[self, right]``. @@ -937,6 +927,52 @@ cdef class UntwistedAffineLieAlgebraElement(Element): return (_build_untwisted_affine_element, (self._parent, self._t_dict, self._c_coeff, self._d_coeff)) + def _repr_generic(self, style, coeff, t_disp, mult, tensor_symb): + """ + Return a representation of ``self`` based on ``style``. + + INPUT: + + - ``style`` -- a function for how to convert the objects + - ``coeff`` -- a function for how to display the coefficients + - ``t_disp`` -- a function for how to display the powers of `t` + - ``mult`` -- the multiplication symbol; must be compatible + with ``style`` + - ``tensor_symb`` -- the tensor symbol; must be compatible + with ``style`` + """ + ret = style('') + mult = style(mult) + tensor_symb = style(tensor_symb) + for t,g in self._t_dict.iteritems(): + if ret: + ret += style(' + ') + if coeff == str: + # We need to special case this because of the necessary added + # comma by Python + ret += "({})".format(g) + tensor_symb + style(t_disp(t)) + else: + ret += coeff((g,)) + tensor_symb + style(t_disp(t)) + if self._c_coeff != 0: + if ret: + ret += style(' + ') + if self._c_coeff != 1: + ret += coeff(self._c_coeff) + mult + style('c') + else: + ret += style('c') + + if self._d_coeff != 0: + if ret: + ret += style(' + ') + if self._d_coeff != 1: + ret += coeff(self._d_coeff) + mult + style('d') + else: + ret += style('d') + + if not ret: + return style('0') + return ret + def _repr_(self): """ Return a string representation of ``self``. @@ -964,27 +1000,7 @@ cdef class UntwistedAffineLieAlgebraElement(Element): (E[alpha[1]] - h1 + 2*E[-alpha[1]])#t^0 + (E[-alpha[1]])#t^1 + 3*c + -2*d """ - ret = ' + '.join('({})#t^{}'.format(g, t) - for t,g in self._t_dict.iteritems()) - if self._c_coeff != 0: - if ret: - ret += ' + ' - if self._c_coeff != 1: - ret += repr(self._c_coeff) + '*c' - else: - ret += 'c' - - if self._d_coeff != 0: - if ret: - ret += ' + ' - if self._d_coeff != 1: - ret += repr(self._d_coeff) + '*d' - else: - ret += 'd' - - if not ret: - return '0' - return ret + return self._repr_generic(str, str, lambda t: "t^{}".format(t), '*', '#') def _latex_(self): r""" @@ -994,47 +1010,54 @@ cdef class UntwistedAffineLieAlgebraElement(Element): sage: L = lie_algebras.Affine(QQ, ['A',1,1]) sage: [latex(g) for g in L.lie_algebra_generators()] - [(E_{\alpha_{1}}) \otimes t^{0}, - (E_{-\alpha_{1}}) \otimes t^{0}, - (E_{\alpha^\vee_{1}}) \otimes t^{0}, - (E_{-\alpha_{1}}) \otimes t^{1}, - (E_{\alpha_{1}}) \otimes t^{-1}, + [\left(E_{\alpha_{1}}\right) \otimes t^{0}, + \left(E_{-\alpha_{1}}\right) \otimes t^{0}, + \left(E_{\alpha^\vee_{1}}\right) \otimes t^{0}, + \left(E_{-\alpha_{1}}\right) \otimes t^{1}, + \left(E_{\alpha_{1}}\right) \otimes t^{-1}, c, d] sage: latex(L.an_element()) - (E_{\alpha_{1}} + E_{\alpha^\vee_{1}} + E_{-\alpha_{1}}) \otimes t^{0} - + (E_{-\alpha_{1}}) \otimes t^{1} + (E_{\alpha_{1}}) \otimes t^{-1} + \left(E_{\alpha_{1}} + E_{\alpha^\vee_{1}} + E_{-\alpha_{1}}\right) \otimes t^{0} + + \left(E_{-\alpha_{1}}\right) \otimes t^{1} + + \left(E_{\alpha_{1}}\right) \otimes t^{-1} + c + d sage: latex(L.zero()) 0 sage: e1,f1,h1,e0,f0,c,d = list(L.lie_algebra_generators()) sage: latex(e1 + 2*f1 - h1 + e0 + 3*c - 2*d) - (E_{\alpha_{1}} - E_{\alpha^\vee_{1}} + 2E_{-\alpha_{1}}) \otimes t^{0} - + (E_{-\alpha_{1}}) \otimes t^{1} + 3 c + -2 d + \left(E_{\alpha_{1}} - E_{\alpha^\vee_{1}} + 2E_{-\alpha_{1}}\right) \otimes t^{0} + + \left(E_{-\alpha_{1}}\right) \otimes t^{1} + 3 c + -2 d """ from sage.misc.latex import latex - ret = ' + '.join('({}) \otimes t^{{{}}}'.format(latex(g), t) - for t,g in self._t_dict.iteritems()) - if self._c_coeff != 0: - if ret: - ret += ' + ' - if self._c_coeff != 1: - ret += latex(self._c_coeff) + ' c' - else: - ret += 'c' + return self._repr_generic(str, latex, lambda t: "t^{{{}}}".format(t), ' ', ' \\otimes ') - if self._d_coeff != 0: - if ret: - ret += ' + ' - if self._d_coeff != 1: - ret += latex(self._d_coeff) + ' d' - else: - ret += 'd' + def _unicode_art_(self): + r""" + Return a unicode art representation of ``self``. - if not ret: - return '0' - return ret + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['A',1,1]) + sage: unicode_art([g for g in L.lie_algebra_generators()]) + [ ( alpha[1] )⊗t⁰, ( -alpha[1] )⊗t⁰, ( alphacheck[1] )⊗t⁰, ( -alpha[1] )⊗t¹, + + ( alpha[1] )⊗t⁻¹, c, d ] + sage: unicode_art(L.an_element()) + ( alpha[1] + alphacheck[1] + -alpha[1] )⊗t⁰ + ( -alpha[1] )⊗t¹ + ( alpha[1] )⊗ + + t⁻¹ + c + d + sage: unicode_art(L.zero()) + 0 + + sage: e1,f1,h1,e0,f0,c,d = list(L.lie_algebra_generators()) + sage: unicode_art(e1 + 2*f1 - h1 + e0 + 3*c - 2*d) + ( alpha[1] - alphacheck[1] + 2·-alpha[1] )⊗t⁰ + ( -alpha[1] )⊗t¹ + 3⋅c + -2⋅d + """ + from sage.typeset.unicode_art import unicode_art, unicode_superscript + return self._repr_generic(unicode_art, unicode_art, lambda t: "t" + unicode_superscript(t), + unicode_art('⋅'), unicode_art('⊗')) cpdef dict t_dict(self): r""" diff --git a/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py b/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py index ea8a7f3d837..b3ff2c8c82d 100644 --- a/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py +++ b/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py @@ -315,6 +315,7 @@ def _coerce_map_from_(self, R): True sage: L(prod(pbw.gens())) Traceback (most recent call last): + ... ValueError: PBW['X']*PBW['Y']*PBW['Z'] is not in the image sage: L(pbw.one()) Traceback (most recent call last): diff --git a/src/sage/algebras/lie_algebras/quotient.py b/src/sage/algebras/lie_algebras/quotient.py index e4ba075fb38..f4b7b87ecc2 100644 --- a/src/sage/algebras/lie_algebras/quotient.py +++ b/src/sage/algebras/lie_algebras/quotient.py @@ -119,9 +119,9 @@ class LieQuotient_finite_dimensional_with_basis(LieAlgebraWithStructureCoefficie Defining Y_1, Y_2, Y_3, Y_4, Y_5 sage: lcs = Q.lower_central_series() sage: [I.basis().list() for I in lcs] - [[Y_1, Y_2, Y_3, Y_4, Y_5], [Y_5, Y_4, Y_3], [Y_5, Y_4], [Y_5], []] + [[Y_1, Y_2, Y_3, Y_4, Y_5], [Y_3, Y_4, Y_5], [Y_4, Y_5], [Y_5], []] sage: Y_2.bracket(Y_3) - Y_5 + -Y_5 Quotients when the base ring is not a field are not implemented:: @@ -157,7 +157,7 @@ class LieQuotient_finite_dimensional_with_basis(LieAlgebraWithStructureCoefficie are different, see :trac:`26352`:: sage: L. = LieAlgebra(QQ, abelian=True) - sage: I2 = L.ideal([a+b, a+c]) + sage: I2 = L.ideal([a+b, a+c], order=sorted) sage: I2.basis() Family (b + a, c + a) sage: Q = L.quotient(I2) diff --git a/src/sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py b/src/sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py index 41ee438d63b..a208c5b5c1f 100644 --- a/src/sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py +++ b/src/sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Rank Two Heisenberg-Virasoro Algebras @@ -161,6 +162,29 @@ def _latex_term(self, m): return 't^{{({},{})}}'.format(m[1][0], m[1][1]) return 'E({},{})'.format(m[1][0], m[1][1]) + def _unicode_art_term(self, m): + r""" + Return a unicode art representation of the term indexed by ``m``. + + EXAMPLES:: + + sage: L = lie_algebras.RankTwoHeisenbergVirasoro(QQ) + sage: L = lie_algebras.RankTwoHeisenbergVirasoro(QQ) + sage: L._unicode_art_term(('K', 2)) + K₂ + sage: L._unicode_art_term(('t', (2,-4))) + t⁽²˴⁻⁴⁾ + sage: L._unicode_art_term(('E', (2,-4))) + E(2,-4) + """ + from sage.typeset.unicode_art import unicode_art, unicode_subscript, unicode_superscript + if m[0] == 'K': + return unicode_art('K' + unicode_subscript(m[1])) + if m[0] == 't': + return unicode_art('t⁽{}˴{}⁾'.format(unicode_superscript(m[1][0]), + unicode_superscript(m[1][1]))) + return unicode_art('E({},{})'.format(m[1][0], m[1][1])) + def _repr_(self): """ Return a string representation of ``self``. diff --git a/src/sage/algebras/lie_algebras/structure_coefficients.py b/src/sage/algebras/lie_algebras/structure_coefficients.py index d1d7c2ed98f..822da88a86a 100644 --- a/src/sage/algebras/lie_algebras/structure_coefficients.py +++ b/src/sage/algebras/lie_algebras/structure_coefficients.py @@ -16,8 +16,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from six import iteritems - from sage.misc.cachefunc import cached_method #from sage.misc.lazy_attribute import lazy_attribute from sage.structure.indexed_generators import (IndexedGenerators, @@ -422,7 +420,7 @@ def _sorted_items_for_printing(self): """ print_options = self.parent().print_options() pos_to_index = dict(enumerate(self.parent()._indices)) - v = [(pos_to_index[k], c) for k, c in iteritems(self.value)] + v = [(pos_to_index[k], c) for k, c in self.value.items()] try: v.sort(key=lambda monomial_coeff: print_options['sorting_key'](monomial_coeff[0]), diff --git a/src/sage/algebras/lie_algebras/subalgebra.py b/src/sage/algebras/lie_algebras/subalgebra.py index 4b6faa08e7f..660dc23bba6 100644 --- a/src/sage/algebras/lie_algebras/subalgebra.py +++ b/src/sage/algebras/lie_algebras/subalgebra.py @@ -100,7 +100,7 @@ class LieSubalgebra_finite_dimensional_with_basis(Parent, UniqueRepresentation): sage: I = L.ideal(X + Y, order=lambda s: ['Z','Y','X'].index(s)) sage: I.basis() - Family (X + Y, Z) + Family (Z, X + Y) sage: I.reduce(el) (-x+y)*Y @@ -127,7 +127,7 @@ class LieSubalgebra_finite_dimensional_with_basis(Parent, UniqueRepresentation): False sage: K = L.ideal(J.basis().list()) sage: K.basis() - Family (W, Z) + Family (Z, W) TESTS: @@ -229,7 +229,10 @@ def __init__(self, ambient, gens, ideal, order=None, category=None): # initialize helper variables for ordering if order is None: - order = lambda x: x + if hasattr(ambient, "_basis_key"): + order = ambient._basis_key + else: + order = lambda x: x self._order = order self._reversed_indices = sorted(ambient.indices(), key=order, reverse=True) @@ -431,13 +434,13 @@ def _to_m(self, X): sage: I._to_m(el) (3, 2, 1) - Otherwise the components can have a more complicated permutation:: + This follows the reverse order of the ambient basis order:: sage: L. = LieAlgebra(QQ, {('x','y'): {'z': 1}}) sage: I = L.ideal([x, z]) - sage: el = x + 2*z + 3*y + sage: el = x + 2*y + 3*z sage: el.to_vector() - (1, 2, 3) + (1, 3, 2) sage: I._to_m(el) (2, 3, 1) """ @@ -475,7 +478,7 @@ def _from_m(self, v): sage: L. = LieAlgebra(QQ, abelian=True) sage: S = L.subalgebra(L.basis().list()) sage: v = S._to_m(c + 2*a + 3*e + 4*f + 5*b + 6*d); v - (4, 3, 6, 1, 5, 2) + (6, 5, 4, 3, 2, 1) sage: S._from_m(v) c + 2*a + 3*e + 4*f + 5*b + 6*d sage: all(S._from_m(S._to_m(X)) == X for X in L.some_elements()) @@ -665,9 +668,21 @@ def basis(self): sage: sc = {('x','y'): {'z': 1}, ('x','z'): {'w': 1}} sage: L. = LieAlgebra(QQ, sc) sage: L.ideal([x + y + z + w]).basis() - Family (w, x + y, z) + Family (x + y, z, w) + + This also works for Lie algebras whose natural basis elements + are not comparable (but have a well-defined basis ordering):: + + sage: sl3 = LieAlgebra(QQ, cartan_type=['A',2]) + sage: D = sl3.derived_subalgebra() + sage: len(D.basis()) + 8 + sage: e = list(sl3.e()) + sage: sl3.ideal(e).dimension() + 8 + sage: sl3.subalgebra(e).dimension() + 3 """ - L = self.ambient() B = [self._to_m(X) for X in L.basis()] @@ -687,7 +702,7 @@ def basis(self): basis = [self.element_class(self, self._from_m(v)) for v in sm.echelonized_basis()] - sortkey = lambda X: self.lift(X).leading_support(key=self._order) + sortkey = lambda X: self._order(self.lift(X).leading_support(key=self._order)) return Family(sorted(basis, key=sortkey)) @cached_method @@ -712,9 +727,9 @@ def leading_monomials(self): sage: key = lambda s: ['d','c','b','a'].index(s) sage: I = L.ideal(a + b, order=key) sage: I.basis() - Family (a + b, 2*c, 4*d) + Family (4*d, 2*c, a + b) sage: I.leading_monomials() - Family (a, c, d) + Family (d, c, a) """ return Family(self.lift(X).leading_monomial(key=self._order) for X in self.basis()) diff --git a/src/sage/algebras/lie_algebras/verma_module.py b/src/sage/algebras/lie_algebras/verma_module.py index 7e21c1ed185..c59d964499f 100644 --- a/src/sage/algebras/lie_algebras/verma_module.py +++ b/src/sage/algebras/lie_algebras/verma_module.py @@ -11,15 +11,15 @@ and return as the ``construction()``. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2017 Travis Scrimshaw # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method @@ -651,7 +651,7 @@ def _acted_upon_(self, scalar, self_on_left=False): try: scalar = P._pbw(scalar) except (ValueError, TypeError): - # Cannot be made into a PBW element, so propogate it up + # Cannot be made into a PBW element, so propagate it up return CombinatorialFreeModule.Element._acted_upon_(self, scalar, self_on_left) @@ -733,10 +733,12 @@ def _repr_type(self): return "Verma module" def _repr_defn(self): - """ + r""" Return a string describing the definition of ``self``, to be used when printing ``self``. + EXAMPLES:: + sage: L = lie_algebras.sl(QQ, 3) sage: La = L.cartan_type().root_system().weight_lattice().fundamental_weights() sage: M = L.verma_module(La[1] + La[2]) diff --git a/src/sage/algebras/lie_algebras/virasoro.py b/src/sage/algebras/lie_algebras/virasoro.py index f0a88ca4763..90369cc0386 100644 --- a/src/sage/algebras/lie_algebras/virasoro.py +++ b/src/sage/algebras/lie_algebras/virasoro.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Virasoro Algebra and Related Lie Algebras @@ -414,6 +415,25 @@ def _latex_term(self, m): return m return IndexedGenerators._latex_generator(self, m) + def _unicode_art_term(self, m): + r""" + Return a unicode art representation of the term indexed by ``m``. + + EXAMPLES:: + + sage: d = lie_algebras.VirasoroAlgebra(QQ) + sage: d._unicode_art_term('c') + c + sage: d._unicode_art_term(2) + d₂ + sage: d._unicode_art_term(-13) + d₋₁₃ + """ + from sage.typeset.unicode_art import unicode_art, unicode_subscript + if isinstance(m, str): + return unicode_art(m) + return unicode_art('d' + unicode_subscript(m)) + def _repr_(self): """ Return a string representation of ``self``. diff --git a/src/sage/algebras/orlik_solomon.py b/src/sage/algebras/orlik_solomon.py index 3a1fb0725c1..893905af74d 100644 --- a/src/sage/algebras/orlik_solomon.py +++ b/src/sage/algebras/orlik_solomon.py @@ -2,7 +2,7 @@ Orlik-Solomon Algebras """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2015 William Slofstra # Travis Scrimshaw # @@ -10,14 +10,15 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.combinat.free_module import CombinatorialFreeModule from sage.categories.algebras import Algebras from sage.sets.family import Family + class OrlikSolomonAlgebra(CombinatorialFreeModule): r""" An Orlik-Solomon algebra. @@ -426,8 +427,8 @@ def subset_image(self, S): if j == i: switch = True return r - else: # So ``S`` is an NBC set. - return self.monomial(S) + # So ``S`` is an NBC set. + return self.monomial(S) def degree_on_basis(self, m): """ diff --git a/src/sage/algebras/orlik_terao.py b/src/sage/algebras/orlik_terao.py index 2b057faed0a..f28abab6a8e 100644 --- a/src/sage/algebras/orlik_terao.py +++ b/src/sage/algebras/orlik_terao.py @@ -2,7 +2,7 @@ Orlik-Terao Algebras """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2019 Travis Scrimshaw # # This program is free software: you can redistribute it and/or modify @@ -10,7 +10,7 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # https://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.combinat.free_module import CombinatorialFreeModule @@ -18,6 +18,7 @@ from sage.matrix.constructor import matrix from sage.sets.family import Family + class OrlikTeraoAlgebra(CombinatorialFreeModule): r""" An Orlik-Terao algebra. @@ -468,8 +469,8 @@ def subset_image(self, S): if coeff: r += mone**ind * R(coeff / lc) * self.subset_image(Si.difference({j})) return r - else: # So ``S`` is an NBC set. - return self.monomial(S) + # So ``S`` is an NBC set. + return self.monomial(S) @cached_method def _flat_module(self, F): diff --git a/src/sage/algebras/q_system.py b/src/sage/algebras/q_system.py index d6de08cc5d7..8103916d8f1 100644 --- a/src/sage/algebras/q_system.py +++ b/src/sage/algebras/q_system.py @@ -286,26 +286,17 @@ def _unicode_art_term(self, t): sage: unicode_art(Q.an_element()) 1 + 2*Q₁⁽¹⁾ + (Q₁⁽¹⁾)²(Q₁⁽²⁾)²(Q₁⁽³⁾)³ + 3*Q₁⁽²⁾ """ - from sage.typeset.unicode_art import UnicodeArt + from sage.typeset.unicode_art import UnicodeArt, unicode_subscript, unicode_superscript if t == self.one_basis(): return UnicodeArt(["1"]) - subs = {'0': u'₀', '1': u'₁', '2': u'₂', '3': u'₃', '4': u'₄', - '5': u'₅', '6': u'₆', '7': u'₇', '8': u'₈', '9': u'₉'} - sups = {'0': u'⁰', '1': u'¹', '2': u'²', '3': u'³', '4': u'⁴', - '5': u'⁵', '6': u'⁶', '7': u'⁷', '8': u'⁸', '9': u'⁹'} - def to_super(x): - return u''.join(sups[i] for i in str(x)) - def to_sub(x): - return u''.join(subs[i] for i in str(x)) - ret = UnicodeArt("") for k, exp in t._sorted_items(): a,m = k - var = UnicodeArt([u"Q" + to_sub(m) + u'⁽' + to_super(a) + u'⁾'], baseline=0) + var = UnicodeArt([u"Q" + unicode_subscript(m) + u'⁽' + unicode_superscript(a) + u'⁾'], baseline=0) if exp > 1: var = (UnicodeArt([u'('], baseline=0) + var - + UnicodeArt([u')' + to_super(exp)], baseline=0)) + + UnicodeArt([u')' + unicode_superscript(exp)], baseline=0)) ret += var return ret diff --git a/src/sage/algebras/quantum_matrix_coordinate_algebra.py b/src/sage/algebras/quantum_matrix_coordinate_algebra.py index 08bd9e7ddb0..498aa34811d 100644 --- a/src/sage/algebras/quantum_matrix_coordinate_algebra.py +++ b/src/sage/algebras/quantum_matrix_coordinate_algebra.py @@ -13,9 +13,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ############################################################################## -from six.moves import range from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute @@ -274,7 +273,6 @@ def product_on_basis(self, a, b): return self.monomial(a * b) G = self._indices.monoid_generators() one = self.base_ring().one() - ret = self.zero() q = self._q qi = q ** -1 monomial = b diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index fa357263b39..fed19456b1b 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -32,8 +32,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import zip -from six import integer_types from sage.arith.all import (hilbert_conductor_inverse, hilbert_conductor, factor, gcd, kronecker_symbol, valuation) @@ -47,6 +45,7 @@ from sage.rings.rational_field import is_RationalField, QQ from sage.rings.infinity import infinity from sage.rings.number_field.number_field import is_NumberField +from sage.rings.power_series_ring import PowerSeriesRing from sage.structure.category_object import normalize_names from sage.structure.parent_gens import ParentWithGens from sage.structure.parent import Parent @@ -204,14 +203,14 @@ def create_key(self, arg0, arg1=None, arg2=None, names='i,j,k'): sage: QuaternionAlgebra.create_key(-1,-1) (Rational Field, -1, -1, ('i', 'j', 'k')) - """ # QuaternionAlgebra(D) if arg1 is None and arg2 is None: K = QQ D = Integer(arg0) a, b = hilbert_conductor_inverse(D) - a = Rational(a); b = Rational(b) + a = Rational(a) + b = Rational(b) elif arg2 is None: # If arg0 or arg1 are Python data types, coerce them @@ -220,7 +219,7 @@ def create_key(self, arg0, arg1=None, arg2=None, names='i,j,k'): for a in [arg0,arg1]: if is_RingElement(a): L.append(a) - elif isinstance(a, integer_types): + elif isinstance(a, int): L.append(Integer(a)) elif isinstance(a, float): L.append(RR(a)) @@ -250,7 +249,6 @@ def create_key(self, arg0, arg1=None, arg2=None, names='i,j,k'): names = normalize_names(3, names) return (K, a, b, names) - def create_object(self, version, key, **extra_args): """ Create the object from the key (extra arguments are ignored). This is @@ -284,6 +282,7 @@ def is_QuaternionAlgebra(A): """ return isinstance(A, QuaternionAlgebra_abstract) + class QuaternionAlgebra_abstract(Algebra): def _repr_(self): """ @@ -292,7 +291,7 @@ def _repr_(self): sage: sage.algebras.quatalg.quaternion_algebra.QuaternionAlgebra_abstract(QQ)._repr_() 'Quaternion Algebra with base ring Rational Field' """ - return "Quaternion Algebra with base ring %s"%self.base_ring() + return "Quaternion Algebra with base ring %s" % self.base_ring() def ngens(self): """ @@ -1041,9 +1040,9 @@ def _magma_init_(self, magma): 'QuaternionAlgebra(_sage_[...],(_sage_[...]![-1, 0]),(_sage_[...]![0, 1]))' """ R = magma(self.base_ring()) - return 'QuaternionAlgebra(%s,%s,%s)'%(R.name(), - self._a._magma_init_(magma), - self._b._magma_init_(magma)) + return 'QuaternionAlgebra(%s,%s,%s)' % (R.name(), + self._a._magma_init_(magma), + self._b._magma_init_(magma)) def quaternion_order(self, basis, check=True): """ @@ -1885,8 +1884,10 @@ def quaternion_algebra(self): sage: I.quaternion_algebra() Quaternion Algebra (-1, -3) with base ring Rational Field """ - try: return self.__quaternion_algebra - except AttributeError: pass + try: + return self.__quaternion_algebra + except AttributeError: + pass A = self.__basis[0].parent() self.__quaternion_algebra = A return A @@ -2252,7 +2253,7 @@ def quadratic_form(self): def theta_series(self, B, var='q'): r""" - Return normalized theta series of self, as a power series over + Return normalized theta series of ``self``, as a power series over `\ZZ` in the variable ``var``, which is 'q' by default. The normalized theta series is by definition @@ -2286,11 +2287,13 @@ def theta_series(self, B, var='q'): if var == self.__theta_series.variable(): return self.__theta_series.add_bigoh(B) else: - ZZ[[var]](self.__theta_series.list()[:B+1]) + p_ring = self._theta_series.parent().change_var(var) + p_ring(self.__theta_series.list()[:B+1]) except AttributeError: pass v = self.theta_series_vector(B) - theta = ZZ[[var]](v.list()).add_bigoh(B) + p_ring = PowerSeriesRing(ZZ, var) + theta = p_ring(v.list()).add_bigoh(B) self.__theta_series = theta return theta @@ -2665,7 +2668,7 @@ def cyclic_right_subideals(self, p, alpha=None): # Here we could replace the ideal by an *equivalent* # ideal that works. This is always possible. # However, I haven't implemented that algorithm yet. - raise NotImplementedError("general algorithm not implemented (%s)"%msg) + raise NotImplementedError("general algorithm not implemented (%s)" % msg) Ai = A.basis_matrix()**(-1) AiB = Ai.change_ring(QQ) * IB diff --git a/src/sage/algebras/quatalg/quaternion_algebra_cython.pyx b/src/sage/algebras/quatalg/quaternion_algebra_cython.pyx index 01fb64c1868..d0d291ccdd8 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra_cython.pyx +++ b/src/sage/algebras/quatalg/quaternion_algebra_cython.pyx @@ -1,3 +1,5 @@ +# distutils: language = c++ +# distutils: libraries = gmp m ntl """ Optimized Cython code needed by quaternion algebras diff --git a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx index a578badf0df..061d6f33c9d 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx +++ b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx @@ -1,3 +1,5 @@ +# distutils: language = c++ +# distutils: libraries = gmp m ntl """ Elements of Quaternion Algebras @@ -263,26 +265,6 @@ cdef class QuaternionAlgebraElement_abstract(AlgebraElement): return int(self[0]) raise TypeError - def __long__(self): - """ - Try to coerce this quaternion to a Python long. - - EXAMPLES:: - - sage: A. = QuaternionAlgebra(-1,-2) - sage: long(A(-3)) - -3L - sage: long(A(-3/2)) - -1L - sage: long(-3 + i) - Traceback (most recent call last): - ... - TypeError - """ - if self.is_constant(): - return long(self[0]) - raise TypeError - def __float__(self): """ Try to coerce this quaternion to a Python float. diff --git a/src/sage/algebras/schur_algebra.py b/src/sage/algebras/schur_algebra.py index e1cabc71f0d..3b604fa442f 100644 --- a/src/sage/algebras/schur_algebra.py +++ b/src/sage/algebras/schur_algebra.py @@ -28,7 +28,6 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range import itertools diff --git a/src/sage/algebras/shuffle_algebra.py b/src/sage/algebras/shuffle_algebra.py index 47918c46052..86b8b8c1cac 100644 --- a/src/sage/algebras/shuffle_algebra.py +++ b/src/sage/algebras/shuffle_algebra.py @@ -115,9 +115,18 @@ class ShuffleAlgebra(CombinatorialFreeModule): sage: R = ShuffleAlgebra(QQ,'xy') sage: R.is_commutative() True + + Check for a fix when using numbers as generators:: + + sage: A = algebras.Shuffle(QQ,[0,1]) + sage: A_d = A.dual_pbw_basis() + sage: W = A.basis().keys() + sage: x = A(W([0,1,0])) + sage: A_d(x) + -2*S[word: 001] + S[word: 010] """ @staticmethod - def __classcall_private__(cls, R, names): + def __classcall_private__(cls, R, names, prefix=None): """ Normalize input to ensure a unique representation. @@ -129,9 +138,12 @@ def __classcall_private__(cls, R, names): sage: F1 is F2 and F1 is F3 True """ - return super(ShuffleAlgebra, cls).__classcall__(cls, R, Alphabet(names)) + if prefix is None: + prefix = 'B' + return super(ShuffleAlgebra, cls).__classcall__(cls, R, + Alphabet(names), prefix) - def __init__(self, R, names): + def __init__(self, R, names, prefix): r""" Initialize ``self``. @@ -147,6 +159,11 @@ def __init__(self, R, names): Traceback (most recent call last): ... TypeError: argument R must be a ring + + sage: F = ShuffleAlgebra(QQ, 'xyz', prefix='f'); F + Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field + sage: F.gens() + Family (f[word: x], f[word: y], f[word: z]) """ if R not in Rings(): raise TypeError("argument R must be a ring") @@ -154,7 +171,7 @@ def __init__(self, R, names): self.__ngens = self._alphabet.cardinality() cat = GradedHopfAlgebrasWithBasis(R).Commutative().Connected() CombinatorialFreeModule.__init__(self, R, Words(names, infinite=False), - latex_prefix="", + latex_prefix="", prefix=prefix, category=cat) def variable_names(self): @@ -371,6 +388,12 @@ def algebra_generators(self): sage: A = ShuffleAlgebra(QQ, ['x1','x2']) sage: A.algebra_generators() Family (B[word: x1], B[word: x2]) + + TESTS:: + + sage: A = ShuffleAlgebra(ZZ,[0,1]) + sage: A.algebra_generators() + Family (B[word: 0], B[word: 1]) """ Words = self.basis().keys() return Family([self.monomial(Words([a])) for a in self._alphabet]) @@ -757,7 +780,7 @@ def algebra_generators(self): (S[word: a], S[word: b]) """ W = self.basis().keys() - return tuple(self.monomial(W(a)) for a in self._alphabet) + return tuple(self.monomial(W([a])) for a in self._alphabet) gens = algebra_generators @@ -933,7 +956,7 @@ def expansion_on_basis(self, w): return self._alg.monomial(w) if w.is_lyndon(): W = self.basis().keys() - letter = W(w[0]) + letter = W([w[0]]) expansion = self.expansion_on_basis(W(w[1:])) return self._alg.sum_of_terms((letter * i, c) for i, c in expansion) diff --git a/src/sage/algebras/splitting_algebra.py b/src/sage/algebras/splitting_algebra.py new file mode 100644 index 00000000000..2cb6dc30bb6 --- /dev/null +++ b/src/sage/algebras/splitting_algebra.py @@ -0,0 +1,777 @@ +# -*- coding: utf-8 -*- +r""" +Splitting Algebras + +*Splitting algebras* have been considered by Dan Laksov, Anders Thorup, +Torsten Ekedahl and others (see references below) in order to study +intersection theory of Grassmann and other flag schemes. Similarily as +*splitting fields* they can be considered as extensions of rings containing +all the roots of a given monic polynomial over that ring under the +assumption that its Galois group is the symmetric group of order equal +to the polynomial's degree. + +Thus they can be used as a tool to express elements of a ring generated by +`n` indeterminates in terms of symmetric functions in these indeterminates. + +This realization of splitting algebras follows the approach of a recursive +quotient ring construction splitting off some linear factor of the +polynomial in each recursive step. Accordingly it is inherited from +:class:`PolynomialQuotientRing_domain`. + +AUTHORS: + +- Sebastian Oehms (April 2020): initial version +""" + +# **************************************************************************** +# Copyright (C) 2020 Sebastian Oehms +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + + +from warnings import warn + +from sage.misc.verbose import verbose +from sage.misc.cachefunc import cached_method +from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing_domain +from sage.rings.polynomial.polynomial_quotient_ring_element import PolynomialQuotientRingElement + + +# ------------------------------------------------------------------------------------------------------------------ +# Element class for the splitting algebra +# -------------------------------------------------------------------------------------------------------- +class SplittingAlgebraElement(PolynomialQuotientRingElement): + r""" + Element class for :class:`SplittingAlgebra`. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: cp6 = cyclotomic_polynomial(6) + sage: CR6. = SplittingAlgebra(cp6) + sage: type(e6) + + + sage: type(CR6(5)) + + """ + def __invert__(self): + r""" + Return the inverse of ``self``. + + Support inversion of special elements attached to the construction + of the parent and which are recorded in the list + ``self.parent()._invertible_elements``. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: CR3. = SplittingAlgebra(cyclotomic_polynomial(3)) + sage: ~e3 + -e3 - 1 + sage: ~(e3 + 5) + Traceback (most recent call last): + ... + NotImplementedError: The base ring (=Integer Ring) is not a field + """ + inv_elements = self.parent()._invertible_elements + if self in inv_elements: + return inv_elements[self] + + return super(SplittingAlgebraElement, self).__invert__() + + + def is_unit(self): + r""" + Return ``True`` if ``self`` is invertible. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: CR3. = SplittingAlgebra(cyclotomic_polynomial(3)) + sage: e3.is_unit() + True + """ + inv_elements = self.parent()._invertible_elements + if self in inv_elements: + return True + + return super(SplittingAlgebraElement, self).is_unit() + + def dict(self): + r""" + Return the dictionary of ``self`` according to its lift to the cover. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: CR3. = SplittingAlgebra(cyclotomic_polynomial(3)) + sage: (e3 + 42).dict() + {0: 42, 1: 1} + """ + return self.lift().dict() + + +# ------------------------------------------------------------------------------------------------------------------ +# Parent class of the splitting algebra +# -------------------------------------------------------------------------------------------------------- +class SplittingAlgebra(PolynomialQuotientRing_domain): + r""" + For a given monic polynomial `p(t)` of degree `n` over a commutative + ring `R`, the splitting algebra is the universal `R`-algebra in which + `p(t)` has `n` roots, or, more precisely, over which `p(t)` factors, + + .. MATH:: + + p(t) = (t - \xi_1) \cdots (t - \xi_n). + + This class creates an algebra as extension over the base ring of a + given polynomial `p` such that `p` splits into linear factors over + that extension. It is assumed (and not checked in general) that the + Galois group of `p` is the symmetric Group `S(n)`. The construction + is recursive (following [LT2012]_, 1.3). + + INPUT: + + - ``monic_polynomial`` -- the monic polynomial which should be split + - ``names`` -- names for the indeterminates to be adjoined to the + base ring of ``monic_polynomial`` + - ``warning`` -- (default: ``True``) can be used (by setting to ``False``) + to suppress a warning which will be thrown whenever it cannot be + checked that the Galois group of ``monic_polynomial`` is maximal + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: Lc. = LaurentPolynomialRing(ZZ) + sage: PabLc. = Lc[]; t = polygen(PabLc) + sage: S. = SplittingAlgebra(t^3 - u*t^2 + v*t - w) + doctest:...: UserWarning: Asuming x^3 - u*x^2 + v*x - w to have maximal + Galois group! + + sage: roots = S.splitting_roots(); roots + [x, y, -y - x + u] + sage: all(t^3 -u*t^2 +v*t -w == 0 for t in roots) + True + sage: xi = ~x; xi + (w^-1)*x^2 + ((-w^-1)*u)*x + (w^-1)*v + sage: ~xi == x + True + sage: ~y + ((-w^-1)*x)*y + (-w^-1)*x^2 + ((w^-1)*u)*x + sage: zi = ((w^-1)*x)*y; ~zi + -y - x + u + + sage: cp3 = cyclotomic_polynomial(3).change_ring(GF(5)) + sage: CR3. = SplittingAlgebra(cp3) + sage: CR3.is_field() + True + sage: CR3.cardinality() + 25 + sage: F. = cp3.splitting_field() + sage: F.cardinality() + 25 + sage: E3 = cp3.change_ring(F).roots()[0][0]; E3 + 3*a + 3 + sage: f = CR3.hom([E3]); f + Ring morphism: + From: Splitting Algebra of x^2 + x + 1 + with roots [e3, 4*e3 + 4] + over Finite Field of size 5 + To: Finite Field in a of size 5^2 + Defn: e3 |--> 3*a + 3 + + REFERENCES: + + - [EL2002]_ + - [Lak2010]_ + - [Tho2011]_ + - [LT2012]_ + """ + Element = SplittingAlgebraElement + + def __init__(self, monic_polynomial, names='X', iterate=True, warning=True): + r""" + Python constructor. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: Lw. = LaurentPolynomialRing(ZZ) + sage: PuvLw. = Lw[]; t = polygen(PuvLw) + sage: S. = SplittingAlgebra(t^3 - u*t^2 + v*t - w, warning=False) + sage: TestSuite(S).run() + """ + + # --------------------------------------------------------------------------------- + # checking input parameters + # --------------------------------------------------------------------------------- + + base_ring = monic_polynomial.base_ring() + if not monic_polynomial.is_monic(): + raise ValueError("given polynomial must be monic") + deg = monic_polynomial.degree() + + from sage.structure.category_object import normalize_names + self._root_names = normalize_names(deg-1, names) + root_names = list(self._root_names) + verbose("Create splitting algebra to base ring %s and polynomial %s (%s %s)" + % (base_ring, monic_polynomial, iterate, warning)) + + self._defining_polynomial = monic_polynomial + self._iterate = iterate + + try: + if not base_ring.is_integral_domain(): + raise TypeError("base_ring must be an integral domain") + except NotImplementedError: + from sage.rings.ring import Ring + if not isinstance(base_ring, Ring): + raise TypeError("base_ring must be an instance of ring") + if warning: + warn('Assuming %s to be an integral domain!' % (base_ring)) + + if deg < 1: + raise ValueError("the degree of the polynomial must positive") + + self._splitting_roots = [] + self._coefficients_list = [] + self._invertible_elements = {} + + if isinstance(base_ring, SplittingAlgebra): + self._invertible_elements = base_ring._invertible_elements + + # ------------------------------------------------------------------------------------ + # taking next root_name + # ------------------------------------------------------------------------------------ + root_name = root_names[0] + p = monic_polynomial.change_variable_name(root_name) + P = p.parent() + + self._set_modulus_irreducible_ = False + try: + if not p.is_irreducible(): + raise ValueError("monic_polynomial must be irreducible") + except (NotImplementedError, AttributeError): + # assuming this has been checked mathematically before + self._set_modulus_irreducible_ = True + if warning: + warn('Asuming %s to have maximal Galois group!' % (monic_polynomial)) + warning = False # one warning must be enough + + verbose("P %s defined:" % (P)) + + if deg > 2 and iterate: + # ------------------------------------------------------------------------------------ + # successive solution via recursion (on base_ring_step) + # ------------------------------------------------------------------------------------ + base_ring_step = SplittingAlgebra(monic_polynomial, tuple(root_names), iterate=False, warning=False) + first_root = base_ring_step.gen() + + verbose("base_ring_step %s defined:" % (base_ring_step)) + + # ------------------------------------------------------------------------------------ + # splitting first root off + # ------------------------------------------------------------------------------------ + from copy import copy + root_names_reduces = copy(root_names) + root_names_reduces.remove(root_name) + + P = base_ring_step[root_names_reduces[0]] + p = P(monic_polynomial.dict()) + q, r = p.quo_rem( (P.gen()-first_root) ) + + verbose("Invoking recursion with: %s" % (q,)) + + SplittingAlgebra.__init__(self, q, root_names_reduces, warning=False) + + splitting_roots = base_ring_step._splitting_roots + self._splitting_roots + coefficients_list = base_ring_step._coefficients_list + self._coefficients_list + + verbose("Adding roots: %s" % (splitting_roots)) + + self._splitting_roots = splitting_roots + self._coefficients_list = coefficients_list + else: + PolynomialQuotientRing_domain.__init__(self, P, p, root_name) + + first_root = self.gen() + self._splitting_roots.append(first_root) + self._coefficients_list = [monic_polynomial.coefficients(sparse=False)] + + if not iterate: + verbose("pre ring defined splitting_roots: %s" % (self._splitting_roots)) + return + + verbose("final ring defined splitting_roots: %s" % (self._splitting_roots)) + + if deg == 2: + coefficients = monic_polynomial.coefficients(sparse=False) + lin_coeff = coefficients[1] + self._splitting_roots.append(-lin_coeff - first_root) + + self._root_names = names + self._splitting_roots = [self(root) for root in self._splitting_roots] + verbose("splitting_roots: %s embedded" % (self._splitting_roots)) + + + # ------------------------------------------------------------------------------------------- + # try to calculate inverses of the roots. This is possible if the original polynomial + # has an invertible constant term. For example let cf = [-w, v,-u, 1] that is + # p = h^3 -u*h^2 + v*h -w, than u = x + y + z, v = x*y + x*z + y*z, w = x*y*z. If + # w is invertible then 1/x = (v -(u-x)*x)/w, 1/y = (v -(u-y)*y)/w, 1/z = (v -(u-z)*z)/w + # ------------------------------------------------------------------------------------------- + # first find the polynomial with invertible constant coefficient + # ------------------------------------------------------------------------------------------- + cf0_inv = None + for cf in self._coefficients_list: + cf0 = cf[0] + try: + cf0_inv = ~(cf[0]) + cf0_inv = self(cf0_inv) + verbose("invertible coefficient: %s found" %(cf0_inv)) + break + except NotImplementedError: + verbose("constant coefficient: %s not invertibe" %(cf0)) + + + # ---------------------------------------------------------------------------------- + # assuming that cf splits into linear factors over self and the _splitting_roots + # are its roots we can calculate inverses + # ---------------------------------------------------------------------------------- + if cf0_inv is not None: + deg_cf = len(cf)-1 + pf = P(cf) + for root in self._splitting_roots: + check = self(pf) + if not check.is_zero(): + continue + root_inv = self.one() + for pos in range(deg_cf-1 ): + root_inv = (-1 )**(pos+1 ) * cf[deg_cf-pos-1 ] - root_inv * root + verbose("inverse %s of root %s" % (root_inv, root)) + root_inv = (-1 )**(deg_cf) * cf0_inv * root_inv + self._invertible_elements.update({root:root_inv}) + verbose("adding inverse %s of root %s" % (root_inv, root)) + invert_items = [(k,v) for k, v in self._invertible_elements.items()] + for k, v in invert_items: + self._invertible_elements.update({v: k}) + return + + + ####################################################################################################################### + # --------------------------------------------------------------------------------------------------------------------- + # overloaded inherited methods + # --------------------------------------------------------------------------------------------------------------------- + ####################################################################################################################### + def __reduce__(self): + r""" + Used in pickling. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: L. = LaurentPolynomialRing(ZZ); x = polygen(L) + sage: S = SplittingAlgebra(x^4 -t*x^3 - u*x^2 - v*x + w, ('X', 'Y', 'Z'), warning=False) + sage: S.__reduce__() + (, + (x^4 - t*x^3 - u*x^2 - v*x + w, ('X', 'Y', 'Z'), True, False)) + sage: S.base_ring().__reduce__() + (, + (Y^3 + (X - t)*Y^2 + (X^2 - t*X - u)*Y + X^3 - t*X^2 - u*X - v, + ('Y', 'Z'), + False, + False)) + + sage: TestSuite(S).run() + """ + defining_polynomial = self.defining_polynomial() + definig_coefficients = self._coefficients_list[0] + if defining_polynomial.coefficients(sparse=False) != definig_coefficients: + # case of factorization algebra (intermediate construction step) + par_pol = self.cover_ring() + defining_polynomial = par_pol(definig_coefficients) + return self.__class__, (defining_polynomial, self._root_names, self._iterate, False) + + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: L. = PolynomialRing(ZZ) + sage: t = polygen(L) + sage: Spl. = SplittingAlgebra(t^3 - (u^2-v)*t^2 + (v+u)*t - 1) + sage: Spl._repr_() + 'Splitting Algebra of x^3 + (-u^2 + v)*x^2 + (u + v)*x - 1 + with roots [S, T, -T - S + u^2 - v] + over Multivariate Polynomial Ring in u, v over Integer Ring' + sage: Spl.base_ring() # indirect doctest + Factorization Algebra of x^3 + (-u^2 + v)*x^2 + (u + v)*x - 1 + with roots [S] over Multivariate Polynomial Ring in u, v over Integer Ring + """ + if self.is_completely_split(): + return ('Splitting Algebra of %s with roots %s over %s' + % (self.defining_polynomial(), self.splitting_roots(), self.scalar_base_ring())) + else: + return ('Factorization Algebra of %s with roots %s over %s' + % (self.defining_polynomial(), self.splitting_roots(), self.scalar_base_ring())) + + def _first_ngens(self, n): + r""" + Used by the preparser for ``R. = ...``. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: L. = PolynomialRing(ZZ) + sage: t = polygen(L) + sage: S. = SplittingAlgebra(t^3 - (u^2-v)*t^2 + (v+u)*t - 1) # indirect doctest + sage: X.parent() + Splitting Algebra of x^3 + (-u^2 + v)*x^2 + (u + v)*x - 1 + with roots [X, Y, -Y - X + u^2 - v] + over Multivariate Polynomial Ring in u, v over Integer Ring + sage: S._first_ngens(4) + (X, Y, u, v) + """ + srts = self.splitting_roots() + k = len(srts)-1 + gens = srts[:k] + list(self.scalar_base_ring().gens()) + return tuple(gens[:n]) + + def _element_constructor_(self, x): + r""" + Make sure ``x`` is a valid member of ``self``, and return the constructed element. + + TESTS:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: L. = LaurentPolynomialRing(ZZ); x = polygen(L) + sage: S. = SplittingAlgebra(x^3 - u*x^2 + v*x - w) + sage: S(u + v) + u + v + sage: S(X*Y + X) + X*Y + X + sage: TestSuite(S).run() # indirect doctest + """ + if isinstance(x, SplittingAlgebraElement): + # coercion from covering fixes pickling problems + return self(x.lift()) + return super(SplittingAlgebra, self)._element_constructor_(x) + + def hom(self, im_gens, codomain=None, check=True, base_map=None): + r""" + This version keeps track with the special recursive structure + of :class:`SplittingAlgebra` + + Type ``Ring.hom?`` to see the general documentation of this method. + Here you see just special examples for the current class. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: L. = LaurentPolynomialRing(ZZ); x = polygen(L) + sage: S = SplittingAlgebra(x^3 - u*x^2 + v*x - w, ('X', 'Y')) + sage: P. = PolynomialRing(ZZ) + sage: F = FractionField(P) + sage: im_gens = [F(g) for g in [y, x, x + y + z, x*y+x*z+y*z, x*y*z]] + sage: f = S.hom(im_gens) + sage: f(u), f(v), f(w) + (x + y + z, x*y + x*z + y*z, x*y*z) + sage: roots = S.splitting_roots(); roots + [X, Y, -Y - X + u] + sage: [f(r) for r in roots] + [x, y, z] + """ + base_ring = self.base_ring() + + if not isinstance(im_gens, (list,tuple)): + im_gens = [im_gens] + + all_gens = self.gens_dict_recursive() + if len(im_gens) != len(all_gens): + return super(SplittingAlgebra, self).hom(im_gens, codomain=codomain, check=check, base_map=base_map) + + num_gens = len(self.gens()) + im_gens_start = [img for img in im_gens if im_gens.index(img) < num_gens] + im_gens_end = [img for img in im_gens if im_gens.index(img) >= num_gens] + + if not im_gens_end: + return super(SplittingAlgebra, self).hom(im_gens, codomain=codomain, check=check, base_map=base_map) + + verbose('base %s im_gens_end %s codomain %s check %s base_map %s' % (base_ring, im_gens_end, codomain, check, base_map)) + hom_on_base_recurs = base_ring.hom(im_gens_end, codomain=codomain, check=check, base_map=base_map) + verbose('hom_on_base_recurs %s' % (hom_on_base_recurs)) + + cover_ring = self.cover_ring() + hom_from_cover = cover_ring.hom(im_gens_start, codomain=codomain, check=check, base_map=hom_on_base_recurs) + lift = self.lifting_map() + return hom_from_cover*lift + + + + ####################################################################################################################### + # --------------------------------------------------------------------------------------------------------------------- + # local methods + # --------------------------------------------------------------------------------------------------------------------- + ####################################################################################################################### + + + ####################################################################################################################### + # --------------------------------------------------------------------------------------------------------------------- + # global methods + # --------------------------------------------------------------------------------------------------------------------- + ####################################################################################################################### + + def is_completely_split(self): + r""" + Return True if the defining polynomial of ``self`` splits into linear factors over ``self``. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: L. = LaurentPolynomialRing(ZZ); x = polygen(L) + sage: S. = SplittingAlgebra(x^3 - u*x^2 + v*x - w) + sage: S.is_completely_split() + True + sage: S.base_ring().is_completely_split() + False + """ + return len(self.splitting_roots()) >= self.defining_polynomial().degree() + + @cached_method + def lifting_map(self): + r""" + Return a section map from ``self`` to the cover ring. It is implemented according + to the same named method of :class:`~sage.rings.quotient_ring.QuotientRing_nc`. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: x = polygen(ZZ) + sage: S = SplittingAlgebra(x^2+1, ('I',)) + sage: lift = S.lifting_map() + sage: lift(5) + 5 + sage: r1, r2 =S.splitting_roots() + sage: lift(r1) + I + """ + from sage.rings.morphism import RingMap_lift + return RingMap_lift(self, self.cover_ring()) + + def splitting_roots(self): + r""" + Return the roots of the splitted equation. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: x = polygen(ZZ) + sage: S = SplittingAlgebra(x^2+1, ('I',)) + sage: S.splitting_roots() + [I, -I] + """ + return self._splitting_roots + + @cached_method + def scalar_base_ring(self): + r""" + Return the ring of scalars of ``self`` (considered as an algebra) + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: L. = LaurentPolynomialRing(ZZ) + sage: x = polygen(L) + sage: S = SplittingAlgebra(x^3 - u*x^2 + v*x - w, ('X', 'Y')) + sage: S.base_ring() + Factorization Algebra of x^3 - u*x^2 + v*x - w with roots [X] + over Multivariate Laurent Polynomial Ring in u, v, w over Integer Ring + sage: S.scalar_base_ring() + Multivariate Laurent Polynomial Ring in u, v, w over Integer Ring + """ + base_ring = self.base_ring() + if isinstance(base_ring, SplittingAlgebra): + if base_ring.is_completely_split(): + # another splitting algebra independent of self + return base_ring + else: + return base_ring.scalar_base_ring() + return base_ring + + @cached_method + def defining_polynomial(self): + r""" + Return the defining polynomial of ``self``. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import SplittingAlgebra + sage: L. = LaurentPolynomialRing(ZZ) + sage: x = polygen(L) + sage: S = SplittingAlgebra(x^3 - u*x^2 + v*x - w, ('X', 'Y')) + sage: S.defining_polynomial() + x^3 - u*x^2 + v*x - w + """ + base_ring = self.base_ring() + if isinstance(base_ring, SplittingAlgebra): + if base_ring.is_completely_split(): + # another splitting algebra independent of self + return self._defining_polynomial + else: + return base_ring.defining_polynomial() + return self._defining_polynomial + + +# -------------------------------------------------------------------------------------------- +# ============================================================================================ +# Utility function to create the roots of a polynomial in an appropriate extension ring +# ============================================================================================ +# -------------------------------------------------------------------------------------------- + +def solve_with_extension(monic_polynomial, root_names=None, var='x', flatten=False, warning=True): + r""" + Return all roots of a monic polynomial in its base ring or in an appropriate + extension ring, as far as possible. + + INPUT: + + - ``monic_polynomial`` -- the monic polynomial whose roots should be created + - ``root_names`` -- names for the indeterminates needed to define the + splitting algebra of the ``monic_polynomial`` (if necessary and possible) + - ``var`` -- (default: ``'x'``) for the indeterminate needed to define the + splitting field of the ``monic_polynomial`` (if necessary and possible) + - ``flatten`` -- (default: ``True``) if ``True`` the roots will not be + given as a list of pairs ``(root, multiplicity)`` but as a list of + roots repeated according to their multiplicity + - ``warning`` -- (default: ``True``) can be used (by setting to ``False``) + to suppress a warning which will be thrown whenever it cannot be checked + that the Galois group of ``monic_polynomial`` is maximal + + OUTPUT: + + List of tuples ``(root, multiplicity)`` respectively list of roots repeated + according to their multiplicity if option ``flatten`` is ``True``. + + EXAMPLES:: + + sage: from sage.algebras.splitting_algebra import solve_with_extension + sage: t = polygen(ZZ) + sage: p = t^2 -2*t +1 + sage: solve_with_extension(p, flatten=True ) + [1, 1] + sage: solve_with_extension(p) + [(1, 2)] + + sage: cp5 = cyclotomic_polynomial(5, var='T').change_ring(UniversalCyclotomicField()) + sage: solve_with_extension(cp5) + [(E(5), 1), (E(5)^4, 1), (E(5)^2, 1), (E(5)^3, 1)] + sage: _[0][0].parent() + Universal Cyclotomic Field + """ + def create_roots(monic_polynomial, warning=True): + r""" + This internal function creates all roots of a polynomial in an + appropriate extension ring assuming that none of the roots is + contained its base ring. + + It first tries to create the splitting field of the given polynomial. + If this is not faithful the splitting algebra will be created. + + INPUT: + + - ``monic_polynomial`` -- the monic polynomial whose roots should + be created + - ``warning`` -- (default: ``True``) can be used (by setting to ``False``) + to suppress a warning which will be thrown whenever it cannot be + checked that the Galois group of ``monic_polynomial`` is maximal + """ + parent = monic_polynomial.parent() + base_ring = parent.base_ring() + + try: + ext_field, embed = monic_polynomial.splitting_field(var, map=True) + + if embed.domain() != base_ring: + # in this case the SplittingAlgebra is preferred + raise NotImplementedError + + # ------------------------------------------------------------------------------------- + # in some cases the embedding of the base_ring in ext_field can not be obtained + # as coercion + # ------------------------------------------------------------------------------------- + reset_coercion = False + from sage.rings.number_field.number_field import NumberField_generic + if isinstance(base_ring, NumberField_generic): + reset_coercion = True + elif base_ring.is_finite() and not base_ring.is_prime_field(): + reset_coercion = True + if reset_coercion: + ext_field._unset_coercions_used() + ext_field.register_coercion(embed) + ext_field.register_conversion(embed) + + verbose("splitting field %s defined" % (ext_field)) + pol_emb = monic_polynomial.change_ring(ext_field) + roots = pol_emb.roots() + except NotImplementedError: + ext_ring = SplittingAlgebra(monic_polynomial, name_list, warning=warning) + verbose("splitting algebra %s defined" % (ext_ring)) + roots = [(r, 1) for r in ext_ring.splitting_roots()] + return roots + + + deg_pol = monic_polynomial.degree() + if not root_names: + from sage.structure.category_object import normalize_names + root_names = normalize_names(deg_pol-1, 'r') + name_list = list(root_names) + root_list = [] + try: + root_list = monic_polynomial.roots() + except (TypeError, ValueError, NotImplementedError): + pass + + if not root_list: + # ------------------------------------------------------------------------------ + # no roots found: find roots in an appropriate extension ring + # ------------------------------------------------------------------------------ + verbose("no roots in base_ring") + if len(name_list) > deg_pol -1: + name_list = [name_list[i] for i in range(deg_pol-1)] + roots = create_roots(monic_polynomial, warning=warning) + + else: + # ------------------------------------------------------------------------------ + # root calculation was possible but maybe some more roots in an apropriate + # extension ring can be constructed. + # ------------------------------------------------------------------------------ + num_roots = sum(m for r, m in root_list) + if num_roots < deg_pol: + h = monic_polynomial.variables()[0] + divisor = monic_polynomial.base_ring().one() + for r, m in root_list: + divisor *= (h - r)**m + q, r = monic_polynomial.quo_rem(divisor) + if len(name_list) > deg_pol - num_roots - 1: + name_list = [name_list[i] for i in range(deg_pol - num_roots - 1)] + verbose("%d root found in base ring, now solving %s" % (num_roots,q)) + missing_roots = create_roots(q, warning=True) + roots = root_list + missing_roots + else: + roots = root_list + verbose("all roots in base ring") + + if flatten: + from sage.misc.flatten import flatten + return flatten([[rt]*m for rt, m in roots]) + return roots + diff --git a/src/sage/algebras/steenrod/__init__.py b/src/sage/algebras/steenrod/__init__.py index 588f9f3b430..e69de29bb2d 100644 --- a/src/sage/algebras/steenrod/__init__.py +++ b/src/sage/algebras/steenrod/__init__.py @@ -1,2 +0,0 @@ -from __future__ import absolute_import -from . import all diff --git a/src/sage/algebras/steenrod/steenrod_algebra.py b/src/sage/algebras/steenrod/steenrod_algebra.py index 6c41164a5f8..ab3ea32e922 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra.py +++ b/src/sage/algebras/steenrod/steenrod_algebra.py @@ -452,7 +452,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range from sage.combinat.free_module import CombinatorialFreeModule from sage.misc.lazy_attribute import lazy_attribute @@ -1333,8 +1332,8 @@ def coprod_list(t): all_q = Set(t[0]) tens_q = {} for a in all_q.subsets(): - left_q = sorted(list(a)) - right_q = sorted(list(all_q - a)) + left_q = sorted(a) + right_q = sorted(all_q - a) sign = Permutation(convert_perm(left_q + right_q)).signature() tens_q[(tuple(left_q), tuple(right_q))] = sign tens = {} diff --git a/src/sage/algebras/steenrod/steenrod_algebra_bases.py b/src/sage/algebras/steenrod/steenrod_algebra_bases.py index 5df87a0558b..1bcc5f92cee 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_bases.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_bases.py @@ -1117,7 +1117,7 @@ def steenrod_basis_error_check(dim, p, **kwds): sage: steenrod_basis_error_check(40,3) # long time sage: steenrod_basis_error_check(80,5) # long time """ - import sage.misc.misc as misc + from sage.misc.verbose import verbose generic = kwds.get('generic', False if p==2 else True ) if not generic: @@ -1131,7 +1131,7 @@ def steenrod_basis_error_check(dim, p, **kwds): for i in range(dim): if i % 5 == 0: - misc.verbose("up to dimension %s"%i) + verbose("up to dimension %s"%i) milnor_dim = len(steenrod_algebra_basis.f(i,'milnor',p=p,generic=generic)) for B in bases: if milnor_dim != len(steenrod_algebra_basis.f(i,B,p,generic=generic)): @@ -1140,7 +1140,7 @@ def steenrod_basis_error_check(dim, p, **kwds): if mat.nrows() != 0 and not mat.is_invertible(): print("%s invertibility problem in dim %s at p=%s" % (B, i, p)) - misc.verbose("done checking, no profiles") + verbose("done checking, no profiles") bases = ('pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz') if not generic: @@ -1150,11 +1150,11 @@ def steenrod_basis_error_check(dim, p, **kwds): for i in range(dim): if i % 5 == 0: - misc.verbose("up to dimension %s"%i) + verbose("up to dimension %s"%i) for pro in profiles: milnor_dim = len(steenrod_algebra_basis.f(i,'milnor',p=p,profile=pro,generic=generic)) for B in bases: if milnor_dim != len(steenrod_algebra_basis.f(i,B,p,profile=pro,generic=generic)): print("problem with milnor/%s in dimension %s with profile %s" % (B, i, pro)) - misc.verbose("done checking with profiles") + verbose("done checking with profiles") diff --git a/src/sage/algebras/steenrod/steenrod_algebra_mult.py b/src/sage/algebras/steenrod/steenrod_algebra_mult.py index 99c77be38cd..2215c243520 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_mult.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_mult.py @@ -197,7 +197,6 @@ # Copyright (C) 2008-2010 John H. Palmieri # Distributed under the terms of the GNU General Public License (GPL) #***************************************************************************** -from six.moves import range from sage.misc.cachefunc import cached_function diff --git a/src/sage/algebras/tensor_algebra.py b/src/sage/algebras/tensor_algebra.py index fb3cad1d8ed..d9e751f68e3 100644 --- a/src/sage/algebras/tensor_algebra.py +++ b/src/sage/algebras/tensor_algebra.py @@ -187,12 +187,14 @@ def _ascii_art_term(self, m): ** * sage: s = TA([Partition([3,2,2,1])]*2 + [Partition([3])]*3 + [Partition([1])]*2).leading_support() - sage: TA._ascii_art_term(s) + sage: t = TA._ascii_art_term(s); t B # B # B # B # B # B # B *** *** *** *** *** * * ** ** ** ** * * + sage: t._breakpoints + [7, 14, 21, 28, 35, 40] sage: I = TA.indices() sage: TA._ascii_art_term(I.one()) @@ -200,23 +202,14 @@ def _ascii_art_term(self, m): """ if len(m) == 0: return '1' - from sage.typeset.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt, ascii_art symb = self._print_options['tensor_symbol'] if symb is None: symb = tensor.symbol M = self._base_module - - it = iter(m._monomial) - k, e = next(it) - rpr = M._ascii_art_term(k) - for i in range(e-1): - rpr += AsciiArt([symb], [len(symb)]) - rpr += M._ascii_art_term(k) - for k,e in it: - for i in range(e): - rpr += AsciiArt([symb], [len(symb)]) - rpr += M._ascii_art_term(k) - return rpr + return ascii_art(*(M._ascii_art_term(k) + for k, e in m._monomial for _ in range(e)), + sep=AsciiArt([symb], breakpoints=[len(symb)])) def _element_constructor_(self, x): """ diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index 95e12d460b1..183699e5e84 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -15,15 +15,15 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from six import iteritems from sage.misc.cachefunc import cached_method from sage.misc.latex import latex, LatexExpr +from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc_c import prod from sage.structure.richcmp import richcmp from sage.structure.element import AlgebraElement from sage.structure.unique_representation import UniqueRepresentation -from copy import copy +from sage.categories.action import Action from sage.categories.rings import Rings from sage.categories.algebras_with_basis import AlgebrasWithBasis from sage.sets.family import Family @@ -158,6 +158,7 @@ def repr_from_monomials(monomials, term_repr, use_latex=False): ret = term return ret + def repr_factored(w, latex_output=False): r""" Return a string representation of ``w`` with the `dx_i` generators @@ -297,6 +298,7 @@ def _latex_(self): def exp(e): return '^{{{}}}'.format(e) if e > 1 else '' + def term(m): R = self.parent()._poly_ring def half_term(mon, polynomial): @@ -360,7 +362,8 @@ def __neg__(self): sage: dy - (3*x - z)*dx dy + z*dx - 3*x*dx """ - return self.__class__(self.parent(), {m:-c for m, c in iteritems(self.__monomials)}) + return self.__class__(self.parent(), + {m:-c for m, c in self.__monomials.items()}) def _add_(self, other): """ @@ -376,14 +379,6 @@ def _add_(self, other): F = self.parent() return self.__class__(F, blas.add(self.__monomials, other.__monomials)) - d = copy(self.__monomials) - zero = self.parent().base_ring().zero() - for m, c in iteritems(other.__monomials): - d[m] = d.get(m, zero) + c - if d[m] == zero: - del d[m] - return self.__class__(self.parent(), d) - def _mul_(self, other): """ Return ``self`` multiplied by ``other``. @@ -574,8 +569,6 @@ def __truediv__(self, x): return self.__class__(F, {t: D[t]._divide_if_possible(x) for t in D}) - __div__ = __truediv__ - def factor_differentials(self): """ Return a dict representing ``self`` with the differentials @@ -625,6 +618,33 @@ def factor_differentials(self): ret[dx] += c * prod(g**e for e, g in zip(x, gens)) return ret + def diff(self, p): + """ + Apply this differential operator to a polynomial. + + INPUT: + + - ``p`` -- polynomial of the underlying polynomial ring + + OUTPUT: + + The result of the left action of the Weyl algebra on the polynomial + ring via differentiation. + + EXAMPLES:: + + sage: R. = QQ[] + sage: W = R.weyl_algebra() + sage: dx, dy = W.differentials() + sage: dx.diff(x^3) + 3*x^2 + sage: (dx*dy).diff(W(x^3*y^3)) + 9*x^2*y^2 + sage: (x*dx + dy + 1).diff(x^4*y^4 + 1) + 5*x^4*y^4 + 4*x^4*y^3 + 1 + """ + return self.parent().diff_action(self, p) + class DifferentialWeylAlgebra(Algebra, UniqueRepresentation): r""" @@ -816,7 +836,7 @@ def _element_constructor_(self, x): return self.element_class(self, {i: R(c) for i,c in x if R(c) != zero}) x = self._poly_ring(x) return self.element_class(self, {(tuple(m), t): c - for m, c in iteritems(x.dict())}) + for m, c in x.dict().items()}) def _coerce_map_from_(self, R): """ @@ -1054,5 +1074,95 @@ def zero(self): """ return self.element_class(self, {}) + @lazy_attribute + def diff_action(self): + """ + Left action of this Weyl algebra on the underlying polynomial ring by + differentiation. + + EXAMPLES:: + + sage: R. = QQ[] + sage: W = R.weyl_algebra() + sage: dx, dy = W.differentials() + sage: W.diff_action + Left action by Differential Weyl algebra of polynomials in x, y + over Rational Field on Multivariate Polynomial Ring in x, y over + Rational Field + sage: W.diff_action(dx^2 + dy + 1, x^3*y^3) + x^3*y^3 + 3*x^3*y^2 + 6*x*y^3 + """ + return DifferentialWeylAlgebraAction(self) + Element = DifferentialWeylAlgebraElement + +class DifferentialWeylAlgebraAction(Action): + """ + Left action of a Weyl algebra on its underlying polynomial ring by + differentiation. + + EXAMPLES:: + + sage: R. = QQ[] + sage: W = R.weyl_algebra() + sage: dx, dy = W.differentials() + sage: W.diff_action + Left action by Differential Weyl algebra of polynomials in x, y + over Rational Field on Multivariate Polynomial Ring in x, y over + Rational Field + + :: + + sage: g = dx^2 + x*dy + sage: p = x^5 + x^3 + y^2*x^2 + 1 + sage: W.diff_action(g, p) + 2*x^3*y + 20*x^3 + 2*y^2 + 6*x + + The action is a left action:: + + sage: h = dx*x + x*y + sage: W.diff_action(h, W.diff_action(g, p)) == W.diff_action(h*g, p) + True + + The action endomorphism of a differential operator:: + + sage: dg = W.diff_action(g); dg + Action of dx^2 + x*dy on Multivariate Polynomial Ring in x, y over + Rational Field under Left action by Differential Weyl algebra... + sage: dg(p) == W.diff_action(g, p) == g.diff(p) + True + """ + + def __init__(self, G): + """ + INPUT: + + - ``G`` -- Weyl algebra + + EXAMPLES:: + + sage: from sage.algebras.weyl_algebra import DifferentialWeylAlgebraAction + sage: W. = DifferentialWeylAlgebra(QQ) + sage: DifferentialWeylAlgebraAction(W) + Left action by Differential Weyl algebra of polynomials in x, y + over Rational Field on Multivariate Polynomial Ring in x, y over + Rational Field + """ + super().__init__(G, G.polynomial_ring(), is_left=True) + + def _act_(self, g, x): + """ + Apply a differential operator to a polynomial. + + EXAMPLES:: + + sage: W. = DifferentialWeylAlgebra(QQ) + sage: dx, dy = W.differentials() + sage: W.diff_action(dx^3 + dx, x^3*y^3 + x*y) + 3*x^2*y^3 + 6*y^3 + y + """ + f = g * x + D = {y: c for (y, dy), c in f.monomial_coefficients(copy=False).items() + if all(dyi == 0 for dyi in dy)} + return self.right_domain()(D) diff --git a/src/sage/all.py b/src/sage/all.py index 97f01b40055..b58640c6756 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -22,7 +22,7 @@ ....: 'IPython', 'prompt_toolkit', 'jedi', # sage dependencies ....: 'threading', 'multiprocessing', # doctest dependencies ....: '__main__', 'sage.doctest', # doctesting - ....: 'signal', 'enum', # may appear in Python 3 + ....: 'signal', 'enum', 'types' # may appear in Python 3 ....: ] sage: def is_not_allowed(frame): ....: module = inspect.getmodule(frame) @@ -57,12 +57,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -# Future statements which apply to this module. We delete the -# future globals because we do not want these to appear in the sage.all -# namespace. This deleting does not affect the parsing of this module. -from __future__ import absolute_import, division, print_function -del absolute_import, division, print_function - import os import sys import operator @@ -79,7 +73,8 @@ warnings.filterwarnings('ignore', category=ImportWarning) warnings.filterwarnings('ignore', category=ResourceWarning) else: - warnings.filters.remove(('ignore', None, DeprecationWarning, None, 0)) + deprecationWarning = ('ignore', None, DeprecationWarning, None, 0) + if deprecationWarning in warnings.filters: warnings.filters.remove(deprecationWarning) # The psutil swap_memory() function tries to collect some statistics # that may not be available and that we don't need. Hide the warnings @@ -91,7 +86,15 @@ # Ignore all deprecations from IPython etc. warnings.filterwarnings('ignore', category=DeprecationWarning, - module='.*(IPython|ipykernel|jupyter_client|jupyter_core|nbformat|notebook|ipywidgets|storemagic)') + module='.*(IPython|ipykernel|jupyter_client|jupyter_core|nbformat|notebook|ipywidgets|storemagic|jedi)') + +# scipy 1.18 introduced reprecation warnings on a number of things they are moving to +# numpy, e.g. DeprecationWarning: scipy.array is deprecated +# and will be removed in SciPy 2.0.0, use numpy.array instead +# This affects networkx 2.2 up and including 2.4 (cf. :trac:29766) +warnings.filterwarnings('ignore', category=DeprecationWarning, + module='.*(scipy|networkx)') + # Ignore collections.abc warnings, there are a lot of them but they are # harmless. warnings.filterwarnings('ignore', category=DeprecationWarning, @@ -207,13 +210,8 @@ from cysignals.alarm import alarm, cancel_alarm # Lazily import notebook functions and interacts (#15335) -lazy_import('sagenb.notebook.notebook_object', 'notebook') -lazy_import('sagenb.notebook.notebook_object', 'inotebook') -lazy_import('sagenb.notebook.sage_email', 'email') lazy_import('sage.interacts.debugger', 'debug') lazy_import('sage.interacts', 'all', 'interacts') -# interact decorator from SageNB (will be overridden by Jupyter) -lazy_import('sagenb.notebook.interact', 'interact') from copy import copy, deepcopy diff --git a/src/sage/arith/functions.pyx b/src/sage/arith/functions.pyx index 1c184b02a9e..74b95dee846 100644 --- a/src/sage/arith/functions.pyx +++ b/src/sage/arith/functions.pyx @@ -168,7 +168,6 @@ cpdef LCM_list(v): sage: LCM_list(Sequence(srange(100))) 0 - sage: from six.moves import range sage: LCM_list(range(100)) 0 diff --git a/src/sage/arith/long.pxd b/src/sage/arith/long.pxd index 887c6fde677..8c918cc30af 100644 --- a/src/sage/arith/long.pxd +++ b/src/sage/arith/long.pxd @@ -136,12 +136,11 @@ cdef inline bint integer_check_long(x, long* value, int* err) except -1: ....: def long_max(): ....: return smallInteger(LONG_MAX) ....: ''') - sage: import six - sage: types = (ZZ, QQ) + six.integer_types + sage: types = (ZZ, QQ, int) sage: L = [1, 12345, 10^9, 2^30, long_max()//9, long_max()//3, long_max()] sage: L += [-x for x in L] + [0, long_min()] sage: for v in L: - ....: for t in (Integer,) + six.integer_types: + ....: for t in (Integer, int): ....: assert check_long(t(v)) == v sage: check_long(2^100) Traceback (most recent call last): diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 2fa75704313..c4da8aa4a5f 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -14,7 +14,6 @@ # **************************************************************************** from __future__ import absolute_import, print_function -from six.moves import range import math import collections @@ -2023,8 +2022,8 @@ def xkcd(n=""): from sage.misc.html import html # import compatible with py2 and py3 - from six.moves.urllib.request import urlopen - from six.moves.urllib.error import HTTPError, URLError + from urllib.request import urlopen + from urllib.error import HTTPError, URLError data = None url = "http://dynamic.xkcd.com/api-0/jsonp/comic/{}".format(n) @@ -2450,8 +2449,8 @@ def factor(n, proof=None, int_=False, algorithm='pari', verbose=0, **kwds): - ``'pari'`` - (default) use the PARI c library - - ``'kash'`` - use KASH computer algebra system (requires the - optional kash package be installed) + - ``'kash'`` - use KASH computer algebra system (requires that + kash be installed) - ``'magma'`` - use Magma (requires magma be installed) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index e4e3d363972..61a98c0b16b 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -394,7 +394,6 @@ sage: sage.calculus.calculus.maxima('f1') f1 """ -from six import iteritems import re from sage.arith.all import algdep @@ -1409,15 +1408,15 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): ################################################################### def laplace(ex, t, s, algorithm='maxima'): r""" - Return the Laplace transform with respect to the variable `t` and + Return the Laplace transform with respect to the variable `t` and transform parameter `s`, if possible. - - If this function cannot find a solution, a formal function is returned. + + If this function cannot find a solution, a formal function is returned. The function that is returned may be viewed as a function of `s`. DEFINITION: - The Laplace transform of a function `f(t)`, defined for all real numbers + The Laplace transform of a function `f(t)`, defined for all real numbers `t \geq 0`, is the function `F(s)` defined by .. MATH:: @@ -1439,9 +1438,9 @@ def laplace(ex, t, s, algorithm='maxima'): - ``'sympy'`` - use SymPy - ``'giac'`` - use Giac - + .. NOTE:: - + The ``'sympy'`` algorithm returns the tuple (`F`, `a`, ``cond``) where `F` is the Laplace transform of `f(t)`, `Re(s)>a` is the half-plane of convergence, and ``cond`` are @@ -1528,17 +1527,17 @@ def laplace(ex, t, s, algorithm='maxima'): sage: laplace(heaviside(t-1), t, s) laplace(heaviside(t - 1), t, s) - - Heaviside step function can be handled with different interfaces. + + Heaviside step function can be handled with different interfaces. Try with giac:: - + sage: laplace(heaviside(t-1), t, s, algorithm='giac') e^(-s)/s - + Try with SymPy:: - + sage: laplace(heaviside(t-1), t, s, algorithm='sympy') - (e^(-s)/s, 0, True) + (e^(-s)/s, 0, True) TESTS: @@ -1548,7 +1547,7 @@ def laplace(ex, t, s, algorithm='maxima'): (t, s) sage: laplace(5*cos(3*t-2)*heaviside(t-2), t, s, algorithm='giac') 5*(s*cos(4)*e^(-2*s) - 3*e^(-2*s)*sin(4))/(s^2 + 9) - + Check unevaluated expression from Giac (it is locale-dependent, see :trac:`22833`):: @@ -1622,15 +1621,15 @@ def laplace(ex, t, s, algorithm='maxima'): def inverse_laplace(ex, s, t, algorithm='maxima'): r""" - Return the inverse Laplace transform with respect to the variable `t` and + Return the inverse Laplace transform with respect to the variable `t` and transform parameter `s`, if possible. - - If this function cannot find a solution, a formal function is returned. + + If this function cannot find a solution, a formal function is returned. The function that is returned may be viewed as a function of `t`. - DEFINITION: - - The inverse Laplace transform of a function `F(s)` is the function + DEFINITION: + + The inverse Laplace transform of a function `F(s)` is the function `f(t)`, defined by .. MATH:: @@ -1678,75 +1677,75 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): sage: inverse_laplace(1/(s^3+1), s, t) 1/3*(sqrt(3)*sin(1/2*sqrt(3)*t) - cos(1/2*sqrt(3)*t))*e^(1/2*t) + 1/3*e^(-t) - No explicit inverse Laplace transform, so one is returned formally a + No explicit inverse Laplace transform, so one is returned formally a function ``ilt``:: sage: inverse_laplace(cos(s), s, t) ilt(cos(s), s, t) - + Transform an expression involving a time-shift, via SymPy:: sage: inverse_laplace(1/s^2*exp(-s), s, t, algorithm='sympy') -(log(e^(-t)) + 1)*heaviside(t - 1) The same instance with Giac:: - + sage: inverse_laplace(1/s^2*exp(-s), s, t, algorithm='giac') (t - 1)*heaviside(t - 1) - + Transform a rational expression:: - + sage: inverse_laplace((2*s^2*exp(-2*s) - exp(-s))/(s^3+1), s, t, algorithm='giac') - -1/3*(sqrt(3)*e^(1/2*t - 1/2)*sin(1/2*sqrt(3)*(t - 1)) - cos(1/2*sqrt(3)*(t - 1))*e^(1/2*t - 1/2) + + -1/3*(sqrt(3)*e^(1/2*t - 1/2)*sin(1/2*sqrt(3)*(t - 1)) - cos(1/2*sqrt(3)*(t - 1))*e^(1/2*t - 1/2) + e^(-t + 1))*heaviside(t - 1) + 2/3*(2*cos(1/2*sqrt(3)*(t - 2))*e^(1/2*t - 1) + e^(-t + 2))*heaviside(t - 2) - + Dirac delta function can also be handled:: - + sage: inverse_laplace(1, s, t, algorithm='giac') dirac_delta(t) - + TESTS: Testing unevaluated expression from Maxima:: - + sage: var('t, s') (t, s) sage: inverse_laplace(exp(-s)/s, s, t) ilt(e^(-s)/s, s, t) - + Testing Giac:: sage: inverse_laplace(exp(-s)/s, s, t, algorithm='giac') heaviside(t - 1) - + Testing SymPy:: - + sage: inverse_laplace(exp(-s)/s, s, t, algorithm='sympy') - heaviside(t - 1) - + heaviside(t - 1) + Testing unevaluated expression from Giac:: - + sage: n = var('n') sage: inverse_laplace(1/s^n, s, t, algorithm='giac') ilt(1/(s^n), t, s) Try with Maxima:: - + sage: inverse_laplace(1/s^n, s, t, algorithm='maxima') ilt(1/(s^n), s, t) - + Try with SymPy:: - + sage: inverse_laplace(1/s^n, s, t, algorithm='sympy') t^(n - 1)*heaviside(t)/gamma(n) - + Testing unevaluated expression from SymPy:: - + sage: inverse_laplace(cos(s), s, t, algorithm='sympy') ilt(cos(s), t, s) - + Testing the same with Giac:: - + sage: inverse_laplace(cos(s), s, t, algorithm='giac') ilt(cos(s), t, s) """ @@ -1776,7 +1775,7 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): try: result = giac.invlaplace(ex, s, t) except TypeError: - raise ValueError("Giac cannot make sense of: %s" % ex) + raise ValueError("Giac cannot make sense of: %s" % ex) if 'ilaplace' in format(result): return dummy_inverse_laplace(ex, t, s) else: @@ -1856,7 +1855,7 @@ def at(ex, *args, **kwds): if not isinstance(ex, (Expression, Function)): ex = SR(ex) kwds = {(k[10:] if k[:10] == "_SAGE_VAR_" else k): v - for k, v in iteritems(kwds)} + for k, v in kwds.items()} if len(args) == 1 and isinstance(args[0], list): for c in args[0]: kwds[str(c.lhs())] = c.rhs() @@ -2250,7 +2249,7 @@ def maxima_options(**kwds): 'an_option=true,another=false,foo=bar' """ return ','.join('%s=%s' % (key, mapped_opts(val)) - for key, val in sorted(iteritems(kwds))) + for key, val in sorted(kwds.items())) # Parser for symbolic ring elements diff --git a/src/sage/calculus/functional.py b/src/sage/calculus/functional.py index feef8446e16..0864be59891 100644 --- a/src/sage/calculus/functional.py +++ b/src/sage/calculus/functional.py @@ -126,6 +126,26 @@ def derivative(f, *args, **kwds): 80*u^3*v^3 sage: derivative(f, [u, v, v]) 80*u^3*v^3 + + We differentiate a scalar field on a manifold:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: f = M.scalar_field(x^2*y, name='f') + sage: derivative(f) + 1-form df on the 2-dimensional differentiable manifold M + sage: derivative(f).display() + df = 2*x*y dx + x^2 dy + + We differentiate a differentiable form, getting its exterior derivative:: + + sage: a = M.one_form(-y, x, name='a'); a.display() + a = -y dx + x dy + sage: derivative(a) + 2-form da on the 2-dimensional differentiable manifold M + sage: derivative(a).display() + da = 2 dx/\dy + """ try: return f.derivative(*args, **kwds) diff --git a/src/sage/calculus/integration.pyx b/src/sage/calculus/integration.pyx index d0a5983eb60..1571cf873d7 100644 --- a/src/sage/calculus/integration.pyx +++ b/src/sage/calculus/integration.pyx @@ -70,7 +70,7 @@ def numerical_integral(func, a, b=None, algorithm='qag', max_points=87, params=[], eps_abs=1e-6, eps_rel=1e-6, rule=6): - r""" + r""" Return the numerical integral of the function on the interval from a to b and an error bound. @@ -241,40 +241,52 @@ def numerical_integral(func, a, b=None, Traceback (most recent call last): ... TypeError: unable to simplify to float approximation - """ - cdef double abs_err # step size - cdef double result - cdef int i - cdef int j - cdef double _a, _b - cdef PyFunctionWrapper wrapper # struct to pass information into GSL C function + Check for :trac:`15496`:: - if b is None or isinstance(a, (list, tuple)): - b = a[1] - a = a[0] + sage: f = x^2/exp(-1/(x^2+1))/(x^2+1) + sage: D = integrate(f,(x,-infinity,infinity),hold=True) + sage: D.n() + Traceback (most recent call last): + ... + ValueError: integral does not converge at -infinity + """ + cdef double abs_err # step size + cdef double result + cdef int i + cdef int j + cdef double _a, _b + cdef PyFunctionWrapper wrapper # struct to pass information into GSL C function + + if b is None or isinstance(a, (list, tuple)): + b = a[1] + a = a[0] - # The integral over a point is always zero - if a == b: - return (0.0, 0.0) + # The integral over a point is always zero + if a == b: + return (0.0, 0.0) - if not callable(func): + if not callable(func): # handle the constant case return (((b - a) * func), 0.0) - cdef gsl_function F - cdef gsl_integration_workspace* W - W=NULL + cdef gsl_function F + cdef gsl_integration_workspace* W + W = NULL - if not isinstance(func, FastDoubleFunc): + if not isinstance(func, FastDoubleFunc): + from sage.rings.infinity import Infinity try: if hasattr(func, 'arguments'): vars = func.arguments() else: vars = func.variables() - if len(vars) == 0: - # handle the constant case - return (((b - a) * func), 0.0) + except (AttributeError): + pass + else: + if not vars: + # handle the constant case + return (((b - a) * func), 0.0) if len(vars) != 1: if len(params) + 1 != len(vars): raise ValueError(("The function to be integrated depends on " @@ -285,70 +297,85 @@ def numerical_integral(func, a, b=None, to_sub = dict(zip(vars[1:], params)) func = func.subs(to_sub) - func = func._fast_float_(str(vars[0])) - except (AttributeError): - pass - if isinstance(func, FastDoubleFunc): + # sanity checks for integration up to infinity + v = str(vars[0]) + if a is -Infinity: + try: + ell = func.limit(**{v: -Infinity}) + except (AttributeError, ValueError): + pass + else: + if ell.is_numeric() and not ell.is_zero(): + raise ValueError('integral does not converge at -infinity') + if b is Infinity: + try: + ell = func.limit(**{v: Infinity}) + except (AttributeError, ValueError): + pass + else: + if ell.is_numeric() and not ell.is_zero(): + raise ValueError('integral does not converge at infinity') + func = func._fast_float_(v) + + if isinstance(func, FastDoubleFunc): F.function = c_ff F.params = func - elif not isinstance(func, compiled_integrand): + elif not isinstance(func, compiled_integrand): wrapper = PyFunctionWrapper() if not func is None: wrapper.the_function = func else: raise ValueError("No integrand defined") try: - if params == [] and len(sage_getargspec(wrapper.the_function)[0]) == 1: - wrapper.the_parameters=[] - elif params == [] and len(sage_getargspec(wrapper.the_function)[0]) > 1: + if not params and len(sage_getargspec(wrapper.the_function)[0]) == 1: + wrapper.the_parameters = [] + elif not params and len(sage_getargspec(wrapper.the_function)[0]) > 1: raise ValueError("Integrand has parameters but no parameters specified") - elif params!=[]: + elif params: wrapper.the_parameters = params except TypeError: - wrapper.the_function = eval("lambda x: func(x)", {'func':func}) + wrapper.the_function = eval("lambda x: func(x)", {'func': func}) wrapper.the_parameters = [] F.function = c_f F.params = wrapper + cdef size_t n + n = max_points - cdef size_t n - n = max_points + gsl_set_error_handler_off() - gsl_set_error_handler_off() - - if algorithm == "qng": + if algorithm == "qng": _a=a _b=b sig_on() gsl_integration_qng(&F, _a, _b, eps_abs, eps_rel, &result, &abs_err, &n) sig_off() - elif algorithm == "qag": - from sage.rings.infinity import Infinity - if a is -Infinity and b is +Infinity: + elif algorithm == "qag": + if a is -Infinity and b is +Infinity: W = gsl_integration_workspace_alloc(n) sig_on() gsl_integration_qagi(&F, eps_abs, eps_rel, n, W, &result, &abs_err) sig_off() - elif a is -Infinity: + elif a is -Infinity: _b = b W = gsl_integration_workspace_alloc(n) sig_on() gsl_integration_qagil(&F, _b, eps_abs, eps_rel, n, W, &result, &abs_err) sig_off() - elif b is +Infinity: + elif b is +Infinity: _a = a W = gsl_integration_workspace_alloc(n) sig_on() gsl_integration_qagiu(&F, _a, eps_abs, eps_rel, n, W, &result, &abs_err) sig_off() - else: + else: _a = a _b = b W = gsl_integration_workspace_alloc(n) @@ -357,7 +384,7 @@ def numerical_integral(func, a, b=None, sig_off() - elif algorithm == "qags": + elif algorithm == "qags": W = gsl_integration_workspace_alloc(n) sig_on() @@ -366,13 +393,14 @@ def numerical_integral(func, a, b=None, gsl_integration_qags(&F, _a, _b, eps_abs, eps_rel, n, W, &result, &abs_err) sig_off() - else: + else: raise TypeError("invalid integration algorithm") - if W != NULL: + if W != NULL: gsl_integration_workspace_free(W) - return result, abs_err + return result, abs_err + cdef double c_monte_carlo_f(double *t, size_t dim, void *params): cdef double value @@ -393,6 +421,7 @@ cdef double c_monte_carlo_f(double *t, size_t dim, void *params): return value + cdef double c_monte_carlo_ff(double *x, size_t dim, void *params): cdef double result ( params).call_c(x, &result) @@ -598,11 +627,11 @@ def monte_carlo_integral(func, xl, xu, size_t calls, algorithm='plain', wrapper = PyFunctionWrapper() wrapper.the_function = func - if params == [] and len(sage_getargspec(wrapper.the_function)[0]) == dim: + if not params and len(sage_getargspec(wrapper.the_function)[0]) == dim: wrapper.the_parameters = [] - elif params == [] and len(sage_getargspec(wrapper.the_function)[0]) > dim: + elif not params and len(sage_getargspec(wrapper.the_function)[0]) > dim: raise ValueError("Integrand has parameters but no parameters specified") - elif params != []: + elif params: wrapper.the_parameters = params wrapper.lx = [None] * dim diff --git a/src/sage/calculus/test_sympy.py b/src/sage/calculus/test_sympy.py index 9fb9358cb2b..86f93667040 100644 --- a/src/sage/calculus/test_sympy.py +++ b/src/sage/calculus/test_sympy.py @@ -109,7 +109,7 @@ sage: e = (1/cos(x)^3)._sympy_(); e cos(x)**(-3) - sage: f = e.series(x, 0, 10); f + sage: f = e.series(x, 0, int(10)); f 1 + 3*x**2/2 + 11*x**4/8 + 241*x**6/240 + 8651*x**8/13440 + O(x**10) And the pretty-printer. Since unicode characters are not working on diff --git a/src/sage/categories/action.pyx b/src/sage/categories/action.pyx index fb39aaafa0a..7ec46f80a98 100644 --- a/src/sage/categories/action.pyx +++ b/src/sage/categories/action.pyx @@ -71,8 +71,8 @@ cdef inline category(x): try: return x.category() except AttributeError: - import sage.categories.all - return sage.categories.all.Objects() + from sage.categories.objects import Objects + return Objects() cdef class Action(Functor): diff --git a/src/sage/categories/additive_magmas.py b/src/sage/categories/additive_magmas.py index 03f795bb97d..35fcfeb9d21 100644 --- a/src/sage/categories/additive_magmas.py +++ b/src/sage/categories/additive_magmas.py @@ -8,8 +8,6 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** -import six - from sage.misc.lazy_import import LazyImport from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method @@ -518,7 +516,7 @@ def algebra_generators(self): EXAMPLES:: sage: S = CommutativeAdditiveSemigroups().example(); S - An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c', 'd') + An example of a commutative semigroup: the free commutative semigroup generated by ('a', 'b', 'c', 'd') sage: A = S.algebra(QQ) sage: A.algebra_generators() Finite family {0: B[a], 1: B[b], 2: B[c], 3: B[d]} @@ -543,7 +541,7 @@ def product_on_basis(self, g1, g2): EXAMPLES:: sage: S = CommutativeAdditiveSemigroups().example(); S - An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c', 'd') + An example of a commutative semigroup: the free commutative semigroup generated by ('a', 'b', 'c', 'd') sage: A = S.algebra(QQ) sage: a,b,c,d = A.algebra_generators() sage: a * d * b @@ -782,10 +780,6 @@ def __bool__(self): True """ - if six.PY2: - __nonzero__ = __bool__ - del __bool__ - def _test_nonzero_equal(self, **options): r""" Test that ``.__bool__()`` behave consistently diff --git a/src/sage/categories/additive_semigroups.py b/src/sage/categories/additive_semigroups.py index c8df3c88682..fde92f27896 100644 --- a/src/sage/categories/additive_semigroups.py +++ b/src/sage/categories/additive_semigroups.py @@ -150,7 +150,7 @@ def algebra_generators(self): EXAMPLES:: sage: S = CommutativeAdditiveSemigroups().example(); S - An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c', 'd') + An example of a commutative semigroup: the free commutative semigroup generated by ('a', 'b', 'c', 'd') sage: A = S.algebra(QQ) sage: A.algebra_generators() Finite family {0: B[a], 1: B[b], 2: B[c], 3: B[d]} @@ -169,7 +169,7 @@ def product_on_basis(self, g1, g2): EXAMPLES:: sage: S = CommutativeAdditiveSemigroups().example(); S - An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c', 'd') + An example of a commutative semigroup: the free commutative semigroup generated by ('a', 'b', 'c', 'd') sage: A = S.algebra(QQ) sage: a,b,c,d = A.algebra_generators() sage: b * d * c diff --git a/src/sage/categories/affine_weyl_groups.py b/src/sage/categories/affine_weyl_groups.py index 5a550c8fa12..37dc4a2c9dc 100644 --- a/src/sage/categories/affine_weyl_groups.py +++ b/src/sage/categories/affine_weyl_groups.py @@ -104,7 +104,7 @@ def affine_grassmannian_elements_of_given_length(self, k): :meth:`AffineWeylGroups.ElementMethods.is_affine_grassmannian` """ - from sage.combinat.backtrack import SearchForest + from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest def select_length(pair): u, length = pair @@ -119,9 +119,9 @@ def succ(pair): u1.is_affine_grassmannian()): yield (u1, length + 1) return - return SearchForest(((self.one(), 0),), succ, algorithm='breadth', - category=FiniteEnumeratedSets(), - post_process=select_length) + return RecursivelyEnumeratedSet_forest(((self.one(), 0),), succ, algorithm='breadth', + category=FiniteEnumeratedSets(), + post_process=select_length) class ElementMethods: def is_affine_grassmannian(self): diff --git a/src/sage/categories/algebras_with_basis.py b/src/sage/categories/algebras_with_basis.py index 7b0a6524467..5606ab39da0 100644 --- a/src/sage/categories/algebras_with_basis.py +++ b/src/sage/categories/algebras_with_basis.py @@ -18,8 +18,6 @@ from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring from .unital_algebras import UnitalAlgebras -import six - class AlgebrasWithBasis(CategoryWithAxiom_over_base_ring): """ @@ -155,8 +153,8 @@ def _product_from_combinatorial_algebra_multiply(self,left,right): #Do the case where the user specifies how to multiply basis elements if hasattr(self, '_multiply_basis'): - 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(): res = self._multiply_basis(left_m, right_m) #Handle the case where the user returns a dictionary #where the keys are the monomials and the values are @@ -189,7 +187,7 @@ def _product_from_combinatorial_algebra_multiply(self,left,right): BR = self.base_ring() zero = BR(0) del_list = [] - for m, c in six.iteritems(z_elt): + for m, c in z_elt.items(): if c == zero: del_list.append(m) for m in del_list: diff --git a/src/sage/categories/all.py b/src/sage/categories/all.py index a60c45eba95..62ff402d522 100644 --- a/src/sage/categories/all.py +++ b/src/sage/categories/all.py @@ -3,16 +3,15 @@ from .category import Category -from .category_types import( - Elements, - ChainComplexes, -) +from .category_types import Elements -from sage.categories.simplicial_complexes import SimplicialComplexes +from .chain_complexes import ChainComplexes -from sage.categories.tensor import tensor -from sage.categories.signed_tensor import tensor_signed -from sage.categories.cartesian_product import cartesian_product +from .simplicial_complexes import SimplicialComplexes + +from .tensor import tensor +from .signed_tensor import tensor_signed +from .cartesian_product import cartesian_product from .functor import (ForgetfulFunctor, IdentityFunctor) diff --git a/src/sage/categories/bialgebras_with_basis.py b/src/sage/categories/bialgebras_with_basis.py index f7ced53db0a..831bab3f068 100644 --- a/src/sage/categories/bialgebras_with_basis.py +++ b/src/sage/categories/bialgebras_with_basis.py @@ -82,7 +82,7 @@ def convolution_product(self, *maps): EXAMPLES: We construct some maps: the identity, the antipode and - projection onto the homogeneous componente of degree 2:: + projection onto the homogeneous component of degree 2:: sage: Id = lambda x: x sage: Antipode = lambda x: x.antipode() diff --git a/src/sage/categories/bimodules.py b/src/sage/categories/bimodules.py index 5d78363039e..c05db937d72 100644 --- a/src/sage/categories/bimodules.py +++ b/src/sage/categories/bimodules.py @@ -107,7 +107,8 @@ def an_instance(cls): sage: Bimodules.an_instance() Category of bimodules over Rational Field on the left and Real Field with 53 bits of precision on the right """ - from sage.rings.all import QQ, RR + from sage.rings.rational_field import QQ + from sage.rings.real_mpfr import RR return cls(QQ, RR) def _repr_object_names(self): diff --git a/src/sage/categories/cartesian_product.py b/src/sage/categories/cartesian_product.py index 571f3624159..bb351b34588 100644 --- a/src/sage/categories/cartesian_product.py +++ b/src/sage/categories/cartesian_product.py @@ -11,7 +11,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from sage.misc.lazy_import import lazy_import from sage.categories.covariant_functorial_construction import CovariantFunctorialConstruction, CovariantConstructionCategory @@ -41,7 +40,7 @@ class CartesianProductFunctor(CovariantFunctorialConstruction, MultivariateConst [['a', 1], ['a', 2], ['b', 1], ['b', 2], ['c', 1], ['c', 2]] If those sets are endowed with more structure, say they are - monoids (hence in the category `Monoids()`), then the result is + monoids (hence in the category ``Monoids()``), then the result is automatically endowed with its natural monoid structure:: sage: M = Monoids().example() @@ -165,8 +164,7 @@ def __call__(self, args, **kwds): Check that Python3 ``range`` is handled correctly:: - sage: from six.moves import range as py3range - sage: C = cartesian_product([py3range(2), py3range(2)]) + sage: C = cartesian_product([range(2), range(2)]) sage: list(C) [(0, 0), (0, 1), (1, 0), (1, 1)] sage: C.category() diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index ea50f8d9d87..4e8f58c84f9 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -449,18 +449,16 @@ class of ``C`` is a dynamic subclass ``Cs_with_category`` of def __init__(self, s=None): """ - Initializes this category. + Initialize this category. EXAMPLES:: sage: class SemiprimitiveRings(Category): ....: def super_categories(self): ....: return [Rings()] - ....: ....: class ParentMethods: ....: def jacobson_radical(self): ....: return self.ideal(0) - ....: sage: C = SemiprimitiveRings() sage: C Category of semiprimitive rings @@ -753,7 +751,7 @@ def __classcontains__(cls, x): def is_abelian(self): """ - Returns whether this category is abelian. + Return whether this category is abelian. An abelian category is a category satisfying: @@ -785,6 +783,7 @@ def is_abelian(self): True sage: Semigroups().is_abelian() Traceback (most recent call last): + ... NotImplementedError: is_abelian """ raise NotImplementedError("is_abelian") diff --git a/src/sage/categories/category_types.py b/src/sage/categories/category_types.py index ed1c7d5cce8..0ec5cce262a 100644 --- a/src/sage/categories/category_types.py +++ b/src/sage/categories/category_types.py @@ -21,6 +21,8 @@ lazy_import('sage.categories.objects', 'Objects') lazy_import('sage.misc.latex', 'latex') +lazy_import('sage.categories.chain_complexes', 'ChainComplexes', + deprecation=29917) #################################################################### # Different types of categories @@ -614,38 +616,3 @@ def __call__(self, v): if v in self: return v return self.ring().ideal(v) - -# TODO: make this into a better category -############################################################# -# ChainComplex -############################################################# -class ChainComplexes(Category_module): - """ - The category of all chain complexes over a base ring. - - EXAMPLES:: - - sage: ChainComplexes(RationalField()) - Category of chain complexes over Rational Field - - sage: ChainComplexes(Integers(9)) - Category of chain complexes over Ring of integers modulo 9 - - TESTS:: - - sage: TestSuite(ChainComplexes(RationalField())).run() - """ - - def super_categories(self): - """ - EXAMPLES:: - - sage: ChainComplexes(Integers(9)).super_categories() - [Category of modules over Ring of integers modulo 9] - """ - from sage.categories.all import Fields, Modules, VectorSpaces - base_ring = self.base_ring() - if base_ring in Fields(): - return [VectorSpaces(base_ring)] - return [Modules(base_ring)] - diff --git a/src/sage/categories/category_with_axiom.py b/src/sage/categories/category_with_axiom.py index 524fb42c386..03bb998a7aa 100644 --- a/src/sage/categories/category_with_axiom.py +++ b/src/sage/categories/category_with_axiom.py @@ -1660,7 +1660,7 @@ class ``Sets.Finite``), or in a separate file (typically in a class from sage.misc.cachefunc import cached_method, cached_function from sage.misc.lazy_attribute import lazy_class_attribute from sage.misc.lazy_import import LazyImport -from sage.misc.misc import call_method +from sage.misc.call import call_method from sage.categories.category import Category from sage.categories.category_singleton import Category_singleton from sage.categories.category_types import Category_over_base_ring diff --git a/src/sage/categories/chain_complexes.py b/src/sage/categories/chain_complexes.py new file mode 100644 index 00000000000..72a4763b3b8 --- /dev/null +++ b/src/sage/categories/chain_complexes.py @@ -0,0 +1,49 @@ +""" +Category of chain complexes +""" + +#***************************************************************************** +# Copyright (C) 2007 Robert Bradshaw +# 2009 Mike Hansen +# 2013 Volker Braun +# 2013, 2015 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.categories.category_types import Category_module + +# TODO: make this into a better category +############################################################# +# ChainComplex +############################################################# +class ChainComplexes(Category_module): + """ + The category of all chain complexes over a base ring. + + EXAMPLES:: + + sage: ChainComplexes(RationalField()) + Category of chain complexes over Rational Field + + sage: ChainComplexes(Integers(9)) + Category of chain complexes over Ring of integers modulo 9 + + TESTS:: + + sage: TestSuite(ChainComplexes(RationalField())).run() + """ + + def super_categories(self): + """ + EXAMPLES:: + + sage: ChainComplexes(Integers(9)).super_categories() + [Category of modules over Ring of integers modulo 9] + """ + from sage.categories.all import Fields, Modules, VectorSpaces + base_ring = self.base_ring() + if base_ring in Fields(): + return [VectorSpaces(base_ring)] + return [Modules(base_ring)] diff --git a/src/sage/categories/coalgebras_with_basis.py b/src/sage/categories/coalgebras_with_basis.py index 8ac39c20d38..24505dcf067 100644 --- a/src/sage/categories/coalgebras_with_basis.py +++ b/src/sage/categories/coalgebras_with_basis.py @@ -197,7 +197,7 @@ def coproduct_iterated(self, n=1): if n == 1: return self.coproduct() from sage.functions.all import floor, ceil - from sage.rings.all import Integer + from sage.rings.integer import Integer # Use coassociativity of `\Delta` to perform many coproducts simultaneously. fn = floor(Integer(n-1)/2); cn = ceil(Integer(n-1)/2) diff --git a/src/sage/categories/commutative_additive_semigroups.py b/src/sage/categories/commutative_additive_semigroups.py index 7bb99dcc687..93c0f2eab5c 100644 --- a/src/sage/categories/commutative_additive_semigroups.py +++ b/src/sage/categories/commutative_additive_semigroups.py @@ -21,7 +21,7 @@ class CommutativeAdditiveSemigroups(CategoryWithAxiom): sage: C = CommutativeAdditiveSemigroups(); C Category of commutative additive semigroups sage: C.example() - An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c', 'd') + An example of a commutative semigroup: the free commutative semigroup generated by ('a', 'b', 'c', 'd') sage: sorted(C.super_categories(), key=str) [Category of additive commutative additive magmas, diff --git a/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py b/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py index edb8a5ffbc6..720d0097632 100644 --- a/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py +++ b/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py @@ -339,7 +339,7 @@ def simple_reflection_orders(self): """ one = self.one() s = self.simple_reflections() - from sage.rings.all import ZZ + from sage.rings.integer_ring import ZZ def mult_order(x): ct = ZZ.one() diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index 5258fd4fdd0..9dbbc7b9467 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -10,13 +10,12 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** # With contributions from Dan Bump, Steve Pon, Qiang Wang, Anne Schilling, Christian Stump, Mark Shimozono -from six.moves import range from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method, cached_in_parent_method from sage.misc.lazy_import import LazyImport from sage.misc.constant_function import ConstantFunction -from sage.misc.misc import attrcall +from sage.misc.call import attrcall from sage.categories.category_singleton import Category_singleton from sage.categories.enumerated_sets import EnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -25,8 +24,6 @@ from sage.misc.flatten import flatten from copy import copy -from sage.rings.integer_ring import ZZ - class CoxeterGroups(Category_singleton): r""" @@ -320,6 +317,7 @@ def braid_orbit(self, word): braid_rels = self.braid_relations() I = self.index_set() + from sage.rings.integer_ring import ZZ be_careful = any(i not in ZZ for i in I) if be_careful: @@ -456,7 +454,7 @@ def weak_order_ideal(self, predicate, side="right", category=None): .. rubric:: Background - The weak order is returned as a :class:`SearchForest`. + The weak order is returned as a :class:`RecursivelyEnumeratedSet_forest`. This is achieved by assigning to each element `u1` of the ideal a single ancestor `u=u1 s_i`, where `i` is the smallest descent of `u`. @@ -475,7 +473,7 @@ def weak_order_ideal(self, predicate, side="right", category=None): sage: [x.length() for x in W] [0, 1, 1, 2, 2, 3] """ - from sage.combinat.backtrack import SearchForest + from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest def succ(u): for i in u.descents(positive=True, side=side): @@ -485,7 +483,7 @@ def succ(u): return from sage.categories.finite_coxeter_groups import FiniteCoxeterGroups default_category = FiniteEnumeratedSets() if self in FiniteCoxeterGroups() else EnumeratedSets() - return SearchForest((self.one(),), succ, algorithm='breadth', + return RecursivelyEnumeratedSet_forest((self.one(),), succ, algorithm='breadth', category=default_category.or_subcategory(category)) @cached_method @@ -720,6 +718,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.integer_ring 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``. @@ -779,7 +799,7 @@ def bruhat_interval(self, x, y): if not x.bruhat_le(y): return ret ret.append([y]) - while ret[-1] != []: + while ret[-1]: nextlayer = [] for z in ret[-1]: for t in z.bruhat_lower_covers(): @@ -1492,21 +1512,21 @@ def reduced_word_graph(self): y = tuple(y) # Check that the reduced expressions differ by only # a single braid move - i = 0 - while i < len(x) and x[i] == y[i]: - i += 1 - if i == len(x): + j = 0 + while j < len(x) and x[j] == y[j]: + j += 1 + if j == len(x): continue - a, b = x[i], y[i] + a, b = x[j], y[j] m = P.coxeter_matrix()[a, b] subword = [a, b] * (m // 2) subword2 = [b, a] * (m // 2) if m % 2: subword.append(a) subword2.append(b) - if (x[i:i+m] != tuple(subword) - or y[i:i+m] != tuple(subword2) - or x[i+m:] != y[i+m:]): + if (x[j:j+m] != tuple(subword) + or y[j:j+m] != tuple(subword2) + or x[j+m:] != y[j+m:]): continue edges.append([x, y, m]) G = Graph(edges, immutable=True, format="list_of_edges") @@ -1823,7 +1843,7 @@ def binary_factorizations(self, predicate=ConstantFunction(True)): sage: w0.binary_factorizations().category() Category of finite enumerated sets """ - from sage.combinat.backtrack import SearchForest + from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest W = self.parent() if not predicate(W.one()): from sage.sets.finite_enumerated_set import FiniteEnumeratedSet @@ -1836,8 +1856,8 @@ def succ(u_v): u1 = u * s[i] if i == u1.first_descent() and predicate(u1): yield (u1, s[i] * v) - return SearchForest(((W.one(), self),), succ, - category=FiniteEnumeratedSets()) + return RecursivelyEnumeratedSet_forest(((W.one(), self),), succ, + category=FiniteEnumeratedSets()) @cached_in_parent_method def bruhat_lower_covers(self): diff --git a/src/sage/categories/crystals.py b/src/sage/categories/crystals.py index cc2b5042969..4b12ce0d8f7 100644 --- a/src/sage/categories/crystals.py +++ b/src/sage/categories/crystals.py @@ -20,7 +20,6 @@ #***************************************************************************** from __future__ import print_function -from builtins import zip from sage.misc.cachefunc import cached_method from sage.misc.abstract_method import abstract_method @@ -30,12 +29,6 @@ from sage.categories.tensor import TensorProductsCategory from sage.categories.morphism import Morphism from sage.categories.homset import Hom, Homset -from sage.misc.latex import latex -from sage.combinat import ranker -from sage.graphs.dot2tex_utils import have_dot2tex -from sage.rings.integer import Integer -from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet -from sage.sets.family import Family class Crystals(Category_singleton): r""" @@ -145,6 +138,7 @@ def example(self, choice="highwt", **kwds): if choice == "naive": return examples.NaiveCrystal(**kwds) else: + from sage.rings.integer import Integer if isinstance(choice, Integer): return examples.HighestWeightCrystalOfTypeA(n=choice, **kwds) else: @@ -389,6 +383,7 @@ def __iter__(self, index_set=None, max_depth=float('inf')): if index_set is None: index_set = self.index_set() succ = lambda x: [x.f(i) for i in index_set] + [x.e(i) for i in index_set] + from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet R = RecursivelyEnumeratedSet(self.module_generators, succ, structure=None) return R.breadth_first_search_iterator(max_depth) @@ -541,6 +536,7 @@ def subcrystal(self, index_set=None, generators=None, max_depth=float("inf"), else: raise ValueError("direction must be either 'both', 'upper', or 'lower'") + from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet subset = RecursivelyEnumeratedSet(generators, succ, structure=None, enumeration='breadth', max_depth=max_depth) @@ -913,6 +909,7 @@ def digraph(self, subset=None, index_set=None): continue d[x][child]=i G = DiGraph(d) + from sage.graphs.dot2tex_utils import have_dot2tex if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels=True, @@ -1128,12 +1125,15 @@ def dot_tex(self): 'digraph G { \n node [ shape=plaintext ];\n N_0 [ label = " ", texlbl = "$1$" ];\n N_1 [ label = " ", texlbl = "$2$" ];\n N_2 [ label = " ", texlbl = "$3$" ];\n N_0 -> N_1 [ label = " ", texlbl = "1" ];\n N_1 -> N_2 [ label = " ", texlbl = "2" ];\n}' """ import re + from sage.combinat import ranker + rank = ranker.from_list(self.list())[0] vertex_key = lambda x: "N_"+str(rank(x)) # To do: check the regular expression # Removing %-style comments, newlines, quotes # This should probably be moved to sage.misc.latex + from sage.misc.latex import latex quoted_latex = lambda x: re.sub("\"|\r|(%[^\n]*)?\n","", latex(x)) result = "digraph G { \n node [ shape=plaintext ];\n" @@ -1849,6 +1849,7 @@ def __init__(self, parent, cartan_type=None, virtualization = dict(virtualization) except (TypeError, ValueError): virtualization = {i: (virtualization(i),) for i in index_set} + from sage.sets.family import Family self._virtualization = Family(virtualization) self._scaling_factors = Family(scaling_factors) diff --git a/src/sage/categories/enumerated_sets.py b/src/sage/categories/enumerated_sets.py index 041066532ba..20de7fcb01a 100644 --- a/src/sage/categories/enumerated_sets.py +++ b/src/sage/categories/enumerated_sets.py @@ -7,7 +7,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # ***************************************************************************** -from six.moves import range from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import LazyImport @@ -142,7 +141,6 @@ def _call_(self, X): Also Python3 range are now accepted:: - sage: from six.moves import range sage: S = EnumeratedSets()(range(4)); S {0, 1, 2, 3} """ @@ -188,7 +186,6 @@ def __iter__(self): sage: class broken(UniqueRepresentation, Parent): ....: def __init__(self): ....: Parent.__init__(self, category = EnumeratedSets()) - ....: sage: it = iter(broken()); [next(it), next(it), next(it)] Traceback (most recent call last): ... @@ -203,7 +200,6 @@ def __iter__(self): ....: return 0 ....: def next(self, elt): ....: return elt+1 - ....: sage: it = iter(set_first_next()); [next(it), next(it), next(it)] [0, 1, 2] @@ -214,7 +210,6 @@ def __iter__(self): ....: Parent.__init__(self, category = EnumeratedSets()) ....: def unrank(self, i): ....: return i + 5 - ....: sage: it = iter(set_unrank()); [next(it), next(it), next(it)] [5, 6, 7] @@ -225,7 +220,6 @@ def __iter__(self): ....: Parent.__init__(self, category = EnumeratedSets()) ....: def list(self): ....: return [5, 6, 7] - ....: sage: it = iter(set_list()); [next(it), next(it), next(it)] [5, 6, 7] diff --git a/src/sage/categories/euclidean_domains.py b/src/sage/categories/euclidean_domains.py index bc3a5a1f321..fa4feb161ad 100644 --- a/src/sage/categories/euclidean_domains.py +++ b/src/sage/categories/euclidean_domains.py @@ -148,7 +148,7 @@ def _test_euclidean_degree(self, **options): min_degree = self.one().euclidean_degree() - from sage.rings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN for a in S: tester.assertIn(a.euclidean_degree(), NN) tester.assertGreaterEqual(a.euclidean_degree(), min_degree) diff --git a/src/sage/categories/examples/commutative_additive_semigroups.py b/src/sage/categories/examples/commutative_additive_semigroups.py index bb4fdc84717..9f2ed73394f 100644 --- a/src/sage/categories/examples/commutative_additive_semigroups.py +++ b/src/sage/categories/examples/commutative_additive_semigroups.py @@ -24,7 +24,7 @@ class FreeCommutativeAdditiveSemigroup(UniqueRepresentation, Parent): EXAMPLES:: sage: S = CommutativeAdditiveSemigroups().example(); S - An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c', 'd') + An example of a commutative semigroup: the free commutative semigroup generated by ('a', 'b', 'c', 'd') sage: S.category() Category of commutative additive semigroups @@ -75,7 +75,7 @@ def __init__(self, alphabet=('a','b','c','d')): EXAMPLES:: sage: M = CommutativeAdditiveSemigroups().example(alphabet=('a','b','c')); M - An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c') + An example of a commutative semigroup: the free commutative semigroup generated by ('a', 'b', 'c') TESTS:: @@ -91,10 +91,10 @@ def _repr_(self): sage: M = CommutativeAdditiveSemigroups().example(alphabet=('a','b','c')) sage: M._repr_() - "An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c')" + "An example of a commutative semigroup: the free commutative semigroup generated by ('a', 'b', 'c')" """ - return "An example of a commutative monoid: the free commutative monoid generated by %s"%(self.alphabet,) + return "An example of a commutative semigroup: the free commutative semigroup generated by %s"%(self.alphabet,) def summation(self, x, y): r""" @@ -154,7 +154,7 @@ def __init__(self, parent, iterable): sage: x.value {'a': 2, 'b': 0, 'c': 1, 'd': 5} sage: x.parent() - An example of a commutative monoid: the free commutative monoid generated by ('a', 'b', 'c', 'd') + An example of a commutative semigroup: the free commutative semigroup generated by ('a', 'b', 'c', 'd') Internally, elements are represented as dense dictionaries which associate to each generator of the monoid its multiplicity. In diff --git a/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py index b815aeb9112..c5719350a6e 100644 --- a/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py @@ -7,7 +7,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** -from six import iteritems from sage.misc.cachefunc import cached_method from sage.sets.family import Family @@ -282,7 +281,7 @@ def module(self): """ return self._M - def from_vector(self, v): + def from_vector(self, v, order=None): """ Return the element of ``self`` corresponding to the vector ``v`` in ``self.module()``. @@ -318,7 +317,7 @@ def __iter__(self): [(0, 2), (2, -1)] """ zero = self.parent().base_ring().zero() - for i, c in iteritems(self.value): + for i, c in self.value.items(): if c != zero: yield (i, c) @@ -368,9 +367,9 @@ def lift(self): """ UEA = self.parent().universal_enveloping_algebra() gens = UEA.gens() - return UEA.sum(c * gens[i] for i, c in iteritems(self.value)) + return UEA.sum(c * gens[i] for i, c in self.value.items()) - def to_vector(self): + def to_vector(self, order=None): """ Return ``self`` as a vector in ``self.parent().module()``. diff --git a/src/sage/categories/examples/finite_weyl_groups.py b/src/sage/categories/examples/finite_weyl_groups.py index 721c30f1f7a..d3d50c9af89 100644 --- a/src/sage/categories/examples/finite_weyl_groups.py +++ b/src/sage/categories/examples/finite_weyl_groups.py @@ -7,7 +7,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #****************************************************************************** -from six.moves import range from sage.misc.cachefunc import cached_method from sage.structure.parent import Parent diff --git a/src/sage/categories/examples/hopf_algebras_with_basis.py b/src/sage/categories/examples/hopf_algebras_with_basis.py index e506b8b46a5..fc3ef19d914 100644 --- a/src/sage/categories/examples/hopf_algebras_with_basis.py +++ b/src/sage/categories/examples/hopf_algebras_with_basis.py @@ -1,5 +1,5 @@ r""" -Examples of algebras with basis +Examples of Hopf algebras with basis """ #***************************************************************************** # Copyright (C) 2008-2009 Nicolas M. Thiery @@ -16,7 +16,7 @@ class MyGroupAlgebra(CombinatorialFreeModule): r""" - An of a Hopf algebra with basis: the group algebra of a group + An example of a Hopf algebra with basis: the group algebra of a group This class illustrates a minimal implementation of a Hopf algebra with basis. """ diff --git a/src/sage/categories/examples/infinite_enumerated_sets.py b/src/sage/categories/examples/infinite_enumerated_sets.py index cbb713e9fa9..e91aacbf739 100644 --- a/src/sage/categories/examples/infinite_enumerated_sets.py +++ b/src/sage/categories/examples/infinite_enumerated_sets.py @@ -135,12 +135,12 @@ def __call__(self, elt): Integer Ring sage: NN(-1) Traceback (most recent call last): + ... ValueError: Value -1 is not a non negative integer. """ if elt in self: return self._element_constructor_(elt) - else: - raise ValueError("Value %s is not a non negative integer."%(elt)) + raise ValueError("Value %s is not a non negative integer." % (elt)) def an_element(self): """ diff --git a/src/sage/categories/examples/lie_algebras.py b/src/sage/categories/examples/lie_algebras.py index 695bd7c1b1e..3bcdd817da0 100644 --- a/src/sage/categories/examples/lie_algebras.py +++ b/src/sage/categories/examples/lie_algebras.py @@ -261,7 +261,7 @@ def _acted_upon_(self, scalar, self_on_left=False): return self.__class__(self.parent(), self.value * scalar) return self.__class__(self.parent(), scalar * self.value) - def __div__(self, x, self_on_left=False): + def __truediv__(self, x, self_on_left=False): """ Division by coefficients. 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/categories/fields.py b/src/sage/categories/fields.py index 686c5eb369a..d88743cc17f 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -20,7 +20,6 @@ from sage.categories.euclidean_domains import EuclideanDomains from sage.categories.division_rings import DivisionRings -import sage.rings.ring from sage.structure.element import coerce_binop class Fields(CategoryWithAxiom): @@ -119,6 +118,7 @@ def __contains__(self, x): 0 """ + import sage.rings.ring try: return self._contains_helper(x) or sage.rings.ring._is_Field(x) except Exception: @@ -559,7 +559,7 @@ def euclidean_degree(self): """ if self.is_zero(): raise ValueError("euclidean degree not defined for the zero element") - from sage.rings.all import ZZ + from sage.rings.integer_ring import ZZ return ZZ.zero() def quo_rem(self, other): diff --git a/src/sage/categories/filtered_hopf_algebras_with_basis.py b/src/sage/categories/filtered_hopf_algebras_with_basis.py index 5e6355b01d4..dcfb8ba05f8 100644 --- a/src/sage/categories/filtered_hopf_algebras_with_basis.py +++ b/src/sage/categories/filtered_hopf_algebras_with_basis.py @@ -14,8 +14,6 @@ from sage.categories.with_realizations import WithRealizationsCategory from sage.misc.cachefunc import cached_method -import six - class FilteredHopfAlgebrasWithBasis(FilteredModulesCategory): """ @@ -132,7 +130,7 @@ def antipode(self, elem): """ return self.linear_combination( (self.antipode_on_basis(mon), coeff) - for mon, coeff in six.iteritems(elem.monomial_coefficients(copy=False)) + for mon, coeff in elem.monomial_coefficients(copy=False).items() ) class ElementMethods: diff --git a/src/sage/categories/finite_complex_reflection_groups.py b/src/sage/categories/finite_complex_reflection_groups.py index 60fbb8d97be..817b762c2ac 100644 --- a/src/sage/categories/finite_complex_reflection_groups.py +++ b/src/sage/categories/finite_complex_reflection_groups.py @@ -324,7 +324,7 @@ def number_of_reflection_hyperplanes(self): sage: W.number_of_reflection_hyperplanes() # optional - gap3 15 """ - from sage.rings.all import ZZ + from sage.rings.integer_ring import ZZ return ZZ.sum(codeg + 1 for codeg in self.codegrees()) @cached_method @@ -358,7 +358,7 @@ def number_of_reflections(self): sage: W.number_of_reflections() # optional - gap3 15 """ - from sage.rings.all import ZZ + from sage.rings.integer_ring import ZZ return ZZ.sum(deg - 1 for deg in self.degrees()) @cached_method @@ -413,7 +413,7 @@ def cardinality(self): sage: W.cardinality() # optional - gap3 192 """ - from sage.rings.all import ZZ + from sage.rings.integer_ring import ZZ return ZZ.prod(self.degrees()) def is_well_generated(self): @@ -860,6 +860,8 @@ def noncrossing_partition_lattice(self, c=None, L=None, L = list(self.absolute_order_ideal(gens=c, in_unitary_group=in_unitary_group, return_lengths=True)) + else: + L = [(pi, pi.reflection_length()) for pi in L] rels = [] ref_lens = {pi:l for (pi, l) in L} for (pi, l) in L: @@ -980,8 +982,20 @@ def absolute_poset(self, in_unitary_group=False): Irreducible complex reflection group of rank 2 and type ST4 sage: W.absolute_poset() # optional - gap3 Finite poset containing 24 elements + + TESTS:: + + sage: W1 = CoxeterGroup(['A',2]) + sage: W2 = WeylGroup(['A',2]) + sage: W3 = SymmetricGroup(3) + sage: W1.absolute_poset() + Finite poset containing 6 elements + sage: W2.absolute_poset() + Finite poset containing 6 elements + sage: W3.absolute_poset() + Finite poset containing 6 elements """ - return self.noncrossing_partition_lattice(L=self, in_unitary_group=in_unitary_group) + return self.noncrossing_partition_lattice(L=tuple(self), in_unitary_group=in_unitary_group) class WellGenerated(CategoryWithAxiom): diff --git a/src/sage/categories/finite_coxeter_groups.py b/src/sage/categories/finite_coxeter_groups.py index 90e301bedef..8e7df277404 100644 --- a/src/sage/categories/finite_coxeter_groups.py +++ b/src/sage/categories/finite_coxeter_groups.py @@ -13,9 +13,6 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.categories.category_with_axiom import CategoryWithAxiom from sage.categories.coxeter_groups import CoxeterGroups -from sage.rings.all import AA, UniversalCyclotomicField, QQbar -from sage.rings.integer_ring import ZZ - class FiniteCoxeterGroups(CategoryWithAxiom): r""" @@ -742,9 +739,12 @@ def permutahedron(self, point=None, base_ring=None): n = self.one().canonical_matrix().rank() weights = self.fundamental_weights() if point is None: + from sage.rings.integer_ring import ZZ point = [ZZ.one()] * n v = sum(point[i-1] * weights[i] for i in weights.keys()) from sage.geometry.polyhedron.constructor import Polyhedron + from sage.rings.qqbar import AA, QQbar + from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField vertices = [v*w for w in self] if base_ring is None and v.base_ring() in [UniversalCyclotomicField(), QQbar]: vertices = [v.change_ring(AA) for v in vertices] diff --git a/src/sage/categories/finite_dimensional_algebras_with_basis.py b/src/sage/categories/finite_dimensional_algebras_with_basis.py index 7b14ee25c1f..1d5ef56fce3 100644 --- a/src/sage/categories/finite_dimensional_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_algebras_with_basis.py @@ -32,7 +32,6 @@ from sage.categories.algebras import Algebras from sage.categories.associative_algebras import AssociativeAlgebras from sage.categories.tensor import TensorProductsCategory -from sage.matrix.constructor import Matrix class FiniteDimensionalAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): r""" @@ -701,6 +700,7 @@ def cartan_invariants_matrix(self): [0 0 0 1 0 1 1 0] [0 0 0 0 0 0 0 1] """ + from sage.matrix.constructor import Matrix from sage.rings.integer_ring import ZZ A_quo = self.semisimple_quotient() idempotents_quo = A_quo.central_orthogonal_idempotents() @@ -963,7 +963,7 @@ def is_identity_decomposition_into_orthogonal_idempotents(self, l): Here are some more counterexamples: 1. Some orthogonal elements summing to `1` but not being - idempotent:: + idempotent:: sage: class PQAlgebra(CombinatorialFreeModule): ....: def __init__(self, F, p): @@ -987,7 +987,7 @@ def is_identity_decomposition_into_orthogonal_idempotents(self, l): sage: A.is_identity_decomposition_into_orthogonal_idempotents((a, b)) False - For comparison:: + For comparison:: sage: A = PQAlgebra(QQ, x**2 - x); y = A.x() sage: a, b = y, 1-y diff --git a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py index 31d7479e511..d4edc0bd3c0 100644 --- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -221,7 +221,7 @@ def _dense_free_module(self, R=None): module = _dense_free_module - def from_vector(self, v): + def from_vector(self, v, order=None): """ Return the element of ``self`` corresponding to the vector ``v`` in ``self.module()``. @@ -239,9 +239,10 @@ def from_vector(self, v): sage: parent(u) is L True """ + if order is None: + order = self._basis_ordering B = self.basis() - return self.sum(v[i] * B[k] for i,k in enumerate(self._basis_ordering) - if v[i] != 0) + return self.sum(v[i] * B[k] for i,k in enumerate(order) if v[i] != 0) def killing_matrix(self, x, y): r""" @@ -1455,7 +1456,7 @@ def adjoint_matrix(self): # In #11111 (more or less) by using matrix of a morphi return matrix(self.base_ring(), [P.bracket(self, b).to_vector() for b in basis]) - def to_vector(self): + def to_vector(self, order=None): """ Return the vector in ``g.module()`` corresponding to the element ``self`` of ``g`` (where ``g`` is the parent of @@ -1495,8 +1496,9 @@ def to_vector(self): mc = self.monomial_coefficients(copy=False) M = self.parent().module() B = M.basis() - return M.sum(mc[k] * B[i] for i,k in enumerate(self.parent()._basis_ordering) - if k in mc) + if order is None: + order = self.parent()._basis_ordering + return M.sum(mc[k] * B[i] for i,k in enumerate(order) if k in mc) _vector_ = to_vector diff --git a/src/sage/categories/finite_dimensional_modules_with_basis.py b/src/sage/categories/finite_dimensional_modules_with_basis.py index afd0d4808a1..4696c2d424c 100644 --- a/src/sage/categories/finite_dimensional_modules_with_basis.py +++ b/src/sage/categories/finite_dimensional_modules_with_basis.py @@ -11,6 +11,7 @@ import operator from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.categories.fields import Fields from sage.misc.cachefunc import cached_method class FiniteDimensionalModulesWithBasis(CategoryWithAxiom_over_base_ring): @@ -233,61 +234,6 @@ def annihilator_basis(self, S, action=operator.mul, side='right'): [action(s, b)._vector_() for b in self.basis()])) return tuple(map(self.from_vector, mat.left_kernel().basis())) - def quotient_module(self, submodule, check=True, already_echelonized=False, category=None): - r""" - Construct the quotient module ``self``/``submodule``. - - INPUT: - - - ``submodule`` -- a submodule with basis of ``self``, or - something that can be turned into one via - ``self.submodule(submodule)``. - - - ``check``, ``already_echelonized`` -- passed down to - :meth:`ModulesWithBasis.ParentMethods.submodule`. - - .. WARNING:: - - At this point, this only supports quotients by free - submodules admitting a basis in unitriangular echelon - form. In this case, the quotient is also a free - module, with a basis consisting of the retract of a - subset of the basis of ``self``. - - EXAMPLES:: - - sage: X = CombinatorialFreeModule(QQ, range(3), prefix="x") - sage: x = X.basis() - sage: Y = X.quotient_module([x[0]-x[1], x[1]-x[2]], already_echelonized=True) - sage: Y.print_options(prefix='y'); Y - Free module generated by {2} over Rational Field - sage: y = Y.basis() - sage: y[2] - y[2] - sage: y[2].lift() - x[2] - sage: Y.retract(x[0]+2*x[1]) - 3*y[2] - - sage: R. = QQ[] - sage: C = CombinatorialFreeModule(R, range(3), prefix='x') - sage: x = C.basis() - sage: gens = [x[0] - x[1], 2*x[1] - 2*x[2], x[0] - x[2]] - sage: Y = X.quotient_module(gens) - - .. SEEALSO:: - - - :meth:`Modules.WithBasis.ParentMethods.submodule` - - :meth:`Rings.ParentMethods.quotient` - - :class:`sage.modules.with_basis.subquotient.QuotientModuleWithBasis` - """ - from sage.modules.with_basis.subquotient import SubmoduleWithBasis, QuotientModuleWithBasis - if not isinstance(submodule, SubmoduleWithBasis): - submodule = self.submodule(submodule, check=check, - unitriangular=True, - already_echelonized=already_echelonized) - return QuotientModuleWithBasis(submodule, category=category) - @cached_method def _dense_free_module(self, base_ring=None): """ @@ -339,6 +285,73 @@ def from_vector(self, vector, order=None): order = range(self.dimension()) return self._from_dict({order[i]: c for i,c in vector.iteritems()}) + def echelon_form(self, elements, row_reduced=False, order=None): + r""" + Return a basis in echelon form of the subspace spanned by + a finite set of elements. + + INPUT: + + - ``elements`` -- a list or finite iterable of elements of ``self`` + - ``row_reduced`` -- (default: ``False``) whether to compute the + basis for the row reduced echelon form + - ``order`` -- (optional) either something that can + be converted into a tuple or a key function + + OUTPUT: + + A list of elements of ``self`` whose expressions as vectors + form a matrix in echelon form. If ``base_ring`` is specified, + then the calculation is achieved in this base ring. + + EXAMPLES:: + + sage: X = CombinatorialFreeModule(QQ, range(3), prefix="x") + sage: x = X.basis() + sage: V = X.echelon_form([x[0]-x[1], x[0]-x[2],x[1]-x[2]]); V + [x[0] - x[2], x[1] - x[2]] + sage: matrix(list(map(vector, V))) + [ 1 0 -1] + [ 0 1 -1] + + :: + + sage: F = CombinatorialFreeModule(ZZ, [1,2,3,4]) + sage: B = F.basis() + sage: elements = [B[1]-17*B[2]+6*B[3], B[1]-17*B[2]+B[4]] + sage: F.echelon_form(elements) + [B[1] - 17*B[2] + B[4], 6*B[3] - B[4]] + + :: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: a,b,c = F.basis() + sage: F.echelon_form([8*a+b+10*c, -3*a+b-c, a-b-c]) + [B['a'] + B['c'], B['b'] + 2*B['c']] + + :: + + sage: R. = QQ[] + sage: C = CombinatorialFreeModule(R, range(3), prefix='x') + sage: x = C.basis() + sage: C.echelon_form([x[0] - x[1], 2*x[1] - 2*x[2], x[0] - x[2]]) + [x[0] - x[2], x[1] - x[2]] + """ + if order is not None: + order = self._compute_support_order(order) + from sage.matrix.constructor import matrix + mat = matrix(self.base_ring(), [g._vector_(order=order) for g in elements]) + # Echelonizing a matrix over a field returned the rref + if row_reduced and self.base_ring() not in Fields(): + try: + mat = mat.rref().change_ring(self.base_ring()) + except (ValueError, TypeError): + raise ValueError("unable to compute the row reduced echelon form") + else: + mat.echelonize() + ret = [self.from_vector(vec, order=order) for vec in mat if vec] + return ret + class ElementMethods: def dense_coefficient_list(self, order=None): """ diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py index b01ea7f4818..e262db24fbe 100644 --- a/src/sage/categories/finite_posets.py +++ b/src/sage/categories/finite_posets.py @@ -18,7 +18,6 @@ from sage.misc.abstract_method import abstract_method from sage.categories.category_with_axiom import CategoryWithAxiom -from sage.plot.plot import graphics_array class FinitePosets(CategoryWithAxiom): r""" @@ -1435,6 +1434,7 @@ def rowmotion_orbits_plots(self): Graphics Array of size 1 x 1 """ + from sage.plot.plot import graphics_array plot_of_orb_plots=[] max_orbit_size = 0 for orb in self.rowmotion_orbits(): @@ -1520,6 +1520,7 @@ def toggling_orbits_plots(self, vs): Graphics Array of size 1 x 1 """ + from sage.plot.plot import graphics_array plot_of_orb_plots=[] max_orbit_size = 0 for orb in self.toggling_orbits(vs): @@ -1889,7 +1890,7 @@ def order_ideals_lattice(self, as_ideals=True, facade=None): if facade is None: facade = self._is_facade if as_ideals: - from sage.misc.misc import attrcall + from sage.misc.call import attrcall from sage.sets.set import Set ideals = [Set(self.order_ideal(antichain)) for antichain in self.antichains()] diff --git a/src/sage/categories/finite_semigroups.py b/src/sage/categories/finite_semigroups.py index 1dc8a494619..76c082af096 100644 --- a/src/sage/categories/finite_semigroups.py +++ b/src/sage/categories/finite_semigroups.py @@ -11,7 +11,7 @@ # ***************************************************************************** from sage.misc.cachefunc import cached_method -from sage.misc.misc import attrcall +from sage.misc.call import attrcall from sage.categories.category_with_axiom import CategoryWithAxiom diff --git a/src/sage/categories/graded_modules_with_basis.py b/src/sage/categories/graded_modules_with_basis.py index 319fe479f6e..ec6f4e7590c 100644 --- a/src/sage/categories/graded_modules_with_basis.py +++ b/src/sage/categories/graded_modules_with_basis.py @@ -11,8 +11,6 @@ from sage.categories.graded_modules import GradedModulesCategory -import six - class GradedModulesWithBasis(GradedModulesCategory): """ @@ -67,7 +65,7 @@ def degree_negation(self, element): else base_minusone) return self.sum_of_terms([(key, diag(key) * value) for key, value in - six.iteritems(element.monomial_coefficients(copy=False))]) + element.monomial_coefficients(copy=False).items()]) class ElementMethods: def degree_negation(self): diff --git a/src/sage/categories/graphs.py b/src/sage/categories/graphs.py index 1be42bb94a5..168f5a2e37d 100644 --- a/src/sage/categories/graphs.py +++ b/src/sage/categories/graphs.py @@ -11,7 +11,9 @@ from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method from sage.categories.category_singleton import Category_singleton +from sage.categories.category_with_axiom import CategoryWithAxiom from sage.categories.simplicial_complexes import SimplicialComplexes +from sage.categories.sets_cat import Sets class Graphs(Category_singleton): r""" @@ -106,3 +108,30 @@ def faces(self): """ return set(self.edges()).union(self.vertices()) + class Connected(CategoryWithAxiom): + """ + The category of connected graphs. + + EXAMPLES:: + + sage: from sage.categories.graphs import Graphs + sage: C = Graphs().Connected() + sage: TestSuite(C).run() + """ + def extra_super_categories(self): + """ + Return the extra super categories of ``self``. + + A connected graph is also a metric space. + + EXAMPLES:: + + sage: from sage.categories.graphs import Graphs + sage: Graphs().Connected().super_categories() # indirect doctest + [Category of connected topological spaces, + Category of connected simplicial complexes, + Category of graphs, + Category of metric spaces] + """ + return [Sets().Metric()] + diff --git a/src/sage/categories/groups.py b/src/sage/categories/groups.py index 920ea94126a..712edff66bf 100644 --- a/src/sage/categories/groups.py +++ b/src/sage/categories/groups.py @@ -78,7 +78,7 @@ def free(index_set=None, names=None, **kwds): sage: F. = Groups().free(); F Free Group on generators {x, y, z} """ - from sage.rings.all import ZZ + from sage.rings.integer_ring import ZZ if index_set in ZZ or (index_set is None and names is not None): from sage.groups.free_group import FreeGroup if names is None: diff --git a/src/sage/categories/highest_weight_crystals.py b/src/sage/categories/highest_weight_crystals.py index 0c7b99e2e8e..dc4441abe46 100644 --- a/src/sage/categories/highest_weight_crystals.py +++ b/src/sage/categories/highest_weight_crystals.py @@ -13,7 +13,6 @@ from sage.categories.crystals import (Crystals, CrystalHomset, CrystalMorphismByGenerators) from sage.categories.tensor import TensorProductsCategory -from sage.graphs.dot2tex_utils import have_dot2tex class HighestWeightCrystals(Category_singleton): """ @@ -331,7 +330,7 @@ def q_dimension(self, q=None, prec=None, use_product=False): + 36*q^12 + 44*q^13 + 57*q^14 + 70*q^15 + O(x^16) """ - from sage.rings.all import ZZ + from sage.rings.integer_ring import ZZ WLR = self.weight_lattice_realization() I = self.index_set() mg = self.highest_weight_vectors() @@ -520,6 +519,7 @@ def digraph(self, subset=None, index_set=None, depth=None): visited = recently_visited G = DiGraph(d) + from sage.graphs.dot2tex_utils import have_dot2tex if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels=True, diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py index fe2b8e466a8..d3d2896a638 100644 --- a/src/sage/categories/homset.py +++ b/src/sage/categories/homset.py @@ -364,7 +364,8 @@ def Hom(X, Y, category=None, check=True): ....: raise TypeError sage: from sage.structure.element import Element sage: class Foo(Parent): - ....: _no_generic_basering_coercion = True + ....: def _coerce_map_from_base_ring(self): + ....: return self._generic_coerce_map(self.base_ring()) ....: class Element(Element): ....: pass sage: X = Foo(base=QQ, category=AlgebrasWithHom(QQ)) diff --git a/src/sage/categories/lie_algebras.py b/src/sage/categories/lie_algebras.py index 9d8f09d0613..b088a9cb39d 100644 --- a/src/sage/categories/lie_algebras.py +++ b/src/sage/categories/lie_algebras.py @@ -166,7 +166,7 @@ def example(self, gens=None): """ if gens is None: from sage.combinat.symmetric_group_algebra import SymmetricGroupAlgebra - from sage.rings.all import QQ + from sage.rings.rational_field import QQ gens = SymmetricGroupAlgebra(QQ, 3).algebra_generators() from sage.categories.examples.lie_algebras import Example return Example(gens) @@ -400,7 +400,7 @@ def module(self): """ @abstract_method(optional=True) - def from_vector(self, v): + def from_vector(self, v, order=None): """ Return the element of ``self`` corresponding to the vector ``v`` in ``self.module()``. @@ -842,7 +842,7 @@ def _bracket_(self, y): """ @abstract_method(optional=True) - def to_vector(self): + def to_vector(self, order=None): """ Return the vector in ``g.module()`` corresponding to the element ``self`` of ``g`` (where ``g`` is the parent of diff --git a/src/sage/categories/lie_algebras_with_basis.py b/src/sage/categories/lie_algebras_with_basis.py index f93830476ed..9cc23084381 100644 --- a/src/sage/categories/lie_algebras_with_basis.py +++ b/src/sage/categories/lie_algebras_with_basis.py @@ -107,7 +107,7 @@ def module(self): # Otherwise just index by the basis of ``self`` as a fallback return CombinatorialFreeModule(self.base_ring(), self.basis()) - def from_vector(self, v): + def from_vector(self, v, order=None): """ Return the element of ``self`` corresponding to the vector ``v`` in ``self.module()``. @@ -194,7 +194,7 @@ def term(ml, mr): return P.sum(cl * cr * term(ml, mr) for ml, cl in self for mr, cr in y) - def to_vector(self): + def to_vector(self, order=None): """ Return the vector in ``g.module()`` corresponding to the element ``self`` of ``g`` (where ``g`` is the parent of diff --git a/src/sage/categories/loop_crystals.py b/src/sage/categories/loop_crystals.py index 00ba408cc77..8d6aa2821c2 100644 --- a/src/sage/categories/loop_crystals.py +++ b/src/sage/categories/loop_crystals.py @@ -22,8 +22,6 @@ from sage.categories.map import Map from sage.graphs.dot2tex_utils import have_dot2tex from sage.functions.other import ceil -from sage.rings.all import ZZ - class LoopCrystals(Category_singleton): r""" @@ -534,6 +532,7 @@ def is_perfect(self, ell=None): Implement a version for tensor products of KR crystals. """ + from sage.rings.integer_ring import ZZ if ell is None: if (self.cartan_type().dual().type() == 'BC' and self.cartan_type().rank() - 1 == self.r()): @@ -978,6 +977,7 @@ def energy_function(self, algorithm=None): if algorithm == 'definition': # Setup + from sage.rings.integer_ring import ZZ energy = ZZ.zero() R_mats = [[K.R_matrix(Kp) for Kp in self.parent().crystals[i+1:]] for i,K in enumerate(self.parent().crystals)] @@ -1179,6 +1179,7 @@ def __init__(self, B, Bp, normalization=0): sage: [H(x) for x in hw] [0, 1, 2, 1] """ + from sage.rings.integer_ring import ZZ self._B = B self._Bp = Bp self._R_matrix = self._B.R_matrix(self._Bp) diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py index 8073599c0d3..89af2269084 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -1082,7 +1082,8 @@ def example(self): sage: C = Magmas().CartesianProducts().example(); C The Cartesian product of (Rational Field, Integer Ring, Integer Ring) sage: C.category() - Category of Cartesian products of commutative rings + Join of Category of Cartesian products of commutative rings and + Category of Cartesian products of metric spaces sage: sorted(C.category().axioms()) ['AdditiveAssociative', 'AdditiveCommutative', 'AdditiveInverse', 'AdditiveUnital', 'Associative', 'Commutative', diff --git a/src/sage/categories/magmatic_algebras.py b/src/sage/categories/magmatic_algebras.py index 09d8ce35bdf..5f6a0cd32a0 100644 --- a/src/sage/categories/magmatic_algebras.py +++ b/src/sage/categories/magmatic_algebras.py @@ -19,8 +19,6 @@ from sage.categories.additive_magmas import AdditiveMagmas from sage.categories.modules import Modules -import six - class MagmaticAlgebras(Category_over_base_ring): """ @@ -142,7 +140,7 @@ def algebra_generators(self): sage: P.algebra_generators() Lazy family (Term map from Partition diagrams of order 1 to Partition Algebra of rank 1 with parameter x over Univariate Polynomial Ring in x - over Integer Ring(i))_{i in Partition diagrams of order 1} + over Integer Ring(i))_{i in Partition diagrams of order 1} """ return self.basis() @@ -217,9 +215,9 @@ def _product_from_product_on_basis_multiply( self, left, right ): B[word: aba] - B[word: abb] + 2*B[word: ca] - 2*B[word: cb] """ - return self.linear_combination( ( self.product_on_basis( mon_left, mon_right ), coeff_left * coeff_right ) - for ( mon_left, coeff_left ) in six.iteritems(left.monomial_coefficients()) - for ( mon_right, coeff_right ) in six.iteritems(right.monomial_coefficients()) ) + return self.linear_combination((self.product_on_basis(mon_left, mon_right), coeff_left * coeff_right ) + for (mon_left, coeff_left) in left.monomial_coefficients().items() + for (mon_right, coeff_right) in right.monomial_coefficients().items() ) class FiniteDimensional(CategoryWithAxiom_over_base_ring): class ParentMethods: diff --git a/src/sage/categories/metric_spaces.py b/src/sage/categories/metric_spaces.py index 2edf200f96a..7fdc8780d0e 100644 --- a/src/sage/categories/metric_spaces.py +++ b/src/sage/categories/metric_spaces.py @@ -9,10 +9,13 @@ #****************************************************************************** from sage.misc.cachefunc import cached_method +from sage.misc.superseded import deprecated_function_alias from sage.categories.category import Category from sage.categories.category_with_axiom import CategoryWithAxiom +from sage.categories.cartesian_product import CartesianProductsCategory from sage.categories.covariant_functorial_construction import RegressiveCovariantConstructionCategory from sage.categories.with_realizations import WithRealizationsCategory +from sage.categories.homsets import HomsetsCategory class MetricSpacesCategory(RegressiveCovariantConstructionCategory): @@ -113,7 +116,7 @@ def _repr_object_names(self): return "metric spaces" class ParentMethods: - def _test_metric(self, **options): + def _test_metric_function(self, **options): r""" Test that this metric space has a properly implemented metric. @@ -125,13 +128,13 @@ def _test_metric(self, **options): EXAMPLES:: sage: UHP = HyperbolicPlane().UHP() - sage: UHP._test_metric() + sage: UHP._test_metric_function() sage: elts = [UHP.random_element() for i in range(5)] - sage: UHP._test_metric(some_elements=elts) + sage: UHP._test_metric_function(some_elements=elts) """ tester = self._tester(**options) S = tester.some_elements() - dist = self.metric() + dist = self.metric_function() for a in S: for b in S: d = dist(a, b) @@ -140,14 +143,14 @@ def _test_metric(self, **options): else: tester.assertEqual(d, 0) - def metric(self): + def metric_function(self): """ - Return the metric of ``self``. + Return the metric function of ``self``. EXAMPLES:: sage: UHP = HyperbolicPlane().UHP() - sage: m = UHP.metric() + sage: m = UHP.metric_function() sage: p1 = UHP.get_point(5 + 7*I) sage: p2 = UHP.get_point(1.0 + I) sage: m(p1, p2) @@ -155,6 +158,8 @@ def metric(self): """ return lambda a,b: a.dist(b) + metric = deprecated_function_alias(30062, metric_function) + def dist(self, a, b): """ Return the distance between ``a`` and ``b`` in ``self``. @@ -213,6 +218,45 @@ def dist(self, b): """ return self.parent().dist(self, b) + class Homsets(HomsetsCategory): + """ + The category of homsets of metric spaces + + It consists of the metric maps, that is, the Lipschitz functions + with Lipschitz constant 1. + """ + + class ElementMethods: + + def _test_metric_map(self, **options): + r""" + Test that this metric space morphism is a metric map, + that is, a Lipschitz function with Lipschitz constant 1. + + EXAMPLES:: + + sage: from sage.categories.metric_spaces import MetricSpaces + sage: from sage.categories.morphism import SetMorphism + sage: Q_abs = SetMorphism(Hom(QQ, QQ, MetricSpaces()), operator.__abs__) + sage: TestSuite(Q_abs).run() + + TESTS:: + + sage: Q_square = SetMorphism(Hom(QQ, QQ, MetricSpaces()), lambda x: x ** 2) + sage: TestSuite(Q_square).run(skip=['_test_pickling']) + Failure in _test_metric_map: + Traceback (most recent call last): + ... + AssertionError: ... not less than or equal to ... + ... + The following tests failed: _test_metric_map + """ + tester = self._tester(**options) + S = self.domain().some_elements() + for a in S: + for b in S: + tester.assertLessEqual(self(a).dist(self(b)), a.dist(b)) + class WithRealizations(WithRealizationsCategory): class ParentMethods: def dist(self, a, b): @@ -232,6 +276,51 @@ def dist(self, a, b): R = self.a_realization() return R.dist(R(a), R(b)) + class CartesianProducts(CartesianProductsCategory): + def extra_super_categories(self): + r""" + Implement the fact that a (finite) Cartesian product of metric spaces is + a metric space. + + EXAMPLES:: + + sage: from sage.categories.metric_spaces import MetricSpaces + sage: C = MetricSpaces().CartesianProducts() + sage: C.extra_super_categories() + [Category of metric spaces] + sage: C.super_categories() + [Category of Cartesian products of topological spaces, + Category of metric spaces] + sage: C.axioms() + frozenset() + """ + return [MetricSpaces()] + + class ParentMethods: + + def dist(self, a, b): + r""" + Return the distance between ``a`` and ``b`` in ``self``. + + It is defined as the maximum of the distances within + the Cartesian factors. + + EXAMPLES:: + + sage: from sage.categories.metric_spaces import MetricSpaces + sage: Q2 = QQ.cartesian_product(QQ) + sage: Q2.category() + Join of + Category of Cartesian products of commutative rings and + Category of Cartesian products of metric spaces + sage: Q2 in MetricSpaces() + True + sage: Q2.dist((0, 0), (2, 3)) + 3 + """ + return max(x.dist(y) for x, y in zip(self(a).cartesian_factors(), + self(b).cartesian_factors())) + class SubcategoryMethods: @cached_method def Complete(self): @@ -256,3 +345,35 @@ class Complete(CategoryWithAxiom): The category of complete metric spaces. """ + class CartesianProducts(CartesianProductsCategory): + + def extra_super_categories(self): + r""" + Implement the fact that a (finite) Cartesian product of complete + metric spaces is a complete metric space. + + EXAMPLES:: + + sage: from sage.categories.metric_spaces import MetricSpaces + sage: C = MetricSpaces().Complete().CartesianProducts() + sage: C.extra_super_categories() + [Category of complete metric spaces] + sage: C.super_categories() + [Category of Cartesian products of metric spaces, + Category of complete metric spaces] + sage: C.axioms() + frozenset({'Complete'}) + + sage: R2 = RR.cartesian_product(RR) + sage: R2 in MetricSpaces() + True + sage: R2 in MetricSpaces().Complete() + True + + sage: QR = QQ.cartesian_product(RR) + sage: QR in MetricSpaces() + True + sage: QR in MetricSpaces().Complete() + False + """ + return [MetricSpaces().Complete()] diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index fe453b5662c..d97d37fd9ff 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -15,7 +15,9 @@ from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import LazyImport from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.categories.morphism import SetMorphism from sage.categories.homsets import HomsetsCategory +from sage.categories.homset import Hom from .category import Category from .category_types import Category_module from sage.categories.tensor import TensorProductsCategory, tensor @@ -521,6 +523,38 @@ def extra_super_categories(self): at_startup=True) class ParentMethods: + + def linear_combination(self, iter_of_elements_coeff, factor_on_left=True): + r""" + Return the linear combination `\lambda_1 v_1 + \cdots + + \lambda_k v_k` (resp. the linear combination `v_1 \lambda_1 + + \cdots + v_k \lambda_k`) where ``iter_of_elements_coeff`` iterates + through the sequence `((\lambda_1, v_1), ..., (\lambda_k, v_k))`. + + INPUT: + + - ``iter_of_elements_coeff`` -- iterator of pairs + ``(element, coeff)`` with ``element`` in ``self`` and + ``coeff`` in ``self.base_ring()`` + + - ``factor_on_left`` -- (optional) if ``True``, the coefficients + are multiplied from the left; if ``False``, the coefficients + are multiplied from the right + + EXAMPLES:: + + sage: m = matrix([[0,1],[1,1]]) + sage: J. = JordanAlgebra(m) + sage: J.linear_combination(((a+b, 1), (-2*b + c, -1))) + 1 + (3, -1) + """ + if factor_on_left: + return self.sum(coeff * element + for element, coeff in iter_of_elements_coeff) + else: + return self.sum(element * coeff + for element, coeff in iter_of_elements_coeff) + @cached_method def tensor_square(self): """ @@ -538,6 +572,41 @@ def tensor_square(self): """ return tensor([self, self]) + def module_morphism(self, *, function, category=None, codomain, **keywords): + r""" + Construct a module morphism from ``self`` to ``codomain``. + + Let ``self`` be a module `X` over a ring `R`. + This constructs a morphism `f: X \to Y`. + + INPUT: + + - ``self`` -- a parent `X` in ``Modules(R)``. + + - ``function`` -- a function `f` from `X` to `Y` + + - ``codomain`` -- the codomain `Y` of the morphism (default: + ``f.codomain()`` if it's defined; otherwise it must be specified) + + - ``category`` -- a category or ``None`` (default: ``None``) + + EXAMPLES:: + + sage: V = FiniteRankFreeModule(QQ, 2) + sage: e = V.basis('e'); e + Basis (e_0,e_1) on the 2-dimensional vector space over the Rational Field + sage: neg = V.module_morphism(function=operator.neg, codomain=V); neg + Generic endomorphism of 2-dimensional vector space over the Rational Field + sage: neg(e[0]) + Element -e_0 of the 2-dimensional vector space over the Rational Field + + """ + # Make sure that we only create a module morphism, even if + # domain and codomain have more structure + if category is None: + category = Modules(self.base_ring()) + return SetMorphism(Hom(self, codomain, category), function) + class ElementMethods: pass @@ -676,9 +745,10 @@ def extra_super_categories(self): return [self.base_category()] class ParentMethods: - def base_ring(self): + + def __init_extra__(self): """ - Return the base ring of this Cartesian product. + Initialise the base ring of this Cartesian product. EXAMPLES:: @@ -689,8 +759,54 @@ def base_ring(self): Free module generated by {2, 3, 4} over Integer Ring sage: C.base_ring() Integer Ring + + Check that :trac:`29225` is fixed:: + + sage: M = cartesian_product((ZZ^2, ZZ^3)); M + The Cartesian product of (Ambient free module of rank 2 over the principal ideal domain Integer Ring, Ambient free module of rank 3 over the principal ideal domain Integer Ring) + sage: M.category() + Category of Cartesian products of modules with basis over (euclidean domains and infinite enumerated sets and metric spaces) + sage: M.base_ring() + Integer Ring + + sage: A = cartesian_product((QQ^2, QQ['x'])); A + The Cartesian product of (Vector space of dimension 2 over Rational Field, Univariate Polynomial Ring in x over Rational Field) + sage: A.category() + Category of Cartesian products of vector spaces over (number fields and quotient fields and metric spaces) + sage: A.base_ring() + Rational Field + + This currently only works if all factors have the same + base ring:: + + sage: B = cartesian_product((ZZ['x'], QQ^3)); B + The Cartesian product of (Univariate Polynomial Ring in x over Integer Ring, Vector space of dimension 3 over Rational Field) + sage: B.category() + Category of Cartesian products of commutative additive groups + sage: B.base_ring() + """ + factors = self._sets + if factors: + R = factors[0].base_ring() + if all(A.base_ring() is R for A in factors): + self._base = R + + class ElementMethods: + + def _lmul_(self, x): + """ + Return the product of `x` with ``self``. + + EXAMPLES:: + + sage: A = FreeModule(ZZ, 2) + sage: B = cartesian_product([A, A]); B + The Cartesian product of (Ambient free module of rank 2 over the principal ideal domain Integer Ring, Ambient free module of rank 2 over the principal ideal domain Integer Ring) + sage: 5*B(([1, 2], [3, 4])) + ((5, 10), (15, 20)) """ - return self._sets[0].base_ring() + return self.parent()._cartesian_product_of_elements( + x*y for y in self.cartesian_factors()) class TensorProducts(TensorProductsCategory): """ diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 7b43e95ad7d..3949f0ee246 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -29,11 +29,8 @@ from sage.categories.fields import Fields from sage.categories.modules import Modules from sage.categories.poor_man_map import PoorManMap -from sage.rings.infinity import Infinity from sage.structure.element import Element, parent -import six - lazy_import('sage.modules.with_basis.morphism', ['ModuleMorphismByLinearity', @@ -258,7 +255,7 @@ def module_morphism(self, on_basis=None, matrix=None, function=None, - ``codomain`` -- the codomain `Y` of the morphism (default: ``f.codomain()`` if it's defined; otherwise it must be specified) - - ``category`` -- a category or ``None`` (default: `None``) + - ``category`` -- a category or ``None`` (default: ``None``) - ``zero`` -- the zero of the codomain (default: ``codomain.zero()``); can be used (with care) to define affine maps. @@ -596,7 +593,64 @@ def _repr_(self): name = "Free module generated by {}".format(self.basis().keys()) return name + " over {}".format(self.base_ring()) - def echelon_form(self, elements, row_reduced=False): + def _compute_support_order(self, elements, support_order=None): + """ + Return the support of a set of elements in ``self`` sorted + in some order. + + INPUT: + + - ``elements`` -- the list of elements + - ``support_order`` -- (optional) either something that can + be converted into a tuple or a key function + + EXAMPLES: + + A finite dimensional module:: + + sage: V = CombinatorialFreeModule(QQ, range(10), prefix='x') + sage: B = V.basis() + sage: elts = [B[0] - 2*B[3], B[5] + 2*B[0], B[2], B[3], B[1] + B[2] + B[8]] + sage: V._compute_support_order(elts) + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + sage: V._compute_support_order(elts, [1,2,0,4,3,5,9,8,7,6]) + (1, 2, 0, 4, 3, 5, 9, 8, 7, 6) + sage: V._compute_support_order(elts, lambda x: -x) + (8, 5, 3, 2, 1, 0) + + An infinite dimensional module:: + + sage: V = CombinatorialFreeModule(QQ, ZZ, prefix='z') + sage: B = V.basis() + sage: elts = [B[0] - 2*B[3], B[5] + 2*B[0], B[2], B[3], B[1] + B[2] + B[8]] + sage: V._compute_support_order(elts) + (0, 1, 2, 3, 5, 8) + sage: V._compute_support_order(elts, [1,2,0,4,3,5,9,8,7,6]) + (1, 2, 0, 4, 3, 5, 9, 8, 7, 6) + sage: V._compute_support_order(elts, lambda x: -x) + (8, 5, 3, 2, 1, 0) + """ + if support_order is None: + try: + support_order = self.get_order() + except (ValueError, TypeError, NotImplementedError, AttributeError): + support_order = set() + for y in elements: + support_order.update(y.support()) + try: # Try to sort to make the output more consistent + support_order = sorted(support_order) + except (ValueError, TypeError): + pass + try: + support_order = tuple(support_order) + except (ValueError, TypeError): + support = set() + for y in elements: + support.update(y.support()) + support_order = sorted(support, key=support_order) + return tuple(support_order) + + def echelon_form(self, elements, row_reduced=False, order=None): r""" Return a basis in echelon form of the subspace spanned by a finite set of elements. @@ -606,61 +660,42 @@ def echelon_form(self, elements, row_reduced=False): - ``elements`` -- a list or finite iterable of elements of ``self`` - ``row_reduced`` -- (default: ``False``) whether to compute the basis for the row reduced echelon form + - ``order`` -- (optional) either something that can + be converted into a tuple or a key function OUTPUT: - A list of elements of ``self`` whose expressions as - vectors form a matrix in echelon form. If ``base_ring`` is - specified, then the calculation is achieved in this base - ring. + A list of elements of ``self`` whose expressions as vectors + form a matrix in echelon form. If ``base_ring`` is specified, + then the calculation is achieved in this base ring. EXAMPLES:: - sage: X = CombinatorialFreeModule(QQ, range(3), prefix="x") - sage: x = X.basis() - sage: V = X.echelon_form([x[0]-x[1], x[0]-x[2],x[1]-x[2]]); V - [x[0] - x[2], x[1] - x[2]] - sage: matrix(list(map(vector, V))) - [ 1 0 -1] - [ 0 1 -1] - - :: - - sage: F = CombinatorialFreeModule(ZZ, [1,2,3,4]) - sage: B = F.basis() - sage: elements = [B[1]-17*B[2]+6*B[3], B[1]-17*B[2]+B[4]] - sage: F.echelon_form(elements) - [B[1] - 17*B[2] + B[4], 6*B[3] - B[4]] - - :: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: a,b,c = F.basis() - sage: F.echelon_form([8*a+b+10*c, -3*a+b-c, a-b-c]) - [B['a'] + B['c'], B['b'] + 2*B['c']] - - :: - sage: R. = QQ[] - sage: C = CombinatorialFreeModule(R, range(3), prefix='x') - sage: x = C.basis() - sage: C.echelon_form([x[0] - x[1], 2*x[1] - 2*x[2], x[0] - x[2]]) - [x[0] - x[2], x[1] - x[2]] + sage: C = CombinatorialFreeModule(R, ZZ, prefix='z') + sage: z = C.basis() + sage: C.echelon_form([z[0] - z[1], 2*z[1] - 2*z[2], z[0] - z[2]]) + [z[0] - z[2], z[1] - z[2]] """ + order = self._compute_support_order(elements, order) + from sage.matrix.constructor import matrix - mat = matrix(self.base_ring(), [g._vector_() for g in elements]) + mat = matrix(self.base_ring(), [[g[s] for s in order] for g in elements]) # Echelonizing a matrix over a field returned the rref - if row_reduced and self.base_ring() not in Fields: + if row_reduced and self.base_ring() not in Fields(): try: mat = mat.rref().change_ring(self.base_ring()) except (ValueError, TypeError): raise ValueError("unable to compute the row reduced echelon form") else: mat.echelonize() - return [self.from_vector(vec) for vec in mat if vec] + return [self._from_dict({order[i]: c for i, c in enumerate(vec) if c}, + remove_zeros=False) + for vec in mat if vec] def submodule(self, gens, check=True, already_echelonized=False, - unitriangular=False, category=None): + unitriangular=False, support_order=None, category=None, + *args, **opts): r""" The submodule spanned by a finite set of elements. @@ -678,6 +713,9 @@ def submodule(self, gens, check=True, already_echelonized=False, - ``unitriangular`` -- (default: ``False``) whether the lift morphism is unitriangular + - ``support_order`` -- (optional) either something that can + be converted into a tuple or a key function + If ``already_echelonized`` is ``False``, then the generators are put in reduced echelon form using :meth:`echelonize`, and reindexed by `0,1,...`. @@ -806,17 +844,85 @@ def submodule(self, gens, check=True, already_echelonized=False, [ 0 1] [-1 -1] + We now construct a (finite-dimensional) submodule of an + infinite dimensional free module:: + + sage: C = CombinatorialFreeModule(QQ, ZZ, prefix='z') + sage: z = C.basis() + sage: gens = [z[0] - z[1], 2*z[1] - 2*z[2], z[0] - z[2]] + sage: Y = C.submodule(gens) + sage: [Y.lift(b) for b in Y.basis()] + [z[0] - z[2], z[1] - z[2]] + TESTS:: sage: TestSuite(Y).run() sage: TestSuite(center).run() """ + support_order = self._compute_support_order(gens, support_order) if not already_echelonized: - gens = self.echelon_form(gens, unitriangular) + gens = self.echelon_form(gens, unitriangular, order=support_order) + from sage.modules.with_basis.subquotient import SubmoduleWithBasis return SubmoduleWithBasis(gens, ambient=self, + support_order=support_order, unitriangular=unitriangular, - category=category) + category=category, *args, **opts) + + def quotient_module(self, submodule, check=True, already_echelonized=False, category=None): + r""" + Construct the quotient module ``self`` / ``submodule``. + + INPUT: + + - ``submodule`` -- a submodule with basis of ``self``, or + something that can be turned into one via + ``self.submodule(submodule)`` + + - ``check``, ``already_echelonized`` -- passed down to + :meth:`ModulesWithBasis.ParentMethods.submodule` + + .. WARNING:: + + At this point, this only supports quotients by free + submodules admitting a basis in unitriangular echelon + form. In this case, the quotient is also a free + module, with a basis consisting of the retract of a + subset of the basis of ``self``. + + EXAMPLES:: + + sage: X = CombinatorialFreeModule(QQ, range(3), prefix="x") + sage: x = X.basis() + sage: Y = X.quotient_module([x[0]-x[1], x[1]-x[2]], already_echelonized=True) + sage: Y.print_options(prefix='y'); Y + Free module generated by {2} over Rational Field + sage: y = Y.basis() + sage: y[2] + y[2] + sage: y[2].lift() + x[2] + sage: Y.retract(x[0]+2*x[1]) + 3*y[2] + + sage: R. = QQ[] + sage: C = CombinatorialFreeModule(R, range(3), prefix='x') + sage: x = C.basis() + sage: gens = [x[0] - x[1], 2*x[1] - 2*x[2], x[0] - x[2]] + sage: Y = X.quotient_module(gens) + + .. SEEALSO:: + + - :meth:`Modules.WithBasis.ParentMethods.submodule` + - :meth:`Rings.ParentMethods.quotient` + - :class:`sage.modules.with_basis.subquotient.QuotientModuleWithBasis` + """ + from sage.modules.with_basis.subquotient import SubmoduleWithBasis, QuotientModuleWithBasis + if not isinstance(submodule, SubmoduleWithBasis): + submodule = self.submodule(submodule, check=check, + unitriangular=True, + already_echelonized=already_echelonized) + return QuotientModuleWithBasis(submodule, category=category) def tensor(*parents, **kwargs): """ @@ -860,6 +966,7 @@ def cardinality(self): sage: s.cardinality() +Infinity """ + from sage.rings.infinity import Infinity if self.dimension() == Infinity: return Infinity return self.base_ring().cardinality() ** self.dimension() @@ -1006,37 +1113,6 @@ def sum_of_terms(self, terms): """ return self.sum(self.term(index, coeff) for (index, coeff) in terms) - def linear_combination(self, iter_of_elements_coeff, factor_on_left=True): - r""" - Return the linear combination `\lambda_1 v_1 + \cdots + - \lambda_k v_k` (resp. the linear combination `v_1 \lambda_1 + - \cdots + v_k \lambda_k`) where ``iter_of_elements_coeff`` iterates - through the sequence `((\lambda_1, v_1), ..., (\lambda_k, v_k))`. - - INPUT: - - - ``iter_of_elements_coeff`` -- iterator of pairs - ``(element, coeff)`` with ``element`` in ``self`` and - ``coeff`` in ``self.base_ring()`` - - - ``factor_on_left`` -- (optional) if ``True``, the coefficients - are multiplied from the left; if ``False``, the coefficients - are multiplied from the right - - EXAMPLES:: - - sage: m = matrix([[0,1],[1,1]]) - sage: J. = JordanAlgebra(m) - sage: J.linear_combination(((a+b, 1), (-2*b + c, -1))) - 1 + (3, -1) - """ - if factor_on_left: - return self.sum(coeff * element - for element, coeff in iter_of_elements_coeff) - else: - return self.sum(element * coeff - for element, coeff in iter_of_elements_coeff) - def _apply_module_morphism(self, x, on_basis, codomain=False): """ Return the image of ``x`` under the module morphism defined by @@ -1099,12 +1175,12 @@ def _apply_module_morphism(self, x, on_basis, codomain=False): if hasattr( codomain, 'linear_combination' ): mc = x.monomial_coefficients(copy=False) - return codomain.linear_combination( (on_basis(key), coeff) - for key, coeff in six.iteritems(mc) ) + return codomain.linear_combination((on_basis(key), coeff) + for key, coeff in mc.items()) else: return_sum = codomain.zero() mc = x.monomial_coefficients(copy=False) - for key, coeff in six.iteritems(mc): + for key, coeff in mc.items(): return_sum += coeff * on_basis(key) return return_sum @@ -1122,7 +1198,7 @@ def _apply_module_endomorphism(self, x, on_basis): """ mc = x.monomial_coefficients(copy=False) return self.linear_combination( (on_basis(key), coeff) - for key, coeff in six.iteritems(mc) ) + for key, coeff in mc.items()) def dimension(self): """ @@ -1223,7 +1299,6 @@ def random_element(self, n=2): we can find a random element in a trivial module:: sage: class Foo(CombinatorialFreeModule): - ....: _no_generic_basering_coercion = True ....: def _element_constructor_(self,x): ....: if x in self: ....: return x @@ -1415,7 +1490,7 @@ def __len__(self): 4 """ zero = self.parent().base_ring().zero() - return len([key for key, coeff in six.iteritems(self.monomial_coefficients(copy=False)) + return len([key for key, coeff in self.monomial_coefficients(copy=False).items() if coeff != zero]) def length(self): @@ -1464,7 +1539,7 @@ def support(self): [[1], [1, 1, 1], [2, 1], [4]] """ zero = self.parent().base_ring().zero() - return [key for key, coeff in six.iteritems(self.monomial_coefficients(copy=False)) + return [key for key, coeff in self.monomial_coefficients(copy=False).items() if coeff != zero] def monomials(self): @@ -1508,7 +1583,7 @@ def terms(self): P = self.parent() zero = P.base_ring().zero() return [P.term(key, value) - for key, value in six.iteritems(self.monomial_coefficients(copy=False)) + for key, value in self.monomial_coefficients(copy=False).items() if value != zero] def coefficients(self, sort=True): @@ -1546,9 +1621,9 @@ def coefficients(self, sort=True): zero = self.parent().base_ring().zero() mc = self.monomial_coefficients(copy=False) if not sort: - return [value for key, value in six.iteritems(mc) if value != zero] + return [value for key, value in mc.items() if value != zero] - v = sorted([(key, value) for key, value in six.iteritems(mc) + v = sorted([(key, value) for key, value in mc.items() if value != zero]) return [value for key, value in v] diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index 100d04d44d4..42b55b8dda4 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -11,10 +11,8 @@ # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #****************************************************************************** -from six.moves import range from sage.misc.cachefunc import cached_method -from sage.misc.misc_c import prod from sage.categories.category_with_axiom import CategoryWithAxiom from sage.categories.semigroups import Semigroups from sage.misc.lazy_import import LazyImport @@ -104,7 +102,7 @@ def free(index_set=None, names=None, **kwds): """ if names is not None: if isinstance(names, str): - from sage.rings.all import ZZ + from sage.rings.integer_ring import ZZ if ',' not in names and index_set in ZZ: names = [names + repr(i) for i in range(index_set)] else: @@ -155,6 +153,7 @@ def prod(self, args): sage: S.prod([S('a'), S('b')]) 'ab' """ + from sage.misc.misc_c import prod return prod(args, self.one()) def _test_prod(self, **options): @@ -350,7 +349,7 @@ def free(index_set=None, names=None, **kwds): """ if names is not None: if isinstance(names, str): - from sage.rings.all import ZZ + from sage.rings.integer_ring import ZZ if ',' not in names and index_set in ZZ: names = [names + repr(i) for i in range(index_set)] else: diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index 391422bdf53..dec8b00b748 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -265,11 +265,11 @@ cdef class Morphism(Map): Caveat: the registration of the coercion must be done before any other coercion is registered or discovered:: - sage: phi = Hom(X, Y)(y) + sage: phi = Hom(X, Z)(z^2) sage: phi.register_as_coercion() Traceback (most recent call last): ... - AssertionError: coercion from Univariate Polynomial Ring in x over Integer Ring to Univariate Polynomial Ring in y over Integer Ring already registered or discovered + AssertionError: coercion from Univariate Polynomial Ring in x over Integer Ring to Univariate Polynomial Ring in z over Integer Ring already registered or discovered """ self._codomain.register_coercion(self) diff --git a/src/sage/categories/polyhedra.py b/src/sage/categories/polyhedra.py index a20538948f2..14006e85866 100644 --- a/src/sage/categories/polyhedra.py +++ b/src/sage/categories/polyhedra.py @@ -28,19 +28,23 @@ class PolyhedralSets(Category_over_base_ring): sage: P = Polyhedron() sage: P.parent().category().element_class - + sage: P.parent().category().element_class.mro() - [, + [, + , , , , , , , + , + , + , , , , - <... 'object'>] + ] sage: isinstance(P, P.parent().category().element_class) True """ diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py index a125fc602b3..5bc194ae2d3 100644 --- a/src/sage/categories/primer.py +++ b/src/sage/categories/primer.py @@ -948,10 +948,7 @@ class SubcategoryMethods: sage: x._mul_?? # not tested sage: x._mul_.__module__ 'sage.categories.coercion_methods' - sage: from six import get_method_function as gmf - sage: gmf(x._mul_) is gmf(Magmas.ElementMethods._mul_parent) # py2 - True - sage: gmf(x._mul_) is Magmas.ElementMethods._mul_parent # py3 + sage: x._mul_.__func__ is Magmas.ElementMethods._mul_parent # py3 True ``product`` is a mathematical method implemented by the parent:: @@ -1096,7 +1093,7 @@ class SubcategoryMethods: See :meth:`Modules().DualObjects `. - Algebras, as in group algebras, monoid algebras, ...: - See: :meth:`Sets.ParentMethods.algebras`. + See: :meth:`Sets.ParentMethods.algebra`. Let for example `A` and `B` be two parents, and let us construct the Cartesian product `A \times B \times B`:: diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 27e436c6360..0f2753185b4 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -2,8 +2,6 @@ Coercion via construction functors """ from __future__ import print_function, absolute_import -from six.moves import range -import six from sage.misc.lazy_import import lazy_import from sage.structure.coerce_exceptions import CoercionException @@ -129,9 +127,9 @@ def __mul__(self, other): """ if not isinstance(self, ConstructionFunctor) and not isinstance(other, ConstructionFunctor): raise CoercionException("Non-constructive product") - if isinstance(other,IdentityConstructionFunctor): + if isinstance(other, IdentityConstructionFunctor): return self - if isinstance(self,IdentityConstructionFunctor): + if isinstance(self, IdentityConstructionFunctor): return other return CompositeConstructionFunctor(other, self) @@ -235,7 +233,7 @@ def _repr_(self): def merge(self, other): """ - Merge ``self`` with another construction functor, or return None. + Merge ``self`` with another construction functor, or return ``None``. .. NOTE:: @@ -263,13 +261,13 @@ def commutes(self, other): """ Determine whether ``self`` commutes with another construction functor. - NOTE: + .. NOTE:: - By default, ``False`` is returned in all cases (even if the two - functors are the same, since in this case :meth:`merge` will apply - anyway). So far there is no construction functor that overloads - this method. Anyway, this method only becomes relevant if two - construction functors have the same rank. + By default, ``False`` is returned in all cases (even if the two + functors are the same, since in this case :meth:`merge` will apply + anyway). So far there is no construction functor that overloads + this method. Anyway, this method only becomes relevant if two + construction functors have the same rank. EXAMPLES:: @@ -289,9 +287,9 @@ def expand(self): """ Decompose ``self`` into a list of construction functors. - NOTE: + .. NOTE:: - The default is to return the list only containing ``self``. + The default is to return the list only containing ``self``. EXAMPLES:: @@ -532,7 +530,7 @@ def __mul__(self, other): """ if isinstance(self, CompositeConstructionFunctor): all = [other] + self.all - elif isinstance(other,IdentityConstructionFunctor): + elif isinstance(other, IdentityConstructionFunctor): return self else: all = other.all + [self] @@ -550,17 +548,17 @@ def _repr_(self): """ s = "..." for c in self.all: - s = "%s(%s)" % (c,s) + s = "%s(%s)" % (c, s) return s def expand(self): """ Return expansion of a CompositeConstructionFunctor. - NOTE: + .. NOTE:: - The product over the list of components, as returned by - the ``expand()`` method, is equal to ``self``. + The product over the list of components, as returned by + the ``expand()`` method, is equal to ``self``. EXAMPLES:: @@ -646,7 +644,6 @@ def __eq__(self, other): """ c = (type(self) == type(other)) if not c: - from sage.categories.functor import IdentityFunctor_generic if isinstance(other, IdentityFunctor_generic): return True return c @@ -672,10 +669,10 @@ def __mul__(self, other): """ Compose construction functors to a composit construction functor, unless one of them is the identity. - NOTE: + .. NOTE:: - The product is in functorial notation, i.e., when applying the product to an object - then the second factor is applied first. + The product is in functorial notation, i.e., when applying the + product to an object then the second factor is applied first. TESTS:: @@ -921,14 +918,14 @@ def __ne__(self, other): def merge(self, other): """ - Merge ``self`` with another construction functor, or return None. + Merge ``self`` with another construction functor, or return ``None``. - NOTE: + .. NOTE:: - Internally, the merging is delegated to the merging of - multipolynomial construction functors. But in effect, - this does the same as the default implementation, that - returns ``None`` unless the to-be-merged functors coincide. + Internally, the merging is delegated to the merging of + multipolynomial construction functors. But in effect, + this does the same as the default implementation, that + returns ``None`` unless the to-be-merged functors coincide. EXAMPLES:: @@ -1076,7 +1073,7 @@ def __mul__(self, other): sage: G*F MPoly[x,y,t] """ - if isinstance(other,IdentityConstructionFunctor): + if isinstance(other, IdentityConstructionFunctor): return self if isinstance(other, MultiPolynomialFunctor): if self.term_order != other.term_order: @@ -1084,15 +1081,15 @@ def __mul__(self, other): if set(self.vars).intersection(other.vars): raise CoercionException("Overlapping variables (%s,%s)" % (self.vars, other.vars)) return MultiPolynomialFunctor(other.vars + self.vars, self.term_order) - elif isinstance(other, CompositeConstructionFunctor) \ - and isinstance(other.all[-1], MultiPolynomialFunctor): + elif (isinstance(other, CompositeConstructionFunctor) + and isinstance(other.all[-1], MultiPolynomialFunctor)): return CompositeConstructionFunctor(other.all[:-1], self * other.all[-1]) else: return CompositeConstructionFunctor(other, self) def merge(self, other): """ - Merge ``self`` with another construction functor, or return None. + Merge ``self`` with another construction functor, or return ``None``. EXAMPLES:: @@ -1251,7 +1248,7 @@ def __init__(self, gens, order, implementation): True """ - if len(gens)<1: + if not gens: raise ValueError("Infinite Polynomial Rings have at least one generator") ConstructionFunctor.__init__(self, Rings(), Rings()) self._gens = tuple(gens) @@ -1260,7 +1257,7 @@ def __init__(self, gens, order, implementation): def _apply_functor_to_morphism(self, f): """ - Morphisms for inifinite polynomial rings are not implemented yet. + Morphisms for infinite polynomial rings are not implemented yet. TESTS:: @@ -1270,10 +1267,10 @@ def _apply_functor_to_morphism(self, f): sage: R.construction()[0](f) # indirect doctest Traceback (most recent call last): ... - NotImplementedError: Morphisms for inifinite polynomial rings are not implemented yet. + NotImplementedError: Morphisms for infinite polynomial rings are not implemented yet. """ - raise NotImplementedError("Morphisms for inifinite polynomial rings are not implemented yet.") + raise NotImplementedError("Morphisms for infinite polynomial rings are not implemented yet.") def _apply_functor(self, R): """ @@ -1298,7 +1295,7 @@ def _repr_(self): InfPoly{[a,b,x], "degrevlex", "sparse"} """ - return 'InfPoly{[%s], "%s", "%s"}'%(','.join(self._gens), self._order, self._imple) + return 'InfPoly{[%s], "%s", "%s"}' % (','.join(self._gens), self._order, self._imple) def __eq__(self, other): """ @@ -1338,10 +1335,10 @@ def __mul__(self, other): """ Compose construction functors to a composite construction functor, unless one of them is the identity. - NOTE: + .. NOTE:: - The product is in functorial notation, i.e., when applying the product to an object - then the second factor is applied first. + The product is in functorial notation, i.e., when applying the + product to an object then the second factor is applied first. TESTS:: @@ -1360,7 +1357,7 @@ def __mul__(self, other): InfPoly{[x,y], "degrevlex", "dense"}(FractionField(...)) """ - if isinstance(other,IdentityConstructionFunctor): + if isinstance(other, IdentityConstructionFunctor): return self if isinstance(other, self.__class__): # INT = set(self._gens).intersection(other._gens) @@ -1400,7 +1397,7 @@ def __mul__(self, other): BadOverlap = False for x in othervars: if x.count('_') == 1: - g,n = x.split('_') + g, n = x.split('_') if n.isdigit(): if g.isalnum(): # we can interprete x in any InfinitePolynomialRing if g in self._gens: # we can interprete x in self, hence, we will not use it as a variable anymore. @@ -1408,12 +1405,12 @@ def __mul__(self, other): IsOverlap = True # some variables of other can be interpreted in self. if OverlappingVars: # Is OverlappingVars in the right order? - g0,n0 = OverlappingVars[-1].split('_') + g0, n0 = OverlappingVars[-1].split('_') i = self._gens.index(g) i0 = self._gens.index(g0) - if iint(n0): # wrong order + if i == i0 and int(n) > int(n0): # wrong order BadOverlap = True OverlappingVars.append(x) else: @@ -1431,18 +1428,18 @@ def __mul__(self, other): if BadOverlap: # the overlapping variables appear in the wrong order raise CoercionException("Overlapping variables (%s,%s) are incompatible" % (self._gens, OverlappingVars)) - if len(OverlappingVars)>1: # multivariate, hence, the term order matters - if other.term_order.name()!=self._order: + if len(OverlappingVars) > 1: # multivariate, hence, the term order matters + if other.term_order.name() != self._order: raise CoercionException("Incompatible term orders %s, %s" % (self._order, other.term_order.name())) # ok, the overlap is fine, we will return something. if RemainingVars: # we can only partially merge other into self - if len(RemainingVars)>1: - return CompositeConstructionFunctor(MultiPolynomialFunctor(RemainingVars,term_order=other.term_order), self) + if len(RemainingVars) > 1: + return CompositeConstructionFunctor(MultiPolynomialFunctor(RemainingVars, term_order=other.term_order), self) return CompositeConstructionFunctor(PolynomialFunctor(RemainingVars[0]), self) return self return CompositeConstructionFunctor(other, self) - def merge(self,other): + def merge(self, other): """ Merge two construction functors of infinite polynomial rings, regardless of monomial order and implementation. @@ -1475,13 +1472,13 @@ def merge(self,other): return self return None try: - OUT = self*other + OUT = self * other # The following happens if "other" has the same order type etc. if not isinstance(OUT, CompositeConstructionFunctor): return OUT except CoercionException: pass - if isinstance(other,InfinitePolynomialFunctor): + if isinstance(other, InfinitePolynomialFunctor): # We don't require that the orders coincide. This is a difference to self*other # We only merge if other's generators are an ordered subset of self's generators for g in other._gens: @@ -1489,7 +1486,7 @@ def merge(self,other): return None # The sequence of variables is part of the ordering. It must coincide in both rings Ind = [self._gens.index(g) for g in other._gens] - if sorted(Ind)!=Ind: + if sorted(Ind) != Ind: return None # OK, other merges into self. Now, choose the default dense implementation, # unless both functors refer to the sparse implementation @@ -1635,6 +1632,7 @@ def __ne__(self, other): def merge(self, other): """ Merging is only happening if both functors are matrix functors of the same dimension. + The result is sparse if and only if both given functors are sparse. EXAMPLES:: @@ -1715,10 +1713,10 @@ def __init__(self, var, multi_variate=False): """ Functor.__init__(self, Rings(), Rings()) - if not isinstance(var, (six.string_types,tuple,list)): + if not isinstance(var, (str, tuple, list)): raise TypeError("variable name or list of variable names expected") self.var = var - self.multi_variate = multi_variate or not isinstance(var, six.string_types) + self.multi_variate = multi_variate or not isinstance(var, str) def _apply_functor(self, R): """ @@ -1791,6 +1789,7 @@ def __ne__(self, other): def merge(self, other): """ Two Laurent polynomial construction functors merge if the variable names coincide. + The result is multivariate if one of the arguments is multivariate. EXAMPLES:: @@ -1860,7 +1859,7 @@ def __init__(self, n, is_sparse=False, inner_product_matrix=None): """ # Functor.__init__(self, Rings(), FreeModules()) # FreeModules() takes a base ring # Functor.__init__(self, Objects(), Objects()) # Object() makes no sense, since FreeModule raises an error, e.g., on Set(['a',1]). - ## FreeModule requires a commutative ring. Thus, we have + # FreeModule requires a commutative ring. Thus, we have Functor.__init__(self, CommutativeRings(), CommutativeAdditiveGroups()) self.n = n self.is_sparse = is_sparse @@ -1908,7 +1907,7 @@ def _apply_functor_to_morphism(self, f): ... NotImplementedError: Can not create induced morphisms of free modules yet """ - ## TODO: Implement this! + # TODO: Implement this! raise NotImplementedError("Can not create induced morphisms of free modules yet") def __eq__(self, other): @@ -1928,7 +1927,8 @@ def __eq__(self, other): True """ if isinstance(other, VectorFunctor): - return (self.n == other.n and self.inner_product_matrix==other.inner_product_matrix) + return (self.n == other.n and + self.inner_product_matrix == other.inner_product_matrix) return False def __ne__(self, other): @@ -2020,10 +2020,10 @@ class SubspaceFunctor(ConstructionFunctor): """ Constructing a subspace of an ambient free module, given by a basis. - NOTE: + .. NOTE:: - This construction functor keeps track of the basis. It can only be applied - to free modules into which this basis coerces. + This construction functor keeps track of the basis. It can only be + applied to free modules into which this basis coerces. EXAMPLES:: @@ -2064,11 +2064,11 @@ def __init__(self, basis): [1 2 3] [4 0 1] """ -## Functor.__init__(self, FreeModules(), FreeModules()) # takes a base ring -## Functor.__init__(self, Objects(), Objects()) # is too general - ## It seems that the category of commutative additive groups - ## currently is the smallest base ring free category that - ## contains in- and output +# Functor.__init__(self, FreeModules(), FreeModules()) # takes a base ring +# Functor.__init__(self, Objects(), Objects()) # is too general + # It seems that the category of commutative additive groups + # currently is the smallest base ring free category that + # contains in- and output Functor.__init__(self, CommutativeAdditiveGroups(), CommutativeAdditiveGroups()) self.basis = basis @@ -2239,7 +2239,8 @@ def merge(self, other): if not self.basis: return other try: - P = pushout(self.basis[0].parent().ambient_module(),other.basis[0].parent().ambient_module()) + P = pushout(self.basis[0].parent().ambient_module(), + other.basis[0].parent().ambient_module()) except CoercionException: return None try: @@ -2248,7 +2249,7 @@ def merge(self, other): submodule = P.span except AttributeError: return None - S = submodule(self.basis+other.basis).echelonized_basis() + S = submodule(self.basis + other.basis).echelonized_basis() return SubspaceFunctor(S) else: return None @@ -2402,10 +2403,10 @@ def __init__(self, p, prec, extras=None): from sage.rings.infinity import Infinity if self.p == Infinity: if self.type not in self._real_types: - raise ValueError("completion type must be one of %s"%(", ".join(self._real_types))) + raise ValueError("completion type must be one of %s" % (", ".join(self._real_types))) else: if self.type not in self._dvr_types: - raise ValueError("completion type must be one of %s"%(", ".join(self._dvr_types[1:]))) + raise ValueError("completion type must be one of %s" % (", ".join(self._dvr_types[1:]))) def _repr_(self): """ @@ -2434,36 +2435,36 @@ def _apply_functor(self, R): """ try: - if len(self.extras) == 0: + if not self.extras: if self.type is None: try: return R.completion(self.p, self.prec) except TypeError: return R.completion(self.p, self.prec, {}) else: - return R.completion(self.p, self.prec, {'type':self.type}) + return R.completion(self.p, self.prec, {'type': self.type}) else: extras = self.extras.copy() extras['type'] = self.type return R.completion(self.p, self.prec, extras) - except (NotImplementedError,AttributeError): + except (NotImplementedError, AttributeError): if R.construction() is None: - raise NotImplementedError("Completion is not implemented for %s"%R.__class__) + raise NotImplementedError("Completion is not implemented for %s" % R.__class__) F, BR = R.construction() M = self.merge(F) or F.merge(self) if M is not None: return M(BR) if self.commutes(F) or F.commutes(self): return F(self(BR)) - raise NotImplementedError("Don't know how to apply %s to %s"%(repr(self),repr(R))) + raise NotImplementedError("Don't know how to apply %s to %s" % (repr(self), repr(R))) def __eq__(self, other): """ - NOTE: + .. NOTE:: - Only the prime used in the completion is relevant to comparison - of Completion functors, although the resulting rings also take - the precision into account. + Only the prime used in the completion is relevant to comparison + of Completion functors, although the resulting rings also take + the precision into account. TESTS:: @@ -2586,11 +2587,16 @@ def merge(self, other): from sage.all import Infinity if self.p == Infinity: new_prec = min(self.prec, other.prec) - new_type = self._real_types[min(self._real_types.index(self.type), \ + new_type = self._real_types[min(self._real_types.index(self.type), self._real_types.index(other.type))] - new_scinot = max(self.extras.get('sci_not',0), other.extras.get('sci_not',0)) - new_rnd = min(self.extras.get('rnd', 0), other.extras.get('rnd', 0)) - return CompletionFunctor(self.p, new_prec, {'type':new_type, 'sci_not':new_scinot, 'rnd':new_rnd}) + new_scinot = max(self.extras.get('sci_not', 0), + other.extras.get('sci_not', 0)) + new_rnd = min(self.extras.get('rnd', 0), + other.extras.get('rnd', 0)) + return CompletionFunctor(self.p, new_prec, + {'type': new_type, + 'sci_not': new_scinot, + 'rnd': new_rnd}) else: new_type = self._dvr_types[min(self._dvr_types.index(self.type), self._dvr_types.index(other.type))] if new_type in ('fixed-mod', 'floating-point'): @@ -2604,13 +2610,13 @@ def merge(self, other): extras['type'] = new_type return CompletionFunctor(self.p, new_prec, extras) -## Completion has a lower rank than FractionField -## and is thus applied first. However, fact is that -## both commute. This is used in the call method, -## since some fraction fields have no completion method -## implemented. +# Completion has a lower rank than FractionField +# and is thus applied first. However, fact is that +# both commute. This is used in the call method, +# since some fraction fields have no completion method +# implemented. - def commutes(self,other): + def commutes(self, other): """ Completion commutes with fraction fields. @@ -2657,9 +2663,9 @@ class QuotientFunctor(ConstructionFunctor): """ Construction functor for quotient rings. - NOTE: + .. NOTE:: - The functor keeps track of variable names. + The functor keeps track of variable names. EXAMPLES:: @@ -2715,7 +2721,7 @@ def __init__(self, I, names=None, as_field=False): self.I = I if names is None: self.names = None - elif isinstance(names, six.string_types): + elif isinstance(names, str): self.names = (names,) else: self.names = tuple(names) @@ -2755,12 +2761,12 @@ def _apply_functor(self, R): if I.ring().has_coerce_map_from(R): R = I.ring() else: - R = pushout(R,I.ring().base_ring()) - I = [R(1)*t for t in I.gens()]*R + R = pushout(R, I.ring().base_ring()) + I = [R.one() * t for t in I.gens()] * R try: - Q = R.quo(I,names=self.names) + Q = R.quo(I, names=self.names) except IndexError: # That may happen! - raise CoercionException("Can not apply this quotient functor to %s"%R) + raise CoercionException("Can not apply this quotient functor to %s" % R) if self.as_field:# and hasattr(Q, 'field'): try: Q = Q.field() @@ -3049,9 +3055,9 @@ def _apply_functor(self, R): """ from sage.all import QQ, ZZ, CyclotomicField if self.cyclotomic: - if R==QQ: + if R == QQ: return CyclotomicField(self.cyclotomic) - if R==ZZ: + if R == ZZ: return CyclotomicField(self.cyclotomic).maximal_order() if len(self.polys) == 1: return R.extension(self.polys[0], names=self.names[0], embedding=self.embeddings[0], @@ -3093,7 +3099,7 @@ def __ne__(self, other): __hash__ = ConstructionFunctor.__hash__ - def merge(self,other): + def merge(self, other): """ Merging with another :class:`AlgebraicExtensionFunctor`. @@ -3116,7 +3122,7 @@ def merge(self,other): - If these two extensions are defined by Conway polynomials over finite fields, merges them into a single extension of degree the lcm of the two degrees. - - Otherwise, None is returned. + - Otherwise, ``None`` is returned. REMARK: @@ -3187,12 +3193,12 @@ def merge(self,other): # *after* expanding the functors. Hence, we can # assume that both functors have a single variable. # But for being on the safe side...: - if len(self.names)!=1 or len(other.names)!=1: + if not (len(self.names) == 1 == len(other.names)): return None -## We don't accept a forgetful coercion, since, together -## with bidirectional coercions between two embedded -## number fields, it would yield to contradictions in -## the coercion system. +# We don't accept a forgetful coercion, since, together +# with bidirectional coercions between two embedded +# number fields, it would yield to contradictions in +# the coercion system. # if self.polys==other.polys and self.names==other.names: # # We have a forgetful functor: # if self.embeddings==[None]: @@ -3200,7 +3206,7 @@ def merge(self,other): # if other.embeddings==[None]: # return other # ... or we may use the given embeddings: - if self.embeddings!=[None] and other.embeddings!=[None]: + if self.embeddings != [None] and other.embeddings != [None]: from sage.all import QQ KS = self(QQ) KO = other(QQ) @@ -3219,20 +3225,21 @@ def merge(self,other): # Finite fields and unramified local extensions may use # integers to encode degrees of extensions. from sage.rings.integer import Integer - if (isinstance(self.polys[0], Integer) and isinstance(other.polys[0], Integer) - and self.embeddings == other.embeddings == [None] - and self.structures == other.structures == [None] - and self.kwds == other.kwds): + if (isinstance(self.polys[0], Integer) + and isinstance(other.polys[0], Integer) + and self.embeddings == other.embeddings == [None] + and self.structures == other.structures == [None] + and self.kwds == other.kwds): return AlgebraicExtensionFunctor([self.polys[0].lcm(other.polys[0])], [None], **self.kwds) def __mul__(self, other): """ Compose construction functors to a composit construction functor, unless one of them is the identity. - NOTE: + .. NOTE:: - The product is in functorial notation, i.e., when applying the product to an object - then the second factor is applied first. + The product is in functorial notation, i.e., when applying the + product to an object then the second factor is applied first. TESTS:: @@ -3244,7 +3251,7 @@ def __mul__(self, other): True """ - if isinstance(other,IdentityConstructionFunctor): + if isinstance(other, IdentityConstructionFunctor): return self if isinstance(other, AlgebraicExtensionFunctor): if set(self.names).intersection(other.names): @@ -3255,8 +3262,8 @@ def __mul__(self, other): precs=self.precs + other.precs, implementations=self.implementations + other.implementations, **self.kwds) - elif isinstance(other, CompositeConstructionFunctor) \ - and isinstance(other.all[-1], AlgebraicExtensionFunctor): + elif (isinstance(other, CompositeConstructionFunctor) + and isinstance(other.all[-1], AlgebraicExtensionFunctor)): return CompositeConstructionFunctor(other.all[:-1], self * other.all[-1]) else: return CompositeConstructionFunctor(other, self) @@ -3336,7 +3343,7 @@ def _apply_functor(self, R): """ try: c = R.construction() - if c is not None and c[0]==self: + if c is not None and c[0] == self: return R except AttributeError: pass @@ -3350,14 +3357,14 @@ def merge(self, other): TESTS:: - sage: K.=NumberField(x^3+x^2+1) + sage: K. = NumberField(x^3+x^2+1) sage: CDF.construction()[0].merge(K.construction()[0]) is None True sage: CDF.construction()[0].merge(CDF.construction()[0]) AlgebraicClosureFunctor """ - if self==other: + if self == other: return self return None # Mathematically, Algebraic Closure subsumes Algebraic Extension. @@ -3392,7 +3399,7 @@ def _repr_(self): sage: PF PermutationGroupFunctor[(1,2)] """ - return "PermutationGroupFunctor%s"%self.gens() + return "PermutationGroupFunctor%s" % self.gens() def __call__(self, R): """ @@ -3420,7 +3427,7 @@ def gens(self): def merge(self, other): """ - Merge ``self`` with another construction functor, or return None. + Merge ``self`` with another construction functor, or return ``None``. EXAMPLES:: @@ -3487,7 +3494,7 @@ def __init__(self, box): sage: FM == loads(dumps(FM)) True """ - ConstructionFunctor.__init__(self,Objects(),Objects()) + ConstructionFunctor.__init__(self, Objects(), Objects()) if not callable(box): raise TypeError("input must be callable") self.box = box @@ -3987,8 +3994,8 @@ def pushout(R, S): S_tower = expand_tower(S_tower[:len(Ss)]) else: # Rc is a list of functors from Z to R and Sc is a list of functors from Z to S - R_tower = expand_tower(R_tower[:len(Rs)+1]) - S_tower = expand_tower(S_tower[:len(Ss)+1]) + R_tower = expand_tower(R_tower[:len(Rs) + 1]) + S_tower = expand_tower(S_tower[:len(Ss) + 1]) Rc = [c[0] for c in R_tower[1:]] Sc = [c[0] for c in S_tower[1:]] @@ -4007,9 +4014,9 @@ def apply_from(Xc): try: while Rc or Sc: # if we are out of functors in either tower, there is no ambiguity - if len(Sc) == 0: + if not Sc: all = apply_from(Rc) - elif len(Rc) == 0: + elif not Rc: all = apply_from(Sc) # if one of the functors has lower rank, do it first elif Rc[-1].rank < Sc[-1].rank: @@ -4066,7 +4073,6 @@ def apply_from(Xc): raise CoercionException(ex) - def pushout_lattice(R, S): r""" Given a pair of objects `R` and `S`, try to construct a @@ -4117,11 +4123,11 @@ def pushout_lattice(R, S): return None # truncate at common ancestor - R_tower = list(reversed(R_tower[:Rs.index(start)+1])) - S_tower = list(reversed(S_tower[:Ss.index(start)+1])) - Rs = [c[1] for c in R_tower] # the list of objects + R_tower = list(reversed(R_tower[:Rs.index(start) + 1])) + S_tower = list(reversed(S_tower[:Ss.index(start) + 1])) + Rs = [c[1] for c in R_tower] # the list of objects Ss = [c[1] for c in S_tower] - Rc = [c[0] for c in R_tower] # the list of functors + Rc = [c[0] for c in R_tower] # the list of functors Sc = [c[0] for c in S_tower] # Here we try and construct a 2-dimensional lattice as follows. @@ -4135,10 +4141,10 @@ def pushout_lattice(R, S): # / \ # Qp Frac(Z[t]) # - for i in range(len(Rs)): - lattice[i,0] = Rs[i] - for j in range(len(Ss)): - lattice[0,j] = Ss[j] + for i, Rsi in enumerate(Rs): + lattice[i, 0] = Rsi + for j, Ssj in enumerate(Ss): + lattice[0, j] = Ssj # Now we attempt to fill in the center, one (diagonal) row at a time, # one commuting square at a time. @@ -4158,42 +4164,43 @@ def pushout_lattice(R, S): # Note that when applying the functors in the correct order, base extension # is not needed (though it may occur in the resulting morphisms). # - for i in range(len(Rc)-1): - for j in range(len(Sc)-1): + for i in range(len(Rc) - 1): + for j in range(len(Sc) - 1): try: - if lattice[i,j+1] == lattice[i+1,j]: + if lattice[i, j + 1] == lattice[i + 1, j]: # In this case we have R <- S -> R # We don't want to perform the operation twice # and all subsequent squares will come from objects # where the operation was already performed (either # to the left or right) Rc[i] = Sc[j] = None # IdentityConstructionFunctor() - lattice[i+1,j+1] = lattice[i,j+1] + lattice[i + 1, j + 1] = lattice[i, j + 1] elif Rc[i] is None and Sc[j] is None: - lattice[i+1,j+1] = lattice[i,j+1] + lattice[i + 1, j + 1] = lattice[i, j + 1] elif Rc[i] is None: - lattice[i+1,j+1] = Sc[j](lattice[i+1,j]) + lattice[i + 1, j + 1] = Sc[j](lattice[i + 1, j]) elif Sc[j] is None: - lattice[i+1,j+1] = Rc[i](lattice[i,j+1]) + lattice[i + 1, j + 1] = Rc[i](lattice[i, j + 1]) else: # For now, we just look at the rank. # TODO: be more sophisticated and query the functors themselves if Rc[i].rank < Sc[j].rank: - lattice[i+1,j+1] = Sc[j](lattice[i+1,j]) - Rc[i] = None # force us to use pre-applied Rc[i] + lattice[i + 1, j + 1] = Sc[j](lattice[i + 1, j]) + Rc[i] = None # force us to use pre-applied Rc[i] else: - lattice[i+1,j+1] = Rc[i](lattice[i,j+1]) - Sc[j] = None # force us to use pre-applied Sc[i] + lattice[i + 1, j + 1] = Rc[i](lattice[i, j + 1]) + Sc[j] = None # force us to use pre-applied Sc[i] except (AttributeError, NameError): # pp(lattice) - for i in range(100): - for j in range(100): + for ni in range(100): + for nj in range(100): try: - R = lattice[i,j] - print(i, j, R) + R = lattice[ni, nj] + print(ni, nj, R) except KeyError: break - raise CoercionException("%s does not support %s" % (lattice[i,j], 'F')) + raise CoercionException("%s does not support %s" + % (lattice[ni, nj], 'F')) # If we are successful, we should have something that looks like this. # @@ -4207,41 +4214,43 @@ def pushout_lattice(R, S): # \ / # Frac(Qp[t]) # - R_loc = len(Rs)-1 - S_loc = len(Ss)-1 + R_loc = len(Rs) - 1 + S_loc = len(Ss) - 1 # Find the composition coercion morphisms along the bottom left... if S_loc > 0: - R_map = lattice[R_loc,1].coerce_map_from(R) + R_map = lattice[R_loc, 1].coerce_map_from(R) for i in range(1, S_loc): - map = lattice[R_loc, i+1].coerce_map_from(lattice[R_loc, i]) # The functor used is implicit here, should it be? + map = lattice[R_loc, i + 1].coerce_map_from(lattice[R_loc, i]) + # The functor used is implicit here, should it be? R_map = map * R_map else: - R_map = R.coerce_map_from(R) # id + R_map = R.coerce_map_from(R) # id # ... and bottom right if R_loc > 0: S_map = lattice[1, S_loc].coerce_map_from(S) for i in range(1, R_loc): - map = lattice[i+1, S_loc].coerce_map_from(lattice[i, S_loc]) + map = lattice[i + 1, S_loc].coerce_map_from(lattice[i, S_loc]) S_map = map * S_map else: - S_map = S.coerce_map_from(S) # id + S_map = S.coerce_map_from(S) # id return R_map, S_map -## def pp(lattice): -## """ -## Used in debugging to print the current lattice. -## """ -## for i in range(100): -## for j in range(100): -## try: -## R = lattice[i,j] -## print(i, j, R) -## except KeyError: -## break +# def pp(lattice): +# """ +# Used in debugging to print the current lattice. +# """ +# for i in range(100): +# for j in range(100): +# try: +# R = lattice[i,j] +# print(i, j, R) +# except KeyError: +# break + def construction_tower(R): """ @@ -4271,12 +4280,13 @@ def construction_tower(R): f, R = c if not isinstance(f, ConstructionFunctor): f = BlackBoxConstructionFunctor(f) - tower.append((f,R)) + tower.append((f, R)) if not isinstance(R, Parent): break c = R.construction() return tower + def expand_tower(tower): """ An auxiliary function that is used in :func:`pushout`. @@ -4315,6 +4325,7 @@ def expand_tower(tower): new_tower.append((fs[0], R)) return list(reversed(new_tower)) + def type_to_parent(P): """ An auxiliary function that is used in :func:`pushout`. @@ -4341,12 +4352,8 @@ def type_to_parent(P): ... TypeError: Not a scalar type. """ - import sage.rings.all - if P in six.integer_types: - return sage.rings.all.ZZ - elif P is float: - return sage.rings.all.RDF - elif P is complex: - return sage.rings.all.CDF - else: + from sage.structure.coerce import py_scalar_parent + parent = py_scalar_parent(P) + if parent is None: raise TypeError("Not a scalar type.") + return parent diff --git a/src/sage/categories/regular_crystals.py b/src/sage/categories/regular_crystals.py index 6512d0f5056..f9466c2fa23 100644 --- a/src/sage/categories/regular_crystals.py +++ b/src/sage/categories/regular_crystals.py @@ -22,8 +22,6 @@ from sage.categories.category_singleton import Category_singleton from sage.categories.crystals import Crystals from sage.categories.tensor import TensorProductsCategory -from sage.combinat.subset import Subsets -from sage.graphs.dot2tex_utils import have_dot2tex class RegularCrystals(Category_singleton): r""" @@ -162,6 +160,7 @@ class ParentMethods: # TODO: this could be a method in Crystals.Algebras.ElementMethods, so that # one could do: + # # sage: C = crystals.Tableaux(['A',2], shape=[2,1]) # sage: M = C.algebra(QQ) # sage: m = M.an_element() @@ -260,7 +259,7 @@ def demazure_subcrystal(self, element, reduced_word, only_support=True): ([[2, 2]], [[1, 2]], 0)] """ from sage.combinat.free_module import CombinatorialFreeModule - from sage.rings.all import QQ + from sage.rings.rational_field import QQ C = CombinatorialFreeModule(QQ, self) D = self.demazure_operator(C(element), reduced_word) if only_support: @@ -449,6 +448,7 @@ def wt_zero(x): edges.append([x, y, i]) from sage.graphs.all import DiGraph G = DiGraph([X, edges], format="vertices_and_edges", immutable=True) + from sage.graphs.dot2tex_utils import have_dot2tex if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels=True, color_by_label=self.cartan_type()._index_set_coloring) @@ -746,6 +746,8 @@ def _test_stembridge_local_axioms(self, index_set=None, verbose=False, **options goodness=True if index_set is None: index_set=self.index_set() + from sage.combinat.subset import Subsets + for (i,j) in Subsets(index_set, 2): if self.e(i) is not None and self.e(j) is not None: triple=self.stembridgeTriple(i,j) @@ -866,6 +868,7 @@ def dual_equivalence_class(self, index_set=None): from sage.graphs.graph import Graph G = Graph([visited, edges], format="vertices_and_edges", immutable=True, multiedges=True) + from sage.graphs.dot2tex_utils import have_dot2tex if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels=True, color_by_label=self.cartan_type()._index_set_coloring) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 6153bfd9bf4..007c38652d7 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -65,19 +65,20 @@ def is_injective(self): """ Return whether or not this morphism is injective. - EXAMPLES: - - This often raises a ``NotImplementedError`` as many homomorphisms do - not implement this method:: + EXAMPLES:: - sage: R. = QQ[] - sage: f = R.hom([x + 1]); f - Ring endomorphism of Univariate Polynomial Ring in x over Rational Field - Defn: x |--> x + 1 - sage: f.is_injective() - Traceback (most recent call last): - ... - NotImplementedError + sage: R. = QQ[] + sage: R.hom([x, y^2], R).is_injective() + True + sage: R.hom([x, x^2], R).is_injective() + False + sage: S. = R.quotient(x^3*y) + sage: R.hom([v, u], S).is_injective() + False + sage: S.hom([-u, v], S).is_injective() + True + sage: S.cover().is_injective() + False If the domain is a field, the homomorphism is injective:: @@ -148,6 +149,13 @@ def is_injective(self): # homomorphism must send the 1 element to the 1 element return True + try: + ker = self.kernel() + except (NotImplementedError, AttributeError): + pass + else: + return ker.is_zero() + if self.domain().characteristic() == 0: if self.codomain().characteristic() != 0: return False @@ -170,8 +178,11 @@ def is_injective(self): if K is self.codomain(): return True - if self.domain().cardinality() > self.codomain().cardinality(): - return False + try: + if self.domain().cardinality() > self.codomain().cardinality(): + return False + except AttributeError: + pass raise NotImplementedError @@ -197,7 +208,7 @@ def _is_nonzero(self): def extend_to_fraction_field(self): r""" - Return the extension of the morphism to fraction fields of + Return the extension of this morphism to fraction fields of the domain and the codomain. EXAMPLES:: @@ -223,6 +234,14 @@ def extend_to_fraction_field(self): Traceback (most recent call last): ... ValueError: the morphism is not injective + + TESTS:: + + sage: A. = RR[] + sage: phi = A.hom([x+1]) + sage: phi.extend_to_fraction_field() + Ring endomorphism of Fraction Field of Univariate Polynomial Ring in x over Real Field with 53 bits of precision + Defn: x |--> x + 1.00000000000000 """ from sage.rings.morphism import RingHomomorphism_from_fraction_field if self.domain().is_field() and self.codomain().is_field(): @@ -230,7 +249,7 @@ def extend_to_fraction_field(self): try: if not self.is_injective(): raise ValueError("the morphism is not injective") - except NotImplementedError: # we trust the user + except (NotImplementedError, TypeError): # we trust the user pass domain = self.domain().fraction_field() codomain = self.codomain().fraction_field() @@ -767,7 +786,6 @@ def quotient(self, I, names=None): ....: def reduce(self, x): ....: R = self.ring() ....: return add([c*R(m) for m,c in x if len(m) < self._power], R(0)) - ....: sage: I = PowerIdeal(F,3) sage: Q = Rings().parent_class.quotient(F, I); Q Quotient of Free Algebra on 3 generators (x, y, z) over Rational Field by the ideal (x^3, x^2*y, x^2*z, x*y*x, x*y^2, x*y*z, x*z*x, x*z*y, x*z^2, y*x^2, y*x*y, y*x*z, y^2*x, y^3, y^2*z, y*z*x, y*z*y, y*z^2, z*x^2, z*x*y, z*x*z, z*y*x, z*y^2, z*y*z, z^2*x, z^2*y, z^3) @@ -916,12 +934,17 @@ def __getitem__(self, arg): sage: GF(17)['a']['b'] Univariate Polynomial Ring in b over Univariate Polynomial Ring in a over Finite Field of size 17 - We can create skew polynomial rings:: + We can create Ore polynomial rings:: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() - sage: k['x',Frob] - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + sage: k['x', Frob] + Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + + sage: R. = QQ[] + sage: der = R.derivation() + sage: R['d', der] + Ore Polynomial Ring in d over Univariate Polynomial Ring in t over Rational Field twisted by d/dt We can also create power series rings by using double brackets:: @@ -1056,7 +1079,7 @@ def normalize_arg(arg): # 1. If arg is a list, try to return a power series ring. if isinstance(arg, list): - if arg == []: + if not arg: raise TypeError("power series rings must have at least one variable") elif len(arg) == 1: # R[["a,b"]], R[[(a,b)]]... @@ -1070,9 +1093,10 @@ def normalize_arg(arg): if isinstance(arg, tuple): from sage.categories.morphism import Morphism - if len(arg) == 2 and isinstance(arg[1], Morphism): - from sage.rings.polynomial.skew_polynomial_ring_constructor import SkewPolynomialRing - return SkewPolynomialRing(self, arg[1], names=arg[0]) + from sage.rings.derivation import RingDerivation + if len(arg) == 2 and isinstance(arg[1], (Morphism, RingDerivation)): + from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing + return OrePolynomialRing(self, arg[1], names=arg[0]) # 2. Otherwise, if all specified elements are algebraic, try to # return an algebraic extension diff --git a/src/sage/categories/semigroups.py b/src/sage/categories/semigroups.py index 3f9939aeb2a..a358149dd13 100644 --- a/src/sage/categories/semigroups.py +++ b/src/sage/categories/semigroups.py @@ -16,7 +16,6 @@ from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import LazyImport -from sage.misc.misc_c import prod from sage.categories.category_with_axiom import CategoryWithAxiom, all_axioms from sage.categories.algebra_functor import AlgebrasCategory from sage.categories.subquotients import SubquotientsCategory @@ -169,6 +168,7 @@ def prod(self, args): ... AssertionError: Cannot compute an empty product in a semigroup """ + from sage.misc.misc_c import prod assert len(args) > 0, "Cannot compute an empty product in a semigroup" return prod(args[1:], args[0]) @@ -543,7 +543,7 @@ def LTrivial(self): .. SEEALSO:: - - :wikipedia:`Green's_relations` + - :wikipedia:`Green%27s_relations` - :class:`Semigroups.SubcategoryMethods.RTrivial` - :class:`Semigroups.SubcategoryMethods.JTrivial` - :class:`Semigroups.SubcategoryMethods.HTrivial` @@ -588,7 +588,7 @@ def RTrivial(self): .. SEEALSO:: - - :wikipedia:`Green's_relations` + - :wikipedia:`Green%27s_relations` - :class:`Semigroups.SubcategoryMethods.LTrivial` - :class:`Semigroups.SubcategoryMethods.JTrivial` - :class:`Semigroups.SubcategoryMethods.HTrivial` @@ -644,7 +644,7 @@ def JTrivial(self): .. SEEALSO:: - - :wikipedia:`Green's_relations` + - :wikipedia:`Green%27s_relations` - :class:`Semigroups.SubcategoryMethods.LTrivial` - :class:`Semigroups.SubcategoryMethods.RTrivial` - :class:`Semigroups.SubcategoryMethods.HTrivial` @@ -680,7 +680,7 @@ def HTrivial(self): .. SEEALSO:: - - :wikipedia:`Green's_relations` + - :wikipedia:`Green%27s_relations` - :class:`Semigroups.SubcategoryMethods.RTrivial` - :class:`Semigroups.SubcategoryMethods.LTrivial` - :class:`Semigroups.SubcategoryMethods.JTrivial` diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 7d48c100d57..f8b1ed19a3f 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -11,7 +11,6 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range from sage.misc.cachefunc import cached_method from sage.misc.sage_unittest import TestSuite @@ -1506,11 +1505,13 @@ def cartesian_product(*parents, **kwargs): sage: cartesian_product([ZZ, ZZ]).category() Join of Category of Cartesian products of commutative rings and + Category of Cartesian products of metric spaces and Category of Cartesian products of enumerated sets sage: cartesian_product([ZZ, ZZ], extra_category=Posets()).category() Join of Category of Cartesian products of commutative rings and Category of posets and + Category of Cartesian products of metric spaces and Category of Cartesian products of enumerated sets """ category = kwargs.pop('category', None) @@ -1717,16 +1718,6 @@ def is_injective(self): To: Finite Field of size 3 sage: f.is_injective() False - - Note that many maps do not implement this method:: - - sage: R. = ZZ[] - sage: f = R.hom([x]) - sage: f.is_injective() - Traceback (most recent call last): - ... - NotImplementedError - """ if self.domain().cardinality() <= 1: return True @@ -1760,10 +1751,7 @@ def is_finite(self): TESTS:: - sage: from six import get_method_function as gmf - sage: gmf(C.is_finite) is gmf(sage.categories.sets_cat.Sets.Infinite.ParentMethods.is_finite) # py2 - True - sage: gmf(C.is_finite) is sage.categories.sets_cat.Sets.Infinite.ParentMethods.is_finite # py3 + sage: C.is_finite.__func__ is sage.categories.sets_cat.Sets.Infinite.ParentMethods.is_finite True """ return False diff --git a/src/sage/categories/sets_with_grading.py b/src/sage/categories/sets_with_grading.py index 9585d861390..36a3a9aa89f 100644 --- a/src/sage/categories/sets_with_grading.py +++ b/src/sage/categories/sets_with_grading.py @@ -14,7 +14,6 @@ from .category_types import Category from sage.categories.sets_cat import Sets from sage.categories.enumerated_sets import EnumeratedSets -from sage.sets.non_negative_integers import NonNegativeIntegers class SetsWithGrading(Category): @@ -145,6 +144,7 @@ def grading_set(self): sage: SetsWithGrading().example().grading_set() Non negative integers """ + from sage.sets.non_negative_integers import NonNegativeIntegers return NonNegativeIntegers() # TODO: diff --git a/src/sage/categories/simplicial_complexes.py b/src/sage/categories/simplicial_complexes.py index b01ff0f2abb..7966237ff86 100644 --- a/src/sage/categories/simplicial_complexes.py +++ b/src/sage/categories/simplicial_complexes.py @@ -45,6 +45,8 @@ class SimplicialComplexes(Category_singleton): @cached_method def super_categories(self): """ + Return the super categories of ``self``. + EXAMPLES:: sage: from sage.categories.simplicial_complexes import SimplicialComplexes @@ -99,3 +101,33 @@ def faces(self): 2: {(1, 3, 4)}} """ + class SubcategoryMethods: + @cached_method + def Connected(self): + """ + Return the full subcategory of the connected objects of ``self``. + + EXAMPLES:: + + sage: from sage.categories.simplicial_complexes import SimplicialComplexes + sage: SimplicialComplexes().Connected() + Category of connected simplicial complexes + + TESTS:: + + sage: SimplicialComplexes().Connected.__module__ + 'sage.categories.simplicial_complexes' + """ + return self._with_axiom('Connected') + + class Connected(CategoryWithAxiom): + """ + The category of connected simplicial complexes. + + EXAMPLES:: + + sage: from sage.categories.simplicial_complexes import SimplicialComplexes + sage: C = SimplicialComplexes().Connected() + sage: TestSuite(C).run() + """ + diff --git a/src/sage/categories/supercrystals.py b/src/sage/categories/supercrystals.py index abd528c1d86..51560ae262d 100644 --- a/src/sage/categories/supercrystals.py +++ b/src/sage/categories/supercrystals.py @@ -257,7 +257,7 @@ def character(self): B[(1, 0, 0, 0, 0)] + B[(0, 1, 0, 0, 0)] + B[(0, 0, 1, 0, 0)] + B[(0, 0, 0, 1, 0)] + B[(0, 0, 0, 0, 1)] """ - from sage.rings.all import ZZ + from sage.rings.integer_ring import ZZ A = self.weight_lattice_realization().algebra(ZZ) return A.sum(A(x.weight()) for x in self) diff --git a/src/sage/categories/topological_spaces.py b/src/sage/categories/topological_spaces.py index 41f98e0decc..409e6cdcd8f 100644 --- a/src/sage/categories/topological_spaces.py +++ b/src/sage/categories/topological_spaces.py @@ -10,6 +10,7 @@ from sage.misc.cachefunc import cached_method from sage.categories.category_with_axiom import CategoryWithAxiom +from sage.categories.cartesian_product import CartesianProductsCategory from sage.categories.covariant_functorial_construction import RegressiveCovariantConstructionCategory from sage.categories.sets_cat import Sets @@ -59,6 +60,25 @@ def _repr_object_names(self): """ return "topological spaces" + class CartesianProducts(CartesianProductsCategory): + def extra_super_categories(self): + r""" + Implement the fact that a (finite) Cartesian product of topological spaces is + a topological space. + + EXAMPLES:: + + sage: from sage.categories.topological_spaces import TopologicalSpaces + sage: C = TopologicalSpaces().CartesianProducts() + sage: C.extra_super_categories() + [Category of topological spaces] + sage: C.super_categories() + [Category of Cartesian products of sets, Category of topological spaces] + sage: C.axioms() + frozenset() + """ + return [TopologicalSpaces()] + class SubcategoryMethods: @cached_method def Connected(self): @@ -101,8 +121,48 @@ class Connected(CategoryWithAxiom): The category of connected topological spaces. """ + class CartesianProducts(CartesianProductsCategory): + def extra_super_categories(self): + r""" + Implement the fact that a (finite) Cartesian product of connected + topological spaces is connected. + + EXAMPLES:: + + sage: from sage.categories.topological_spaces import TopologicalSpaces + sage: C = TopologicalSpaces().Connected().CartesianProducts() + sage: C.extra_super_categories() + [Category of connected topological spaces] + sage: C.super_categories() + [Category of Cartesian products of topological spaces, + Category of connected topological spaces] + sage: C.axioms() + frozenset({'Connected'}) + """ + return [TopologicalSpaces().Connected()] + + class Compact(CategoryWithAxiom): """ The category of compact topological spaces. """ + class CartesianProducts(CartesianProductsCategory): + def extra_super_categories(self): + r""" + Implement the fact that a (finite) Cartesian product of compact + topological spaces is compact. + + EXAMPLES:: + + sage: from sage.categories.topological_spaces import TopologicalSpaces + sage: C = TopologicalSpaces().Compact().CartesianProducts() + sage: C.extra_super_categories() + [Category of compact topological spaces] + sage: C.super_categories() + [Category of Cartesian products of topological spaces, + Category of compact topological spaces] + sage: C.axioms() + frozenset({'Compact'}) + """ + return [TopologicalSpaces().Compact()] diff --git a/src/sage/categories/unital_algebras.py b/src/sage/categories/unital_algebras.py index ec757ade1e7..c1e2f74a3bf 100644 --- a/src/sage/categories/unital_algebras.py +++ b/src/sage/categories/unital_algebras.py @@ -79,9 +79,11 @@ def __init_extra__(self): An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: coercion_model = sage.structure.element.get_coercion_model() sage: coercion_model.discover_coercion(QQ, A) - (Generic morphism: - From: Rational Field - To: An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field, None) + ((map internal to coercion system -- copy before use) + Generic morphism: + From: Rational Field + To: An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field, + None) sage: A(1) # indirect doctest B[word: ] @@ -102,15 +104,17 @@ def __init_extra__(self): sage: F(3) 3*B[0] + sage: class Bar(Parent): + ....: _no_generic_basering_coercion = True + sage: Bar(category=Algebras(QQ)) + doctest:warning...: + DeprecationWarning: the attribute _no_generic_basering_coercion is deprecated, implement _coerce_map_from_base_ring() instead + See http://trac.sagemath.org/19225 for details. + <__main__.Bar_with_category object at 0x...> """ - # If self has an attribute _no_generic_basering_coercion - # set to True, then this declaration is skipped. - # This trick, introduced in #11900, is used in - # sage.matrix.matrix_space.py and - # sage.rings.polynomial.polynomial_ring. - # It will hopefully be refactored into something more - # conceptual later on. if getattr(self, '_no_generic_basering_coercion', False): + from sage.misc.superseded import deprecation + deprecation(19225, "the attribute _no_generic_basering_coercion is deprecated, implement _coerce_map_from_base_ring() instead") return base_ring = self.base_ring() @@ -121,6 +125,71 @@ def __init_extra__(self): # We will not use any generic stuff, since a (presumably) better conversion # has already been registered. return + if base_ring is None: + # It may happen that self.base_ring() is not initialised at this point. + return + + mor = self._coerce_map_from_base_ring() + if mor is not None: + mor._make_weak_references() + try: + self.register_coercion(mor) + except AssertionError: + pass + + def _coerce_map_from_(self, other): + """ + Return a coercion map from ``other`` to ``self``, or ``None``. + + TESTS: + + Check that :trac:`19225` is solved:: + + sage: A = cartesian_product((QQ['z'],)); A + The Cartesian product of (Univariate Polynomial Ring in z over Rational Field,) + sage: A.coerce_map_from(ZZ) + Composite map: + From: Integer Ring + To: The Cartesian product of (Univariate Polynomial Ring in z over Rational Field,) + Defn: Natural morphism: + From: Integer Ring + To: Rational Field + then + Generic morphism: + From: Rational Field + To: The Cartesian product of (Univariate Polynomial Ring in z over Rational Field,) + sage: A(1) + (1,) + """ + if other is self.base_ring(): + return self._coerce_map_from_base_ring() + else: + return self._coerce_map_via([self.base_ring()], other) + + def _coerce_map_from_base_ring(self): + """ + Return a suitable coercion map from the base ring of ``self``. + + TESTS:: + + sage: A = cartesian_product((QQ['z'],)); A + The Cartesian product of (Univariate Polynomial Ring in z over Rational Field,) + sage: A.base_ring() + Rational Field + sage: A._coerce_map_from_base_ring() + Generic morphism: + From: Rational Field + To: The Cartesian product of (Univariate Polynomial Ring in z over Rational Field,) + + Check that :trac:`29312` is fixed:: + + sage: F. = FreeAlgebra(QQ, implementation='letterplace') + sage: F._coerce_map_from_base_ring() + Generic morphism: + From: Rational Field + To: Free Associative Unital Algebra on 3 generators (x, y, z) over Rational Field + """ + base_ring = self.base_ring() # Pick a homset for the morphism to live in... if self in Rings(): @@ -139,7 +208,7 @@ def __init_extra__(self): cat = cat.Distributive() H = Hom(base_ring, self, cat) - # We need to register a coercion from the base ring to self. + # We need to construct a coercion from the base ring to self. # # There is a generic method from_base_ring(), that just does # multiplication with the multiplicative unit. However, the @@ -151,7 +220,8 @@ def __init_extra__(self): # If there is a specialised from_base_ring(), then it should # be used unconditionally. generic_from_base_ring = self.category().parent_class.from_base_ring - if type(self).from_base_ring != generic_from_base_ring: + from_base_ring = self.from_base_ring # bound method + if from_base_ring.__func__ != generic_from_base_ring: # Custom from_base_ring() use_from_base_ring = True if isinstance(generic_from_base_ring, lazy_attribute): @@ -171,7 +241,7 @@ def __init_extra__(self): mor = None if use_from_base_ring: - mor = SetMorphism(function=self.from_base_ring, parent=H) + mor = SetMorphism(function=from_base_ring, parent=H) else: # We have the multiplicative unit, so implement the # coercion from the base ring as multiplying with that. @@ -187,11 +257,7 @@ def __init_extra__(self): mor = SetMorphism(function=one._lmul_, parent=H) except (NotImplementedError, AttributeError, TypeError): pass - if mor is not None: - try: - self.register_coercion(mor) - except AssertionError: - pass + return mor class WithBasis(CategoryWithAxiom_over_base_ring): diff --git a/src/sage/categories/vector_spaces.py b/src/sage/categories/vector_spaces.py index 75ae92b54ce..c31f47ead35 100644 --- a/src/sage/categories/vector_spaces.py +++ b/src/sage/categories/vector_spaces.py @@ -147,7 +147,26 @@ def additional_structure(self): return None class ParentMethods: - pass + + def dimension(self): + """ + Return the dimension of this vector space. + + EXAMPLES:: + + sage: M = FreeModule(FiniteField(19), 100) + sage: W = M.submodule([M.gen(50)]) + sage: W.dimension() + 1 + + sage: M = FiniteRankFreeModule(QQ, 3) + sage: M.dimension() + 3 + sage: M.tensor_module(1,2).dimension() + 27 + + """ + return self.rank() class ElementMethods: pass diff --git a/src/sage/categories/weyl_groups.py b/src/sage/categories/weyl_groups.py index 6ea5cf878ea..2f84ca64bb7 100644 --- a/src/sage/categories/weyl_groups.py +++ b/src/sage/categories/weyl_groups.py @@ -12,8 +12,6 @@ from sage.misc.lazy_import import LazyImport from sage.categories.category_singleton import Category_singleton from sage.categories.coxeter_groups import CoxeterGroups -from sage.rings.infinity import infinity -from sage.rings.rational_field import QQ class WeylGroups(Category_singleton): @@ -279,7 +277,7 @@ def is_pieri_factor(self): return self in self.parent().pieri_factors() - def left_pieri_factorizations(self, max_length = infinity): + def left_pieri_factorizations(self, max_length=None): r""" Returns all factorizations of ``self`` as `uv`, where `u` is a Pieri factor and `v` is an element of the Weyl group. @@ -335,6 +333,9 @@ def left_pieri_factorizations(self, max_length = infinity): sage: W.from_reduced_word([0,2,1,0]).left_pieri_factorizations().cardinality() 6 """ + if max_length is None: + from sage.rings.infinity import infinity + max_length = infinity pieri_factors = self.parent().pieri_factors() def predicate(u): return u in pieri_factors and u.length() <= max_length @@ -342,7 +343,7 @@ def predicate(u): return self.binary_factorizations(predicate) @cached_in_parent_method - def stanley_symmetric_function_as_polynomial(self, max_length = infinity): + def stanley_symmetric_function_as_polynomial(self, max_length=None): r""" Returns a multivariate generating function for the number of factorizations of a Weyl group element into Pieri @@ -404,8 +405,12 @@ def stanley_symmetric_function_as_polynomial(self, max_length = infinity): by taking right factors, and in particular Grassmanian elements. """ + if max_length is None: + from sage.rings.infinity import infinity + max_length = infinity W = self.parent() pieri_factors = W.pieri_factors() + from sage.rings.rational_field import QQ R = QQ[','.join('x%s'%l for l in range(1,pieri_factors.max_length()+1))] x = R.gens() if self.is_one(): @@ -483,6 +488,7 @@ def stanley_symmetric_function(self): - [Pon2010]_ """ import sage.combinat.sf + from sage.rings.rational_field import QQ m = sage.combinat.sf.sf.SymmetricFunctions(QQ).monomial() return m.from_polynomial_exp(self.stanley_symmetric_function_as_polynomial()) @@ -633,6 +639,7 @@ def inversion_arrangement(self, side='right'): inv = self.inversions(side=side, inversion_type='roots') from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangements I = self.parent().cartan_type().index_set() + from sage.rings.rational_field import QQ H = HyperplaneArrangements(QQ, tuple(['a{}'.format(i) for i in I])) gens = H.gens() if not inv: diff --git a/src/sage/coding/abstract_code.py b/src/sage/coding/abstract_code.py index 13080ca4a0b..6ebd37677cc 100644 --- a/src/sage/coding/abstract_code.py +++ b/src/sage/coding/abstract_code.py @@ -55,8 +55,6 @@ import inspect from sage.misc.sageinspect import sage_getargspec -from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF - def _explain_constructor(cl): r""" @@ -222,7 +220,8 @@ def __init__(self, length, default_encoder_name=None, sage: C Dummy code of length 6 - We can list its elements and check if an element is in the code: + We can list its elements and check if an element is in the code:: + sage: list(C) [(0, 0, 0, 0, 0, 0), (1, 0, 0, 0, 0, 0), @@ -949,7 +948,7 @@ def encoder(self, encoder_name=None, *args, **kwargs): ValueError: Constructing the Systematic encoder failed, possibly due to missing or incorrect parameters. The constructor requires no arguments. It takes the optional arguments ['systematic_positions']. - See the documentation of sage.coding.linear_code.LinearCodeSystematicEncoder for more details. + See the documentation of sage.coding.linear_code_no_metric.LinearCodeSystematicEncoder for more details. """ if not self._default_encoder_name: raise NotImplementedError("No encoder implemented for this code.") @@ -990,7 +989,7 @@ def encoders_available(self, classes=False): sage: dictionary = C.encoders_available(True) sage: sorted(dictionary.items()) [('GeneratorMatrix', ), - ('Systematic', )] + ('Systematic', )] """ if classes: return copy(self._registered_encoders) diff --git a/src/sage/coding/channel.py b/src/sage/coding/channel.py index d4605319d24..db63b66ff2d 100644 --- a/src/sage/coding/channel.py +++ b/src/sage/coding/channel.py @@ -44,9 +44,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# https://www.gnu.org/licenses/ -# **************************************************************************** -from six.moves import range +# http://www.gnu.org/licenses/ +#***************************************************************************** from sage.structure.sage_object import SageObject from sage.rings.integer import Integer diff --git a/src/sage/coding/codes_catalog.py b/src/sage/coding/codes_catalog.py index 87416f44f0b..6101b0268a6 100644 --- a/src/sage/coding/codes_catalog.py +++ b/src/sage/coding/codes_catalog.py @@ -21,7 +21,7 @@ :meth:`~sage.coding.hamming_code.HammingCode` @ Hamming codes :meth:`~sage.coding.golay_code.GolayCode` @ Golay codes :meth:`~sage.coding.goppa_code.GoppaCode` @ Goppa codes - + :meth:`~sage.coding.kasami_codes.KasamiCode` @ Kasami codes Families of Codes (Generator matrix representation) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -100,6 +100,9 @@ _lazy_import('sage.coding.hamming_code', 'HammingCode') _lazy_import('sage.coding.golay_code', 'GolayCode') _lazy_import('sage.coding.goppa_code', 'GoppaCode') +_lazy_import('sage.coding.kasami_codes', 'KasamiCode') +_lazy_import('sage.coding.linear_rank_metric', 'LinearRankMetricCode') +_lazy_import('sage.coding.gabidulin_code', 'GabidulinCode') _lazy_import('sage.coding.guava', ['QuasiQuadraticResidueCode', 'RandomLinearCodeGuava']) diff --git a/src/sage/coding/databases.py b/src/sage/coding/databases.py index 6f19175f4fd..e0bce13871c 100644 --- a/src/sage/coding/databases.py +++ b/src/sage/coding/databases.py @@ -2,7 +2,7 @@ r""" Access functions to online databases for coding theory """ -from six.moves import range + from sage.interfaces.all import gap from sage.features.gap import GapPackage @@ -155,7 +155,7 @@ def best_linear_code_in_codetables_dot_de(n, k, F, verbose=False): - Steven Sivek (2005-11-14) - David Joyner (2008-03) """ - from six.moves.urllib.request import urlopen + from urllib.request import urlopen from sage.cpython.string import bytes_to_str q = F.order() if not q in [2, 3, 4, 5, 7, 8, 9]: diff --git a/src/sage/coding/decoders_catalog.py b/src/sage/coding/decoders_catalog.py index 8beca59aa72..6c181877c7d 100644 --- a/src/sage/coding/decoders_catalog.py +++ b/src/sage/coding/decoders_catalog.py @@ -75,5 +75,7 @@ lazy_import('sage.coding.punctured_code', 'PuncturedCodeOriginalCodeDecoder') lazy_import('sage.coding.subfield_subcode', 'SubfieldSubcodeOriginalCodeDecoder') lazy_import('sage.coding.information_set_decoder', 'LinearCodeInformationSetDecoder') +lazy_import('sage.coding.linear_rank_metric', 'LinearRankMetricCodeNearestNeighborDecoder') +lazy_import('sage.coding.gabidulin_code', 'GabidulinGaoDecoder') del lazy_import diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 9a8f0dffbb6..0e00a5170de 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -119,8 +119,8 @@ def encode(self, word): This is a default implementation which assumes that the message space of the encoder is `F^{k}`, where `F` is - :meth:`sage.coding.linear_code.AbstractLinearCode.base_field` - and `k` is :meth:`sage.coding.linear_code.AbstractLinearCode.dimension`. + :meth:`sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric.base_field` + and `k` is :meth:`sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric.dimension`. If this is not the case, this method should be overwritten by the subclass. .. NOTE:: @@ -244,7 +244,7 @@ def unencode(self, c, nocheck=False): sage: C = LinearCode(G) Traceback (most recent call last): ... - ValueError: length must be a positive integer + ValueError: length must be a non-zero positive integer """ if not nocheck and c not in self.code(): raise EncodingError("Given word is not in the code") diff --git a/src/sage/coding/encoders_catalog.py b/src/sage/coding/encoders_catalog.py index e1bed60ae9c..6006fcdb453 100644 --- a/src/sage/coding/encoders_catalog.py +++ b/src/sage/coding/encoders_catalog.py @@ -15,7 +15,7 @@ **Generic encoders** - :class:`linear_code.LinearCodeGeneratorMatrixEncoder ` -- :class:`linear_code.LinearCodeSystematicEncoder ` +- :class:`linear_code_no_metric.LinearCodeSystematicEncoder ` **Generalized Reed-Solomon code encoders** @@ -49,10 +49,11 @@ 'CyclicCodeVectorEncoder']) _lazy_import('sage.coding.extended_code', 'ExtendedCodeExtendedMatrixEncoder') _lazy_import('sage.coding.grs_code', ['GRSEvaluationVectorEncoder', 'GRSEvaluationPolynomialEncoder']) -_lazy_import('sage.coding.linear_code', ['LinearCodeGeneratorMatrixEncoder', - 'LinearCodeSystematicEncoder']) +_lazy_import('sage.coding.linear_code', 'LinearCodeGeneratorMatrixEncoder') +_lazy_import('sage.coding.linear_code_no_metric', 'LinearCodeSystematicEncoder') _lazy_import('sage.coding.punctured_code', 'PuncturedCodePuncturedMatrixEncoder') _lazy_import('sage.coding.reed_muller_code', ['ReedMullerVectorEncoder', 'ReedMullerPolynomialEncoder']) _lazy_import('sage.coding.subfield_subcode', 'SubfieldSubcodeParityCheckEncoder') _lazy_import('sage.coding.parity_check_code', ['ParityCheckCodeGeneratorMatrixEncoder','ParityCheckCodeStraightforwardEncoder']) _lazy_import('sage.coding.goppa_code', ['GoppaCodeEncoder']) +_lazy_import('sage.coding.gabidulin_code', ['GabidulinVectorEvaluationEncoder', 'GabidulinPolynomialEvaluationEncoder']) diff --git a/src/sage/coding/gabidulin_code.py b/src/sage/coding/gabidulin_code.py new file mode 100644 index 00000000000..60854282c79 --- /dev/null +++ b/src/sage/coding/gabidulin_code.py @@ -0,0 +1,1066 @@ +r""" +Gabidulin Code + +This module provides the :class:`~sage.coding.gabidulin.GabidulinCode`, which constructs +Gabidulin Codes that are the rank metric equivalent of Reed Solomon codes and are +defined as the evaluation codes of degree-restricted skew polynomials. + +This module also provides :class:`~sage.coding.gabidulin.GabidulinPolynomialEvaluationEncoder`, +an encoder with a skew polynomial message space and :class:`~sage.coding.gabidulin.GabidulinVectorEvaluationEncoder`, +an encoder based on the generator matrix. It also provides a decoder +:class:`~sage.coding.gabidulin.GabidulinGaoDecoder` which corrects errors using +the Gao algorithm in the rank metric. + +AUTHOR: + +- Arpit Merchant (2016-08-16) +- Marketa Slukova (2019-08-19): initial version +""" + +from sage.matrix.constructor import matrix +from sage.modules.free_module_element import vector +from sage.modules.free_module import VectorSpace +from sage.rings.integer import Integer +from sage.coding.encoder import Encoder +from sage.coding.decoder import Decoder, DecodingError +from sage.rings.integer_ring import ZZ +from sage.functions.other import floor +from sage.coding.linear_rank_metric import * + +class GabidulinCode(AbstractLinearRankMetricCode): + """ + A Gabidulin Code. + + DEFINITION: + + A linear Gabidulin code Gab[n, k] over `F_{q^m}` of length `n` (at most + `m`) and dimension `k` (at most `n`) is the set of all codewords, that + are the evaluation of a `q`-degree restricted skew polynomial `f(x)` + belonging to the skew polynomial constructed over the base ring `F_{q^m}` + and the twisting homomorphism `\sigma`. + + .. math:: + + \{ \text{Gab[n, k]} = \big\{ (f(g_0) f(g_1) ... f(g_{n-1})) = f(\textbf{g}) : \text{deg}_{q}f(x) < k \big\} \} + + where the fixed evaluation points `g_0, g_1,..., g_{n-1}` are linearly + independent over `F_{q^m}`. + + EXAMPLES: + + A Gabidulin Code can be constructed in the following way: + + sage: Fqm = GF(16) + sage: Fq = GF(4) + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: C + [2, 2, 1] linear Gabidulin code over GF(16)/GF(4) + """ + _registered_encoders = {} + _registered_decoders = {} + + def __init__(self, base_field, length, dimension, sub_field=None, + twisting_homomorphism=None, evaluation_points=None): + r""" + Representation of a Gabidulin Code. + + INPUT: + + - ``base_field`` -- finite field of order `q^m` where `q` is a prime power + and `m` is an integer + + - ``length`` -- length of the resulting code + + - ``dimension`` -- dimension of the resulting code + + - ``sub_field`` -- (default: ``None``) finite field of order `q` + which is a subfield of the ``base_field``. If not given, it is the + prime subfield of the ``base_field``. + + - ``twisting_homomorphism`` -- (default: ``None``) homomorphism of the + underlying skew polynomial ring. If not given, it is the Frobenius + endomorphism on ``base_field``, which sends an element `x` to `x^{q}`. + + - ``evaluation_points`` -- (default: ``None``) list of elements + `g_0, g_1,...,g_{n-1}` of the ``base_field`` that are linearly + independent over the ``sub_field``. These elements form the first row + of the generator matrix. If not specified, these are the `nth` powers + of the generator of the ``base_field``. + + Both parameters ``sub_field`` and ``twisting_homomorphism`` are optional. + Since they are closely related, here is a complete list of behaviours: + + - both ``sub_field`` and ``twisting_homomorphism`` given -- in this case + we only check that given that ``twisting_homomorphism`` has a fixed + field method, it returns ``sub_field`` + + - only ``twisting_homomorphism`` given -- we set ``sub_field`` to be the + fixed field of the ``twisting_homomorphism``. If such method does not + exist, an error is raised. + + - only ``sub_field`` given -- we set ``twisting_homomorphism`` to be the + Frobenius of the field extension + + - neither ``sub_field`` or ``twisting_homomorphism`` given -- we take + ``sub_field`` to be the prime field of ``base_field`` and the + ``twisting_homomorphism`` to be the Frobenius wrt. the prime field + + TESTS: + + If ``length`` is bigger than the degree of the extension, an error is + raised:: + + sage: C = codes.GabidulinCode(GF(64), 4, 3, GF(4)) + Traceback (most recent call last): + ... + ValueError: 'length' can be at most the degree of the extension, 3 + + If the number of evaluation points is not equal to the length + of the code, an error is raised: + + sage: Fqm = GF(5^20) + sage: Fq = GF(5) + sage: aa = Fqm.gen() + sage: evals = [ aa^i for i in range(21) ] + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq, None, evals) + Traceback (most recent call last): + ... + ValueError: the number of evaluation points should be equal to the length of the code + + If evaluation points are not linearly independent over the ``base_field``, + an error is raised: + + sage: evals = [ aa*i for i in range(2) ] + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq, None, evals) + Traceback (most recent call last): + ... + ValueError: the evaluation points provided are not linearly independent + + If an evaluation point does not belong to the ``base_field``, an error + is raised: + + sage: a = GF(3).gen() + sage: evals = [ a*i for i in range(2) ] + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq, None, evals) + Traceback (most recent call last): + ... + ValueError: evaluation point does not belong to the 'base field' + + Given that both ``sub_field`` and ``twisting_homomorphism`` are specified + and ``twisting_homomorphism`` has a fixed field method. If the fixed + field of ``twisting_homomorphism`` is not ``sub_field``, an error is + raised: + + sage: Fqm = GF(64) + sage: Fq = GF(8) + sage: twist = GF(64).frobenius_endomorphism(n=2) + sage: C = codes.GabidulinCode(Fqm, 3, 2, Fq, twist) + Traceback (most recent call last): + ... + ValueError: the fixed field of the twisting homomorphism has to be the relative field of the extension + + If ``twisting_homomorphism`` is given, but ``sub_field`` is not. In case + ``twisting_homomorphism`` does not have a fixed field method, and error + is raised: + + sage: Fqm. = GF(64) + sage: sigma = Hom(Fqm, Fqm)[1]; sigma + Ring endomorphism of Finite Field in z6 of size 2^6 + Defn: z6 |--> z6^2 + sage: C = codes.GabidulinCode(Fqm, 3, 2, None, sigma) + Traceback (most recent call last): + ... + ValueError: if 'sub_field' is not given, the twisting homomorphism has to have a 'fixed_field' method + """ + twist_fix_field = None + have_twist = (twisting_homomorphism != None) + have_subfield = (sub_field != None) + + if have_twist and have_subfield: + try: + twist_fix_field = twisting_homomorphism.fixed_field()[0] + except AttributeError: + pass + if twist_fix_field and twist_fix_field.order() != sub_field.order(): + raise ValueError("the fixed field of the twisting homomorphism has to be the relative field of the extension") + + if have_twist and not have_subfield: + if not twist_fix_field: + raise ValueError("if 'sub_field' is not given, the twisting homomorphism has to have a 'fixed_field' method") + else: + sub_field = twist_fix_field + + if (not have_twist) and have_subfield: + twisting_homomorphism = base_field.frobenius_endomorphism(n=sub_field.degree()) + + if (not have_twist) and not have_subfield: + sub_field = base_field.base_ring() + twisting_homomorphism = base_field.frobenius_endomorphism() + + self._twisting_homomorphism = twisting_homomorphism + + super(GabidulinCode, self).__init__(base_field, sub_field, length, "VectorEvaluation", "Gao") + + if length > self.extension_degree(): + raise ValueError("'length' can be at most the degree of the extension, {}".format(self.extension_degree())) + if evaluation_points is None: + evaluation_points = [base_field.gen()**i for i in range(base_field.degree())][:length] + else: + if not len(evaluation_points) == length: + raise ValueError("the number of evaluation points should be equal to the length of the code") + for i in range(length): + if not evaluation_points[i] in base_field: + raise ValueError("evaluation point does not belong to the 'base field'") + basis = self.matrix_form_of_vector(vector(evaluation_points)) + if basis.rank() != length: + raise ValueError("the evaluation points provided are not linearly independent") + self._evaluation_points = evaluation_points + self._dimension = dimension + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: Fqm = GF(16) + sage: Fq = GF(4) + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq); C + [2, 2, 1] linear Gabidulin code over GF(16)/GF(4) + """ + R = self.base_field() + S = self.sub_field() + if R and S in Fields(): + return "[%s, %s, %s] linear Gabidulin code over GF(%s)/GF(%s)"%(self.length(), self.dimension(), self.minimum_distance(), R.cardinality(), S.cardinality()) + else: + return "[%s, %s, %s] linear Gabidulin code over %s/%s"%(self.length(), self.dimension(), self.minimum_distance(), R, S) + + def _latex_(self): + """ + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: Fqm = GF(16) + sage: Fq = GF(4) + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq); + sage: latex(C) + [2, 2, 1] \textnormal{ linear Gabidulin code over } \Bold{F}_{2^{4}}/\Bold{F}_{2^{2}} + """ + return "[%s, %s, %s] \\textnormal{ linear Gabidulin code over } %s/%s"\ + % (self.length(), self.dimension() ,self.minimum_distance(), + self.base_field()._latex_(), self.sub_field()._latex_()) + + def __eq__(self, other): + """ + Tests equality between Gabidulin Code objects. + + INPUT: + + - ``other`` -- another Gabidulin Code object + + OUTPUT: + + - ``True`` or ``False`` + + EXAMPLES:: + + sage: Fqm = GF(16) + sage: Fq = GF(4) + sage: C1 = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: C2 = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: C1.__eq__(C2) + True + + sage: Fqmm = GF(64) + sage: C3 = codes.GabidulinCode(Fqmm, 2, 2, Fq) + sage: C3.__eq__(C2) + False + """ + return isinstance(other, GabidulinCode) \ + and self.base_field() == other.base_field() \ + and self.sub_field() == other.sub_field() \ + and self.length() == other.length() \ + and self.dimension() == other.dimension() \ + and self.evaluation_points() == other.evaluation_points() + + def twisting_homomorphism(self): + r""" + Return the twisting homomorphism of ``self``. + + EXAMPLES:: + + sage: Fqm = GF(5^20) + sage: Fq = GF(5^4) + sage: C = codes.GabidulinCode(Fqm, 5, 3, Fq) + sage: C.twisting_homomorphism() + Frobenius endomorphism z20 |--> z20^(5^4) on Finite Field in z20 of size 5^20 + """ + return self._twisting_homomorphism + + def minimum_distance(self): + r""" + Return the minimum distance of ``self``. + + Since Gabidulin Codes are Maximum-Distance-Separable (MDS), this returns + ``self.length() - self.dimension() + 1``. + + EXAMPLES:: + + sage: Fqm = GF(5^20) + sage: Fq = GF(5) + sage: C = codes.GabidulinCode(Fqm, 20, 15, Fq) + sage: C.minimum_distance() + 6 + """ + return self.length() - self.dimension() + 1 + + def parity_evaluation_points(self): + r""" + Returns the parity evalution points of ``self``. + + These form the first row of the parity check matrix of ``self``. + + EXAMPLES:: + + sage: C = codes.GabidulinCode(GF(2^10), 5, 2) + sage: list(C.parity_check_matrix().row(0)) == C.parity_evaluation_points() #indirect_doctest + True + """ + eval_pts = self.evaluation_points() + n = self.length() + k = self.dimension() + q = self.sub_field().order() + sigma = self.twisting_homomorphism() + + coefficient_matrix = matrix(self.base_field(), n - 1, n, + lambda i,j: (sigma**(-n + k + 1 + i))(eval_pts[j])) + solution_space = coefficient_matrix.right_kernel() + return list(solution_space.basis()[0]) + + def dual_code(self): + r""" + Returns the dual code `C^{\perp}` of ``self``, the code `C`, + + .. MATH:: + + C^{\perp} = \{ v \in V\ |\ v\cdot c = 0,\ \forall c \in C \}. + + EXAMPLES:: + + sage: C = codes.GabidulinCode(GF(2^10), 5, 2) + sage: C1 = C.dual_code(); C1 + [5, 3, 3] linear Gabidulin code over GF(1024)/GF(2) + sage: C == C1.dual_code() + True + """ + return GabidulinCode(self.base_field(), self.length(), + self.length() - self.dimension(), self.sub_field(), + self.twisting_homomorphism(), + self.parity_evaluation_points()) + + def parity_check_matrix(self): + r""" + Returns the parity check matrix of ``self``. + + This is the generator matrix of the dual code of ``self``. + + EXAMPLES:: + + sage: C = codes.GabidulinCode(GF(2^3), 3, 2) + sage: C.parity_check_matrix() + [ 1 z3 z3^2 + z3] + sage: C.parity_check_matrix() == C.dual_code().generator_matrix() + True + """ + return self.dual_code().generator_matrix() + + def evaluation_points(self): + """ + Return the evaluation points of ``self``. + + EXAMPLES:: + + sage: Fqm = GF(5^20) + sage: Fq = GF(5^4) + sage: C = codes.GabidulinCode(Fqm, 4, 4, Fq) + sage: C.evaluation_points() + [1, z20, z20^2, z20^3] + """ + return self._evaluation_points + +####################### encoders ############################### + +class GabidulinVectorEvaluationEncoder(Encoder): + + def __init__(self, code): + """ + This method constructs the vector evaluation encoder for + Gabidulin Codes. + + INPUT: + + - ``code`` -- the associated code of this encoder. + + EXAMPLES:: + + sage: Fqm = GF(16) + sage: Fq = GF(4) + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: E = codes.encoders.GabidulinVectorEvaluationEncoder(C) + sage: E + Vector evaluation style encoder for [2, 2, 1] linear Gabidulin code over GF(16)/GF(4) + + Alternatively, we can construct the encoder from ``C`` directly:: + + sage: E = C.encoder("VectorEvaluation") + sage: E + Vector evaluation style encoder for [2, 2, 1] linear Gabidulin code over GF(16)/GF(4) + + TESTS: + + If the code is not a Gabidulin code, an error is raised: + + sage: C = codes.HammingCode(GF(4), 2) + sage: E = codes.encoders.GabidulinVectorEvaluationEncoder(C) + Traceback (most recent call last): + ... + ValueError: code has to be a Gabidulin code + """ + if not isinstance(code, GabidulinCode): + raise ValueError("code has to be a Gabidulin code") + super(GabidulinVectorEvaluationEncoder, self).__init__(code) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES: + + sage: Fqm = GF(5^20) + sage: Fq = GF(5^4) + sage: C = codes.GabidulinCode(Fqm, 4, 4, Fq) + sage: E = codes.encoders.GabidulinVectorEvaluationEncoder(C); E + Vector evaluation style encoder for [4, 4, 1] linear Gabidulin code over GF(95367431640625)/GF(625) + """ + return "Vector evaluation style encoder for %s" % self.code() + + def _latex_(self): + """ + Return a latex representation of ``self``. + + EXAMPLES: + + sage: Fqm = GF(5^20) + sage: Fq = GF(5^4) + sage: C = codes.GabidulinCode(Fqm, 4, 4, Fq) + sage: E = codes.encoders.GabidulinVectorEvaluationEncoder(C) + sage: latex(E) + \textnormal{Vector evaluation style encoder for } [4, 4, 1] \textnormal{ linear Gabidulin code over } \Bold{F}_{5^{20}}/\Bold{F}_{5^{4}} + """ + return "\\textnormal{Vector evaluation style encoder for } %s" % self.code()._latex_() + + def __eq__(self, other): + """ + Tests equality between Gabidulin Generator Matrix Encoder objects. + + INPUT: + + - ``other`` -- another Gabidulin Generator Matrix Encoder + + OUTPUT: + + - ``True`` or ``False`` + + EXAMPLES:: + + sage: Fqm = GF(16) + sage: Fq = GF(4) + sage: C1 = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: E1 = codes.encoders.GabidulinVectorEvaluationEncoder(C1) + sage: C2 = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: E2 = codes.encoders.GabidulinVectorEvaluationEncoder(C2) + sage: E1.__eq__(E2) + True + + sage: Fqmm = GF(64) + sage: C3 = codes.GabidulinCode(Fqmm, 2, 2, Fq) + sage: E3 = codes.encoders.GabidulinVectorEvaluationEncoder(C3) + sage: E3.__eq__(E2) + False + """ + return isinstance(other, GabidulinVectorEvaluationEncoder) \ + and self.code() == other.code() + + def generator_matrix(self): + """ + Return the generator matrix of ``self``. + + EXAMPLES:: + + sage: Fqm = GF(2^9) + sage: Fq = GF(2^3) + sage: C = codes.GabidulinCode(Fqm, 3, 3, Fq) + sage: list(C.generator_matrix().row(1)) == [C.evaluation_points()[i]**(2**3) for i in range(3)] + True + """ + from functools import reduce + C = self.code() + eval_pts = C.evaluation_points() + k = C.dimension() + sigma = C.twisting_homomorphism() + create_matrix_elements = lambda A,k,f: reduce(lambda L,x: [x] + \ + list(map(lambda l: list(map(f,l)), L)), [A]*k, []) + return matrix(C.base_field(), C.dimension(), C.length(), \ + create_matrix_elements(eval_pts, C.dimension(), sigma)) + +class GabidulinPolynomialEvaluationEncoder(Encoder): + r""" + Encoder for Gabidulin codes which uses evaluation of skew polynomials to + obtain codewords. + + Let `C` be a Gabidulin code of length `n` and dimension `k` over some + finite field `F = GF(q^m)`. We denote by `\alpha_i` its evaluations + points, where `1 \leq i \leq n`. Let `p`, a skew polynomial of degree at + most `k-1` in `F[x]`, be the message. + + The encoding of `m` will be the following codeword: + + .. MATH:: + + (p(\alpha_1), \dots, p(\alpha_n)). + + TESTS:: + + This module uses the following experimental feature: + This test block is here only to trigger the experimental warning so it does not + interferes with doctests:: + + sage: Fqm = GF(2^9) + sage: Fq = GF(2^3) + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: S. = Fqm['x', C.twisting_homomorphism()] + sage: z9 = Fqm.gen() + sage: p = (z9^6 + z9^2 + z9 + 1)*x + z9^7 + z9^5 + z9^4 + z9^2 + sage: vector(p.multi_point_evaluation(C.evaluation_points())) + doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. + See http://trac.sagemath.org/13215 for details. + (z9^7 + z9^6 + z9^5 + z9^4 + z9 + 1, z9^6 + z9^5 + z9^3 + z9) + + EXAMPLES:: + + sage: Fqm = GF(16) + sage: Fq = GF(4) + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C) + sage: E + Polynomial evaluation style encoder for [2, 2, 1] linear Gabidulin code over GF(16)/GF(4) + + Alternatively, we can construct the encoder from ``C`` directly:: + + sage: E = C.encoder("PolynomialEvaluation") + sage: E + Polynomial evaluation style encoder for [2, 2, 1] linear Gabidulin code over GF(16)/GF(4) + """ + + def __init__(self, code): + r""" + INPUT: + + - ``code`` -- the associated code of this encoder + + TESTS: + + If the code is not a Gabidulin code, an error is raised: + + sage: C = codes.HammingCode(GF(4), 2) + sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C) + Traceback (most recent call last): + ... + ValueError: code has to be a Gabidulin code + """ + if not isinstance(code, GabidulinCode): + raise ValueError("code has to be a Gabidulin code") + super(GabidulinPolynomialEvaluationEncoder, self).__init__(code) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES: + + sage: Fqm = GF(5^20) + sage: Fq = GF(5^4) + sage: C = codes.GabidulinCode(Fqm, 4, 4, Fq) + sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C); E + Polynomial evaluation style encoder for [4, 4, 1] linear Gabidulin code over GF(95367431640625)/GF(625) + """ + return "Polynomial evaluation style encoder for %s" % self.code() + + def _latex_(self): + """ + Return a latex representation of ``self``. + + EXAMPLES: + + sage: Fqm = GF(5^20) + sage: Fq = GF(5^4) + sage: C = codes.GabidulinCode(Fqm, 4, 4, Fq) + sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C) + sage: latex(E) + \textnormal{Polynomial evaluation style encoder for } [4, 4, 1] \textnormal{ linear Gabidulin code over } \Bold{F}_{5^{20}}/\Bold{F}_{5^{4}} + """ + return "\\textnormal{Polynomial evaluation style encoder for } %s" % self.code()._latex_() + + def __eq__(self, other): + """ + Test equality between Gabidulin Polynomial Evaluation Encoder objects. + + INPUT: + + - ``other`` -- another Gabidulin Polynomial Evaluation Encoder + + OUTPUT: + + - ``True`` or ``False`` + + EXAMPLES:: + + sage: Fqm = GF(16) + sage: Fq = GF(4) + sage: C1 = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: E1 = codes.encoders.GabidulinPolynomialEvaluationEncoder(C1) + sage: C2 = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: E2 = codes.encoders.GabidulinPolynomialEvaluationEncoder(C2) + sage: E1.__eq__(E2) + True + + sage: Fqmm = GF(64) + sage: C3 = codes.GabidulinCode(Fqmm, 2, 2, Fq) + sage: E3 = codes.encoders.GabidulinPolynomialEvaluationEncoder(C3) + sage: E3.__eq__(E2) + False + """ + return isinstance(other, GabidulinPolynomialEvaluationEncoder) \ + and self.code() == other.code() + + def message_space(self): + r""" + Return the message space of the associated code of ``self``. + + EXAMPLES: + + sage: Fqm = GF(5^20) + sage: Fq = GF(5^4) + sage: C = codes.GabidulinCode(Fqm, 4, 4, Fq) + sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C) + sage: E.message_space() + Ore Polynomial Ring in x over Finite Field in z20 of size 5^20 twisted by z20 |--> z20^(5^4) + """ + C = self.code() + return C.base_field()['x', C.twisting_homomorphism()] + + def encode(self, p, form="vector"): + """ + Transform the polynomial ``p`` into a codeword of :meth:`code`. + + The output codeword can be represented as a vector or a matrix, + depending on the ``form`` input. + + INPUT: + + - ``p`` -- a skew polynomial from the message space of ``self`` of degree + less than ``self.code().dimension()`` + + - ``form`` -- type parameter taking strings "vector" or "matrix" + as values and converting the output codeword into the respective form + (default: "vector") + + OUTPUT: + + - a codeword corresponding to `p` in vector or matrix form + + EXAMPLES: + + sage: Fqm = GF(2^9) + sage: Fq = GF(2^3) + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C) + sage: S. = Fqm['x', C.twisting_homomorphism()] + sage: z9 = Fqm.gen() + sage: p = (z9^6 + z9^2 + z9 + 1)*x + z9^7 + z9^5 + z9^4 + z9^2 + sage: codeword_vector = E.encode(p, "vector"); codeword_vector + (z9^7 + z9^6 + z9^5 + z9^4 + z9 + 1, z9^6 + z9^5 + z9^3 + z9) + sage: codeword_matrix = E.encode(p, "matrix"); codeword_matrix + [ z3 z3^2 + z3] + [ z3 1] + [ z3^2 z3^2 + z3 + 1] + + TESTS: + + If the skew polynomial, `p`, has degree greater than or equal to the + dimension of the code, an error is raised:: + + sage: t = z9^4*x^2 + z9 + sage: codeword_vector = E.encode(t, "vector"); codeword_vector + Traceback (most recent call last): + ... + ValueError: the skew polynomial to encode must have degree at most 1 + + The skew polynomial, `p`, must belong to the message space of the code. + Otherwise, an error is raised:: + + sage: Fqmm = GF(2^12) + sage: S. = Fqmm['x', Fqmm.frobenius_endomorphism(n=3)] + sage: q = S.random_element(degree=2) + sage: codeword_vector = E.encode(q, "vector"); codeword_vector + Traceback (most recent call last): + ... + ValueError: the message to encode must be in Ore Polynomial Ring in x over Finite Field in z9 of size 2^9 twisted by z9 |--> z9^(2^3) + """ + C = self.code() + M = self.message_space() + if p not in M: + raise ValueError("the message to encode must be in %s" % M) + if p.degree() >= C.dimension(): + raise ValueError("the skew polynomial to encode must have degree at most %s" % (C.dimension() - 1)) + eval_pts = C.evaluation_points() + codeword = p.multi_point_evaluation(eval_pts) + if form == "vector": + return vector(codeword) + elif form == "matrix": + return C.matrix_form_of_vector(vector(codeword)) + else: + return ValueError("the argument 'form' takes only either 'vector' or 'matrix' as valid input") + + def unencode_nocheck(self, c): + """ + Returns the message corresponding to the codeword ``c``. + + Use this method with caution: it does not check if ``c`` + belongs to the code, and if this is not the case, the output is + unspecified. + + INPUT: + + - ``c`` -- a codeword of :meth:`code` + + OUTPUT: + + - a skew polynomial of degree less than ``self.code().dimension()`` + + EXAMPLES: + + sage: Fqm = GF(2^9) + sage: Fq = GF(2^3) + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C) + sage: S. = Fqm['x', C.twisting_homomorphism()] + sage: z9 = Fqm.gen() + sage: p = (z9^6 + z9^4)*x + z9^2 + z9 + sage: codeword_vector = E.encode(p, "vector") + sage: E.unencode_nocheck(codeword_vector) + (z9^6 + z9^4)*x + z9^2 + z9 + """ + C = self.code() + eval_pts = C.evaluation_points() + values = [c[i] for i in range(len(c))] + points = [(eval_pts[i], values[i]) for i in range(len(eval_pts))] + p = self.message_space().lagrange_polynomial(points) + return p + + +####################### decoders ############################### + + +class GabidulinGaoDecoder(Decoder): + + def __init__(self, code): + r""" + Gao style decoder for Gabidulin Codes. + + INPUT: + + - ``code`` -- the associated code of this decoder + + EXAMPLES:: + + sage: Fqm = GF(16) + sage: Fq = GF(4) + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: D = codes.decoders.GabidulinGaoDecoder(C) + sage: D + Gao decoder for [2, 2, 1] linear Gabidulin code over GF(16)/GF(4) + + Alternatively, we can construct the encoder from ``C`` directly:: + + sage: D = C.decoder("Gao") + sage: D + Gao decoder for [2, 2, 1] linear Gabidulin code over GF(16)/GF(4) + + TESTS: + + If the code is not a Gabidulin code, an error is raised: + + sage: C = codes.HammingCode(GF(4), 2) + sage: D = codes.decoders.GabidulinGaoDecoder(C) + Traceback (most recent call last): + ... + ValueError: code has to be a Gabidulin code + """ + if not isinstance(code, GabidulinCode): + raise ValueError("code has to be a Gabidulin code") + super(GabidulinGaoDecoder, self).__init__(code, code.ambient_space(), "PolynomialEvaluation") + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES: + + sage: Fqm = GF(5^20) + sage: Fq = GF(5^4) + sage: C = codes.GabidulinCode(Fqm, 4, 4, Fq) + sage: D = codes.decoders.GabidulinGaoDecoder(C); D + Gao decoder for [4, 4, 1] linear Gabidulin code over GF(95367431640625)/GF(625) + """ + return "Gao decoder for %s" % self.code() + + def _latex_(self): + """ + Return a latex representation of ``self``. + + EXAMPLES: + + sage: Fqm = GF(5^20) + sage: Fq = GF(5^4) + sage: C = codes.GabidulinCode(Fqm, 4, 4, Fq) + sage: D = codes.decoders.GabidulinGaoDecoder(C) + sage: latex(D) + \textnormal{Gao decoder for } [4, 4, 1] \textnormal{ linear Gabidulin code over } \Bold{F}_{5^{20}}/\Bold{F}_{5^{4}} + """ + return "\\textnormal{Gao decoder for } %s" % self.code()._latex_() + + def __eq__(self, other): + """ + Tests equality between Gabidulin Gao Decoder objects. + + INPUT: + + - ``other`` -- another Gabidulin Gao Decoder + + OUTPUT: + + - ``True`` or ``False`` + + EXAMPLES:: + + sage: Fqm = GF(16) + sage: Fq = GF(4) + sage: C1 = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: D1 = codes.decoders.GabidulinGaoDecoder(C1) + sage: C2 = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: D2 = codes.decoders.GabidulinGaoDecoder(C2) + sage: D1.__eq__(D2) + True + + sage: Fqmm = GF(64) + sage: C3 = codes.GabidulinCode(Fqmm, 2, 2, Fq) + sage: D3 = codes.decoders.GabidulinGaoDecoder(C3) + sage: D3.__eq__(D2) + False + """ + return isinstance(other, GabidulinGaoDecoder) \ + and self.code() == other.code() + + def _partial_xgcd(self, a, b, d_stop): + """ + Compute the partial gcd of `a` and `b` using the right linearized + extended Euclidean algorithm up to the `d_stop` iterations. This + is a private method for internal use only. + + INPUT: + + - ``a`` -- a skew polynomial + + - ``b`` -- another skew polynomial + + - ``d_stop`` -- the number of iterations for which the algorithm + is to be run + + OUTPUT: + + - ``r_c`` -- right linearized remainder of `a` and `b` + + - ``u_c`` -- right linearized quotient of `a` and `b` + + EXAMPLES: + + sage: Fqm = GF(2^9) + sage: Fq = GF(2^3) + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: D = codes.decoders.GabidulinGaoDecoder(C) + sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C) + sage: S. = Fqm['x', C.twisting_homomorphism()] + sage: z9 = Fqm.gen() + sage: p = (z9^6 + z9^4)*x + z9^2 + z9 + sage: codeword_vector = E.encode(p, "vector") + sage: r = D.decode_to_message(codeword_vector) #indirect_doctest + sage: r + (z9^6 + z9^4)*x + z9^2 + z9 + """ + S = self.message_space() + if (a not in S) or (b not in S): + raise ValueError("both the input polynomials must belong to %s" % S) + if a.degree() < b.degree(): + raise ValueError("degree of first polynomial must be greater than or equal to degree of second polynomial") + r_p = a + r_c = b + u_p = S.zero() + u_c = S.one() + v_p = u_c + v_c = u_p + + while r_c.degree() >= d_stop: + (q, r_c), r_p = r_p.right_quo_rem(r_c), r_c + u_c, u_p = u_p - q*u_c, u_c + v_c, v_p = v_p - q*v_c, v_c + return r_c, u_c + + def _decode_to_code_and_message(self, r): + """ + Return the decoded codeword and message (skew polynomial) + corresponding to the received codeword `r`. This is a + private method for internal use only. + + INPUT: + + - ``r`` -- received codeword + + OUTPUT: + + - the decoded codeword and decoded message corresponding to + the received codeword `r` + + EXAMPLES: + + sage: Fqm = GF(2^9) + sage: Fq = GF(2^3) + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: D = codes.decoders.GabidulinGaoDecoder(C) + sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C) + sage: S. = Fqm['x', C.twisting_homomorphism()] + sage: z9 = Fqm.gen() + sage: p = (z9^6 + z9^4)*x + z9^2 + z9 + sage: codeword_vector = E.encode(p, "vector") + sage: r = D.decode_to_message(codeword_vector) #indirect doctest + sage: r + (z9^6 + z9^4)*x + z9^2 + z9 + """ + C = self.code() + length = len(r) + eval_pts = C.evaluation_points() + S = self.message_space() + + if length == C.dimension() or r in C: + return r, self.connected_encoder().unencode_nocheck(r) + + points = [(eval_pts[i], r[i]) for i in range(len(eval_pts))] + #R = S.lagrange_polynomial(eval_pts, list(r)) + R = S.lagrange_polynomial(points) + r_out, u_out = self._partial_xgcd(S.minimal_vanishing_polynomial(eval_pts), + R, floor((C.length() + C.dimension())//2)) + quo, rem = r_out.left_quo_rem(u_out) + if not rem.is_zero(): + raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") + if quo not in S: + raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") + c = self.connected_encoder().encode(quo) + if C.rank_weight_of_vector(c-r) > self.decoding_radius(): + raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") + return c, quo + + def decode_to_code(self, r): + """ + Return the decoded codeword corresponding to the + received word `r`. + + INPUT: + + - ``r`` -- received codeword + + OUTPUT: + + - the decoded codeword corresponding to the received codeword + + EXAMPLES: + + sage: Fqm = GF(3^20) + sage: Fq = GF(3) + sage: C = codes.GabidulinCode(Fqm, 5, 3, Fq) + sage: D = codes.decoders.GabidulinGaoDecoder(C) + sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C) + sage: S. = Fqm['x', C.twisting_homomorphism()] + sage: z20 = Fqm.gen() + sage: p = x + sage: codeword_vector = E.encode(p, "vector") + sage: codeword_vector + (1, z20^3, z20^6, z20^9, z20^12) + sage: l = list(codeword_vector) + sage: l[0] = l[1] #make an error + sage: D.decode_to_code(vector(l)) + (1, z20^3, z20^6, z20^9, z20^12) + """ + return self._decode_to_code_and_message(r)[0] + + def decode_to_message(self, r): + """ + Return the skew polynomial (message) corresponding to the + received word `r`. + + INPUT: + + - ``r`` -- received codeword + + OUTPUT: + + - the message corresponding to the received codeword + + EXAMPLES: + + sage: Fqm = GF(2^9) + sage: Fq = GF(2^3) + sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq) + sage: D = codes.decoders.GabidulinGaoDecoder(C) + sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C) + sage: S. = Fqm['x', C.twisting_homomorphism()] + sage: z9 = Fqm.gen() + sage: p = (z9^6 + z9^4)*x + z9^2 + z9 + sage: codeword_vector = E.encode(p, "vector") + sage: r = D.decode_to_message(codeword_vector) + sage: r + (z9^6 + z9^4)*x + z9^2 + z9 + """ + return self._decode_to_code_and_message(r)[1] + + def decoding_radius(self): + """ + Return the decoding radius of the Gabidulin Gao Decoder. + + EXAMPLES: + + sage: Fqm = GF(5^20) + sage: Fq = GF(5) + sage: C = codes.GabidulinCode(Fqm, 20, 4, Fq) + sage: D = codes.decoders.GabidulinGaoDecoder(C) + sage: D.decoding_radius() + 8 + """ + return (self.code().minimum_distance()-1)//2 + +############################## registration #################################### + +GabidulinCode._registered_encoders["PolynomialEvaluation"] = GabidulinPolynomialEvaluationEncoder +GabidulinCode._registered_encoders["VectorEvaluation"] = GabidulinVectorEvaluationEncoder + +GabidulinCode._registered_decoders["Gao"] = GabidulinGaoDecoder diff --git a/src/sage/coding/golay_code.py b/src/sage/coding/golay_code.py index d88f4cfa601..b4b6d64f847 100644 --- a/src/sage/coding/golay_code.py +++ b/src/sage/coding/golay_code.py @@ -160,7 +160,7 @@ def dual_code(self): If ``self`` is an extended Golay code, ``self`` is returned. Otherwise, it returns the output of - :meth:`sage.coding.linear_code.AbstractLinearCode.dual_code` + :meth:`sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric.dual_code` EXAMPLES:: diff --git a/src/sage/coding/goppa_code.py b/src/sage/coding/goppa_code.py index 091312c52fa..152877c062e 100644 --- a/src/sage/coding/goppa_code.py +++ b/src/sage/coding/goppa_code.py @@ -61,6 +61,7 @@ def _columnize(element): v = vector(element) return v.column() + class GoppaCode(AbstractLinearCode): r""" Implementation of Goppa codes. @@ -73,7 +74,7 @@ class GoppaCode(AbstractLinearCode): In binary cases, the minimum distance is `2t + 1`, where `t` is the degree of `g`. - INPUTS: + INPUT: - ``generating_pol`` -- a monic polynomial with coefficients in a finite field `\GF{p^m}`, the code is defined over `\GF{p}`, `p` must be a prime number diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index a8d9dc504df..0152d53721b 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -48,7 +48,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import -from six.moves import range from sage.categories.cartesian_product import cartesian_product @@ -118,6 +117,28 @@ class GeneralizedReedSolomonCode(AbstractLinearCode): sage: C = codes.GeneralizedReedSolomonCode(F.list()[:n], k, colmults) sage: C [40, 12, 29] Generalized Reed-Solomon Code over GF(59) + + SageMath implements efficient decoding algorithms for GRS codes:: + + sage: F = GF(11) + sage: n, k = 10, 5 + sage: C = codes.GeneralizedReedSolomonCode(F.list()[1:n+1], k) + sage: r = vector(F, (8, 2, 6, 10, 6, 10, 7, 6, 7, 2)) + sage: C.decode_to_message(r) + (3, 6, 6, 3, 1) + + TESTS: + + Test that the bug in :trac:`30045` is fixed:: + + sage: F = GF(5) + sage: C = codes.GeneralizedReedSolomonCode(F.list()[:5], 2) + sage: D = codes.decoders.GRSErrorErasureDecoder(C) + sage: y = (vector(F, [3, 0, 3, 0, 3]), vector(GF(2),[0, 1, 0, 1, 0])) + sage: D.decode_to_code(y) + (3, 3, 3, 3, 3) + sage: D.decode_to_message(y) + (3, 0) """ _registered_encoders = {} _registered_decoders = {} @@ -550,37 +571,6 @@ def _punctured_form(self, points): dimension = G.rank() return GeneralizedReedSolomonCode(punctured_alphas, dimension, punctured_col_mults) - def decode_to_message(self, r): - r""" - Decode ``r`` to an element in message space of ``self``. - - .. NOTE:: - - If the code associated to ``self`` has the same length as its - dimension, ``r`` will be unencoded as is. In that case, - if ``r`` is not a codeword, the output is unspecified. - - INPUT: - - - ``r`` -- a codeword of ``self`` - - OUTPUT: - - - a vector of ``self`` message space - - EXAMPLES:: - - sage: F = GF(11) - sage: n, k = 10, 5 - sage: C = codes.GeneralizedReedSolomonCode(F.list()[1:n+1], k) - sage: r = vector(F, (8, 2, 6, 10, 6, 10, 7, 6, 7, 2)) - sage: C.decode_to_message(r) - (3, 6, 6, 3, 1) - """ - if self.length() == self.dimension(): - return self.encoder().unencode_nocheck(r) - return vector(self.decoder().decode_to_message(r)) - def ReedSolomonCode(base_field, length, dimension, primitive_root=None): r""" diff --git a/src/sage/coding/hamming_code.py b/src/sage/coding/hamming_code.py index 25c3c55a9de..fa71d85cb94 100644 --- a/src/sage/coding/hamming_code.py +++ b/src/sage/coding/hamming_code.py @@ -58,6 +58,7 @@ def __init__(self, base_field, order): ValueError: base_field has to be a finite field If ``order`` is not a Sage Integer or a Python int, an exception is raised:: + sage: codes.HammingCode(GF(3), 3.14) Traceback (most recent call last): ... diff --git a/src/sage/coding/information_set_decoder.py b/src/sage/coding/information_set_decoder.py index b0fe180bce3..0846c1fa67d 100644 --- a/src/sage/coding/information_set_decoder.py +++ b/src/sage/coding/information_set_decoder.py @@ -41,7 +41,6 @@ #****************************************************************************** # python3 from __future__ import division, print_function, absolute_import -from six.moves import range from sage.all import ZZ, Integer, vector, SageObject, binomial from .decoder import Decoder diff --git a/src/sage/coding/kasami_codes.pyx b/src/sage/coding/kasami_codes.pyx new file mode 100644 index 00000000000..09bb3cef4d1 --- /dev/null +++ b/src/sage/coding/kasami_codes.pyx @@ -0,0 +1,354 @@ +# -*- coding: utf-8 -*- +r""" +Kasami code + +This module implements a construction for the extended Kasami codes. +The "regular" Kasami codes are obtained from truncating the extended version. + +The extended Kasami code with parameters `(s,t)` is defined as + +.. MATH:: + + \{ v \in GF(2)^s \mid + \sum_{a \in GF(s)} v_a = + \sum_{a \in GF(s)} a v_a = + \sum_{a \in GF(s)} a^{t+1} v_a = 0 \} + + +It follows that these are subfield subcodes of the code having those three +equations as parity checks. The only valid parameters `s,t` are given by the +below, where `q` is a power of 2 + +* `s = q^{2j+1}`, `t = q^m` with `m \leq j` and `\gcd(m,2j+1) = 1` +* `s = q^2`, `t=q` + +The coset graphs of the Kasami codes are distance-regular. In particular, the +extended Kasami codes result in distance-regular graphs with intersection arrays + +* `[q^{2j+1}, q^{2j+1} - 1, q^{2j+1} - q, q^{2j+1} - q^{2j} + 1;` + `1, q, q^{2j} -1, q^{2j+1}]` +* `[q^2, q^2 - 1, q^2 - q, 1; 1, q, q^2 - 1, q^2]` + +The Kasami codes result in distance-regular graphs with intersection arrays + +* `[q^{2j+1} - 1, q^{2j+1} - q, q^{2j+1} - q^{2j} + 1; 1, q, q^{2j} -1]` +* `[q^2 - 1, q^2 - q, 1; 1, q, q^2 - 1]` + +REFERENCES: + +- [BCN1989]_ p. 358 for a definition. + +- [Kas1966a]_ + +- [Kas1966b]_ + +- [Kas1971]_ + +AUTHORS: + +- Ivo Maffei (2020-07-09): initial version +""" + +#***************************************************************************** +# Copyright (C) 2020 Ivo Maffei +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.rings.finite_rings.finite_field_constructor import GF +from sage.matrix.constructor import matrix +from sage.coding.linear_code import (AbstractLinearCode, + LinearCodeGeneratorMatrixEncoder) +from sage.arith.misc import is_prime_power, gcd + +class KasamiCode(AbstractLinearCode): + r""" + Representation of a Kasami Code. + + The extended Kasami code with parameters `(s,t)` is defined as + + .. MATH:: + + \{ v \in GF(2)^s \mid + \sum_{a \in GF(s)} v_a = + \sum_{a \in GF(s)} a v_a = + \sum_{a \in GF(s)} a^{t+1} v_a = 0 \} + + The only valid parameters `s,t` are given by the below, + where `q` is a power of 2: + + * `s = q^{2j+1}`, `t = q^m` with `m \leq j` and `\gcd(m,2j+1) = 1` + * `s = q^2`, `t=q` + + The Kasami code `(s,t)` is obtained from the extended + Kasami code `(s,t)`, via truncation of all words. + + INPUT: + + - ``s,t`` -- (integer) the parameters of the Kasami code + + - ``extended`` -- (default: ``True``) if set to ``True``, + creates an extended Kasami code. + + EXAMPLES:: + + sage: codes.KasamiCode(16,4) + [16, 9] Extended (16, 4)-Kasami code + sage: _.minimum_distance() + 4 + + sage: codes.KasamiCode(8, 2, extended=False) + [7, 1] (8, 2)-Kasami code + + sage: codes.KasamiCode(8,4) + Traceback (most recent call last): + ... + ValueError: The parameters(=8,4) are invalid. Check the documentation + + The extended Kasami code is the extension of the Kasami code:: + + sage: C = codes.KasamiCode(16, 4, extended=False) + sage: Cext = C.extended_code() + sage: D = codes.KasamiCode(16, 4, extended=True) + sage: D.generator_matrix() == Cext.generator_matrix() + True + + .. SEEALSO:: + + :mod:`sage.coding.linear_code`. + + REFERENCES: + + For more information on Kasami codes and their use see [BCN1989]_ + or [Kas1966a]_, [Kas1966b]_, [Kas1971]_ + + TESTS:: + + sage: C1 = codes.KasamiCode(16, 4) + sage: C2 = codes.KasamiCode(16, 4, extended=False) + sage: C1.parameters() == C2.parameters() + True + sage: C1 == C2 + False + sage: C1.minimum_distance() == C2.minimum_distance()+1 + True + sage: C = codes.KasamiCode(4,2) + sage: C.dimension() + 0 + sage: C.generator_matrix() + [] + """ + + _registered_encoders = {} + _registered_decoders = {} + + def __init__(self, s, t, extended=True): + r""" + Constructor for the ``KasamiCode`` class. + + TESTS:: + + sage: codes.KasamiCode(64,8) + [64, 54] Extended (64, 8)-Kasami code + + sage: codes.KasamiCode(64,8, extended=False) + [63, 54] (64, 8)-Kasami code + + sage: codes.KasamiCode(3,5) + Traceback (most recent call last): + ... + ValueError: The parameter t(=5) must be a power of 2 + """ + # Check validity of s and t + (p,i) = is_prime_power(t,get_data=True) + if p != 2: + raise ValueError(f"The parameter t(={t}) must be a power of 2") + + if s != t*t: + # then we must have s=q^{2j+1} and t = q^m + (p,k) = is_prime_power(s,get_data=True) + if p != 2: + raise ValueError(f"The parameter s(={s}) must be a power of 2") + + # q= 2^l here l = gcd(k,i) + l = gcd(i,k) + q = 2**l + m = i // l + + if (k//l) % 2 == 0: + raise ValueError( + f"The parameter s(={s}) is invalid. Check the documentation") + + j = ((k//l) - 1) // 2 + + # gcd(m,2*j+1) = gcd( i/l, k/l) = 1 + if m > j: + raise ValueError( + (f"The parameters(={s},{t}) are invalid. " + "Check the documentation")) + + # s and t are valid!!! + self._s = s + self._t = t + self._extended = extended + + length = s-1 + if extended: + length += 1 + + super(KasamiCode, self).__init__(GF(2), length, + "GeneratorMatrix", "Syndrome") + + def parameters(self): + r""" + Return the parameters `s,t` of ``self``. + + EXAMPLES:: + + sage: C = codes.KasamiCode(16, 4, extended=True) + sage: C.parameters() + (16, 4) + sage: D = codes.KasamiCode(16, 4, extended=False) + sage: D.parameters() + (16, 4) + sage: C = codes.KasamiCode(8,2) + sage: C.parameters() + (8, 2) + """ + return (self._s,self._t) + + def __eq__(self, other): + r""" + Test equality between Kasami Code objects. + + Two Kasami codes are the same if they + have the same `s,t` values and are both + extended or both regular. + + EXAMPLES:: + + sage: C1 = codes.KasamiCode(8,2) + sage: C2 = codes.KasamiCode(8,2) + sage: C1.__eq__(C2) + True + """ + # Check that s, t, extended values of both + # objects are the same + return isinstance(other, KasamiCode) \ + and self.parameters() == other.parameters() \ + and self._extended == other._extended + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: codes.KasamiCode(4,2,extended=True) + [4, 0] Extended (4, 2)-Kasami code + """ + ext = "" + if self._extended: + ext = " Extended" + return "[%s, %s]%s (%s, %s)-Kasami code"\ + % (self.length(),self.dimension(), ext, self._s, self._t) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: C = codes.KasamiCode(16,4) + sage: latex(C) + [16, 9]\textnormal{ Extended} (16, 4)\textnormal{-Kasami code} + """ + ext = "" + if self._extended: + ext = " Extended" + return "[%s, %s]\\textnormal{%s} (%s, %s)\\textnormal{-Kasami code}"\ + % (self.length(), self.dimension(), ext, self._s, self._t) + + def generator_matrix(self): + r""" + Return a generator matrix of ``self``. + + EXAMPLES:: + + sage: C = codes.KasamiCode(16, 4, extended=False) + sage: C.generator_matrix() + [1 0 0 0 0 0 0 0 0 1 0 0 1 1 1] + [0 1 0 0 0 0 0 0 0 1 1 0 1 0 0] + [0 0 1 0 0 0 0 0 0 0 1 1 0 1 0] + [0 0 0 1 0 0 0 0 0 0 0 1 1 0 1] + [0 0 0 0 1 0 0 0 0 0 0 0 1 1 0] + [0 0 0 0 0 1 0 0 0 1 1 0 1 1 1] + [0 0 0 0 0 0 1 0 0 0 1 1 0 1 1] + [0 0 0 0 0 0 0 1 0 1 1 1 0 0 1] + [0 0 0 0 0 0 0 0 1 1 0 1 0 0 0] + + ALGORITHM: + + We build the parity check matrix given by the three equations that + the codewords must satisfy. Then we generate the parity check matrix + over `GF(2)` and from this the obtain the generator matrix for the + extended Kasami codes. + + For the Kasami codes, we truncate the last column. + + TESTS:: + + sage: C = codes.KasamiCode(4,2) + sage: C.generator_matrix() + [] + sage: C = codes.KasamiCode(8,2) + sage: C.generator_matrix() + [1 1 1 1 1 1 1 1] + sage: C.minimum_distance() + 8 + sage: C = codes.KasamiCode(8, 2, extended=False) + sage: C.generator_matrix() + [1 1 1 1 1 1 1] + sage: C.minimum_distance() + 7 + sage: C = codes.KasamiCode(4, 2, extended=False) + sage: C.generator_matrix() + [] + sage: C = codes.KasamiCode(16, 4, extended=False) + sage: C.minimum_distance() + 3 + """ + from sage.functions.log import log + + m = log(self._s, 2) + F = GF(self._s) + + def exp(row): + return matrix(F, + [x + [0]*(m - len(x)) for x in + [a.polynomial().list() for a in row]]).transpose() + + # Parity check matrix over GF(s) + Hs = matrix(F, [[1]*self._s, + F.list(), + [a**(self._t + 1) for a in F]]) + + # Parity check matrix over GF(2) + H = matrix.block(GF(2), 3, 1, [exp(row) for row in Hs.rows()]) + + # Generator matrix + G = H.right_kernel().basis_matrix() + + if self._extended: + return G + + newG = [v[:-1] for v in G] + return matrix(GF(2), newG) + + +KasamiCode._registered_encoders[ + "GeneratorMatrix"] = LinearCodeGeneratorMatrixEncoder diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index e1064980632..f2c35c28478 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -203,18 +203,18 @@ class should inherit from this class. Also ``AbstractLinearCode`` should never # ***************************************************************************** from __future__ import division, print_function, absolute_import -from six.moves import range +import os +import subprocess +from io import StringIO from copy import copy from sage.cpython.string import bytes_to_str from sage.interfaces.all import gap -from sage.categories.modules import Modules from sage.categories.cartesian_product import cartesian_product from sage.categories.fields import Fields from sage.matrix.matrix_space import MatrixSpace from sage.modules.free_module import VectorSpace -from sage.modules.module import Module from sage.modules.free_module_element import vector from sage.arith.all import GCD, binomial from sage.groups.all import SymmetricGroup @@ -224,13 +224,13 @@ class should inherit from this class. Also ``AbstractLinearCode`` should never from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.integer import Integer from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF -from sage.structure.parent import Parent from sage.misc.all import prod from sage.misc.functional import is_even from sage.misc.cachefunc import cached_method from sage.misc.randstate import current_randstate from sage.combinat.subset import Subsets from sage.features.gap import GapPackage +from sage.coding.linear_code_no_metric import AbstractLinearCodeNoMetric from sage.coding.abstract_code import AbstractCode from .encoder import Encoder from .decoder import Decoder @@ -286,7 +286,7 @@ def _dump_code_in_leon_format(C): return file_loc -class AbstractLinearCode(AbstractCode, Module): +class AbstractLinearCode(AbstractLinearCodeNoMetric): """ Abstract base class for linear codes. @@ -423,77 +423,17 @@ def __init__(self, base_field, length, default_encoder_name, default_decoder_nam False sage: print(C.divisor()) #long time 1 - - TESTS: - - If the name of the default decoder is not known by the class, it will raise - a exception:: - - sage: class MyCodeFamily2(sage.coding.linear_code.AbstractLinearCode): - ....: def __init__(self, field, length, dimension, generator_matrix): - ....: sage.coding.linear_code.AbstractLinearCode.__init__(self,field, length, "GeneratorMatrix", "Fail") - ....: self._dimension = dimension - ....: self._generator_matrix = generator_matrix - ....: def generator_matrix(self): - ....: return self._generator_matrix - ....: def _repr_(self): - ....: return "[%d, %d] dummy code over GF(%s)" % (self.length(), self.dimension(), self.base_field().cardinality()) - - sage: C = MyCodeFamily2(GF(17), 10, 5, generator_matrix) - Traceback (most recent call last): - ... - ValueError: You must set a valid decoder as default decoder for this code, by filling in the dictionary of registered decoders - - If the name of the default encoder is not known by the class, it will raise - an exception:: - - sage: class MyCodeFamily3(sage.coding.linear_code.AbstractLinearCode): - ....: def __init__(self, field, length, dimension, generator_matrix): - ....: sage.coding.linear_code.AbstractLinearCode.__init__(self,field, length, "Fail", "Syndrome") - ....: self._dimension = dimension - ....: self._generator_matrix = generator_matrix - ....: def generator_matrix(self): - ....: return self._generator_matrix - ....: def _repr_(self): - ....: return "[%d, %d] dummy code over GF(%s)" % (self.length(), self.dimension(), self.base_field().cardinality()) - - sage: C = MyCodeFamily3(GF(17), 10, 5, generator_matrix) - Traceback (most recent call last): - ... - ValueError: You must set a valid encoder as default encoder for this code, by filling in the dictionary of registered encoders - - A ring instead of a field:: - - sage: codes.LinearCode(IntegerModRing(4),matrix.ones(4)) - Traceback (most recent call last): - ... - ValueError: 'generator' must be defined on a field (not a ring) """ from sage.coding.information_set_decoder import LinearCodeInformationSetDecoder # Add here any generic encoder or decoder. This allows any class which # inherits from AbstractLinearCode to use generic decoders/encoders - self._registered_encoders['Systematic'] = LinearCodeSystematicEncoder self._registered_decoders['Syndrome'] = LinearCodeSyndromeDecoder self._registered_decoders['NearestNeighbor'] = LinearCodeNearestNeighborDecoder self._registered_decoders['InformationSet'] = LinearCodeInformationSetDecoder - if not isinstance(length, (int, Integer)) or length <= 0: - raise ValueError("length must be a positive integer") - if not base_field.is_field(): - raise ValueError("{} is not a field".format(base_field)) - if not default_encoder_name in self._registered_encoders: - raise ValueError("You must set a valid encoder as default encoder for this code, by filling in the dictionary of registered encoders") - if not default_decoder_name in self._registered_decoders: - raise ValueError("You must set a valid decoder as default decoder for this code, by filling in the dictionary of registered decoders") - - self._default_decoder_name = default_decoder_name - self._default_encoder_name = default_encoder_name - super(AbstractLinearCode, self).__init__(length, default_encoder_name, default_decoder_name) - cat = Modules(base_field).FiniteDimensional().WithBasis().Finite() - facade_for = VectorSpace(base_field, self._length) - self.Element = type(facade_for.an_element()) #for when we made this a non-facade parent - Parent.__init__(self, base=base_field, facade=facade_for, category=cat) + self._generic_constructor = LinearCode + super(AbstractLinearCode, self).__init__(base_field, length, default_encoder_name, default_decoder_name) def _an_element_(self): r""" @@ -570,18 +510,6 @@ def automorphism_group_gens(self, equivalence="semilinear"): return aut_group_can_label.get_autom_gens(), \ aut_group_can_label.get_autom_order() - def ambient_space(self): - r""" - Returns the ambient vector space of ``self``. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: C.ambient_space() - Vector space of dimension 7 over Finite Field of size 2 - """ - return VectorSpace(self.base_ring(),self.length()) - def assmus_mattson_designs(self, t, mode=None): r""" Assmus and Mattson Theorem (section 8.4, page 303 of [HP2003]_): Let @@ -691,44 +619,6 @@ def assmus_mattson_designs(self, t, mode=None): return ans return 0 - def base_field(self): - r""" - Return the base field of ``self``. - - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: C.base_field() - Finite Field of size 2 - """ - return self.base_ring() - - def basis(self): - r""" - Returns a basis of ``self``. - - OUTPUT: - - - ``Sequence`` - an immutable sequence whose universe is ambient space of ``self``. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: C.basis() - [ - (1, 0, 0, 0, 0, 1, 1), - (0, 1, 0, 0, 1, 0, 1), - (0, 0, 1, 0, 1, 1, 0), - (0, 0, 0, 1, 1, 1, 1) - ] - sage: C.basis().universe() - Vector space of dimension 7 over Finite Field of size 2 - """ - gens = self.gens() - from sage.structure.sequence import Sequence - return Sequence(gens, universe=self.ambient_space(), check = False, immutable=True, cr=True) - # S. Pancratz, 19 Jan 2010: In the doctests below, I removed the example # ``C.binomial_moment(3)``, which was also marked as ``#long``. This way, # we shorten the doctests time while still maintaining a zero and a @@ -894,24 +784,6 @@ def canonical_representative(self, equivalence="semilinear"): return aut_group_can_label.get_canonical_form(), \ aut_group_can_label.get_transporter() - def __contains__(self, v): - r""" - Returns True if `v` can be coerced into ``self``. Otherwise, returns False. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: vector((1, 0, 0, 0, 0, 1, 1)) in C # indirect doctest - True - sage: vector((1, 0, 0, 0, 2, 1, 1)) in C # indirect doctest - True - sage: vector((1, 0, 0, 0, 0, 1/2, 1)) in C # indirect doctest - False - """ - if not v in self.ambient_space() or len(v) != self.length(): - return False - return self.syndrome(v) == 0 - def characteristic(self): r""" Returns the characteristic of the base ring of ``self``. @@ -1008,46 +880,6 @@ def chinen_polynomial(self): f = CP/CP(1,s) return f(t,sqrt(q)) - @cached_method - def parity_check_matrix(self): - r""" - Returns the parity check matrix of ``self``. - - The parity check matrix of a linear code `C` corresponds to the - generator matrix of the dual code of `C`. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: Cperp = C.dual_code() - sage: C; Cperp - [7, 4] Hamming Code over GF(2) - [7, 3] linear code over GF(2) - sage: C.generator_matrix() - [1 0 0 0 0 1 1] - [0 1 0 0 1 0 1] - [0 0 1 0 1 1 0] - [0 0 0 1 1 1 1] - sage: C.parity_check_matrix() - [1 0 1 0 1 0 1] - [0 1 1 0 0 1 1] - [0 0 0 1 1 1 1] - sage: Cperp.parity_check_matrix() - [1 0 0 0 0 1 1] - [0 1 0 0 1 0 1] - [0 0 1 0 1 1 0] - [0 0 0 1 1 1 1] - sage: Cperp.generator_matrix() - [1 0 1 0 1 0 1] - [0 1 1 0 0 1 1] - [0 0 0 1 1 1 1] - """ - G = self.generator_matrix() - H = G.right_kernel() - M = H.basis_matrix() - M.set_immutable() - return M - @cached_method def covering_radius(self): r""" @@ -1155,68 +987,6 @@ def projectivize(row): return True - def dual_code(self): - r""" - Returns the dual code `C^{\perp}` of the code `C`, - - .. MATH:: - - C^{\perp} = \{ v \in V\ |\ v\cdot c = 0,\ \forall c \in C \}. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: C.dual_code() - [7, 3] linear code over GF(2) - sage: C = codes.HammingCode(GF(4, 'a'), 3) - sage: C.dual_code() - [21, 3] linear code over GF(4) - """ - return LinearCode(self.parity_check_matrix()) - - def dimension(self): - r""" - Returns the dimension of this code. - - EXAMPLES:: - - sage: G = matrix(GF(2),[[1,0,0],[1,1,0]]) - sage: C = LinearCode(G) - sage: C.dimension() - 2 - - TESTS: - - Check that :trac:`21156` is fixed:: - - sage: from sage.coding.linear_code import AbstractLinearCode - sage: from sage.coding.encoder import Encoder - sage: class MonkeyCode(AbstractLinearCode): - ....: _registered_encoders = {} - ....: _registered_decoders = {} - ....: def __init__(self): - ....: super(MonkeyCode, self).__init__(GF(5), 10, "Monkey", "Syndrome") - ....: - sage: class MonkeyEncoder(Encoder): - ....: def __init__(self, code): - ....: super(MonkeyEncoder, self).__init__(C) - ....: @cached_method - ....: def generator_matrix(self): - ....: G = identity_matrix(GF(5), 5).augment(matrix(GF(5), 5, 7)) - ....: return G - ....: - sage: MonkeyCode._registered_encoders["Monkey"] = MonkeyEncoder - sage: C = MonkeyCode() - sage: C.dimension() - 5 - """ - try: - return self._dimension - except AttributeError: - dimension = self.generator_matrix().nrows() - self._dimension = dimension - return self._dimension - def direct_sum(self, other): """ Direct sum of the codes ``self`` and ``other`` @@ -1388,60 +1158,6 @@ def construction_x(self, other, aux): G = left.augment(right) return LinearCode(G) - def __eq__(self, right): - """ - Checks if ``self`` is equal to ``right``. - - EXAMPLES:: - - sage: C1 = codes.HammingCode(GF(2), 3) - sage: C2 = codes.HammingCode(GF(2), 3) - sage: C1 == C2 - True - - TESTS: - - We check that :trac:`16644` is fixed:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: C == ZZ - False - """ - if not (isinstance(right, LinearCode)\ - and self.length() == right.length()\ - and self.dimension() == right.dimension()\ - and self.base_ring() == right.base_ring()): - return False - Ks = self.parity_check_matrix().right_kernel() - rbas = right.gens() - if not all(c in Ks for c in rbas): - return False - Kr = right.parity_check_matrix().right_kernel() - sbas = self.gens() - if not all(c in Kr for c in sbas): - return False - return True - - def __ne__(self, other): - r""" - Tests inequality of ``self`` and ``other``. - - This is a generic implementation, which returns the inverse of ``__eq__`` for self. - - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C1 = LinearCode(G) - sage: C2 = LinearCode(G) - sage: C1 != C2 - False - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,1,1]]) - sage: C2 = LinearCode(G) - sage: C1 != C2 - True - """ - return not self == other - def extended_code(self): r""" Returns `self` as an extended code. @@ -1502,180 +1218,6 @@ def galois_closure(self, F0): G = MS([Grref.row(i) for i in range(r)]) return LinearCode(G) - def __getitem__(self, i): - r""" - Returns the `i`-th codeword of this code. - - The implementation of this depends on the implementation of the - :meth:`.__iter__` method. - - The implementation is as follows. Suppose that: - - - the primitive element of the base_ring of this code is `a`, - - the prime subfield is `p`, - - the field has order `p^m`, - - the code has dimension `k`, - - and the generator matrix is `G`. - - Then the :meth:`.__iter__` method returns the elements in this order: - - 1. first, the following ordered list is returned: - ``[i*a^0 * G[0] for i in range(p)]`` - 2. Next, the following ordered list is returned: - ``[i*a^0 * G[0] + a^1*G[0] for i in range(p)]`` - 3. This continues till we get - ``[(i*a^0 +(p-1)*a^1 +...+ (p-1)*a^(m-1))*G[0] for i in range(p)]`` - 4. Then, we move to G[1]: - ``[i*a^0 * G[0] + a^0*G[1] for i in range(p)]``, - and so on. - Hence the `i`-th element can be obtained by the p-adic expansion - of `i` as ``[i_0, i_1, ...,i_{m-1}, i_m, i_{m+1}, ..., i_{km-1}].`` - The element that is generated is: - - .. MATH:: - - \begin{aligned} - & (i_0 a^0 + i_1 a^1 + \cdots + i_{m-1} a^{m-1}) G[0] + \\ - & (i_m a^0 + i_{m+1} a^1 + \cdots + i_{2m-1} a^{m-1}) G[1] + \\ - & \vdots\\ - & (i_{(k-1)m} a^0 + \cdots + i_{km-1} a^{m-1}) G[k-1] - \end{aligned} - - EXAMPLES:: - - sage: G = Matrix(GF(3), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: C[24] - (2, 2, 0, 1, 2, 2, 0) - sage: C[24] == C.list()[24] - True - - TESTS:: - - sage: C = random_matrix(GF(25,'a'), 2, 7).row_space() - sage: C = LinearCode(C.basis_matrix()) - sage: Clist = C.list() - sage: all(C[i] == Clist[i] for i in range(len(C))) - True - - Check that only the indices less than the size of the code are - allowed:: - - sage: C[25**2] - Traceback (most recent call last): - ... - IndexError: The value of the index 'i' (=625) must be between - 0 and 'q^k -1' (=624), inclusive, where 'q' is the size of the - base field and 'k' is the dimension of the code. - - Check that codewords are immutable. See :trac:`16338`:: - - sage: C[0].is_immutable() - True - - """ - # IMPORTANT: If the __iter__() function implementation is changed - # then the implementation here must also be changed so that - # list(self)[i] and self[i] both return the same element. - - F = self.base_ring() - maxindex = F.order()**self.dimension()-1 - if i < 0 or i > maxindex: - raise IndexError("The value of the index 'i' (={}) must be between " - "0 and 'q^k -1' (={}), inclusive, where 'q' is " - "the size of the base field and 'k' is the " - "dimension of the code.".format(i, maxindex)) - - a = F.primitive_element() - m = F.degree() - p = F.prime_subfield().order() - A = [a ** k for k in range(m)] - G = self.generator_matrix() - N = self.dimension()*F.degree() # the total length of p-adic vector - ivec = Integer(i).digits(p, padto=N) - - codeword = 0 - row = 0 - for g in G: - codeword += sum(ivec[j+row*m]*A[j] for j in range(m)) * g - row += 1 - - # The codewords for a specific code can not change. So, we set them - # to be immutable. - codeword.set_immutable() - return codeword - - def generator_matrix(self, encoder_name=None, **kwargs): - r""" - Returns a generator matrix of ``self``. - - INPUT: - - - ``encoder_name`` -- (default: ``None``) name of the encoder which will be - used to compute the generator matrix. The default encoder of ``self`` - will be used if default value is kept. - - - ``kwargs`` -- all additional arguments are forwarded to the construction of the - encoder that is used. - - EXAMPLES:: - - sage: G = matrix(GF(3),2,[1,-1,1,-1,1,1]) - sage: code = LinearCode(G) - sage: code.generator_matrix() - [1 2 1] - [2 1 1] - """ - E = self.encoder(encoder_name, **kwargs) - return E.generator_matrix() - - def systematic_generator_matrix(self, systematic_positions=None): - """ - Return a systematic generator matrix of the code. - - A generator matrix of a code is called systematic if it contains - a set of columns forming an identity matrix. - - INPUT: - - - ``systematic_positions`` -- (default: ``None``) if supplied, the set - of systematic positions in the systematic generator matrix. See the - documentation for :class:`LinearCodeSystematicEncoder` details. - - EXAMPLES:: - - sage: G = matrix(GF(3), [[ 1, 2, 1, 0],\ - [ 2, 1, 1, 1]]) - sage: C = LinearCode(G) - sage: C.generator_matrix() - [1 2 1 0] - [2 1 1 1] - sage: C.systematic_generator_matrix() - [1 2 0 1] - [0 0 1 2] - - Specific systematic positions can also be requested: - - sage: C.systematic_generator_matrix(systematic_positions=[3,2]) - [1 2 0 1] - [1 2 1 0] - """ - systematic_positions = tuple(systematic_positions) if systematic_positions else None - return self.encoder("Systematic", systematic_positions=systematic_positions).generator_matrix() - - @cached_method - def gens(self): - r""" - Returns the generators of this code as a list of vectors. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: C.gens() - [(1, 0, 0, 0, 0, 1, 1), (0, 1, 0, 0, 1, 0, 1), (0, 0, 1, 0, 1, 1, 0), (0, 0, 0, 1, 1, 1, 1)] - """ - return self.generator_matrix().rows() - def genus(self): r""" Returns the "Duursma genus" of the code, `\gamma_C = n+1-k-d`. @@ -1700,120 +1242,6 @@ def genus(self): gammaC = n+1-k-d return gammaC - def __iter__(self): - """ - Return an iterator over the elements of this linear code. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: [list(c) for c in C if c.hamming_weight() < 4] - [[0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 1, 1], - [0, 1, 0, 0, 1, 0, 1], [0, 0, 1, 0, 1, 1, 0], - [1, 1, 1, 0, 0, 0, 0], [1, 0, 0, 1, 1, 0, 0], - [0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 1, 0, 0, 1]] - - TESTS:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: L = list(C) - sage: L[10].is_immutable() - True - - """ - from sage.modules.finite_submodule_iter import \ - FiniteFieldsubspace_iterator - return FiniteFieldsubspace_iterator(self.generator_matrix(), immutable=True) - - @cached_method - def information_set(self): - """ - Return an information set of the code. - - Return value of this method is cached. - - A set of column positions of a generator matrix of a code - is called an information set if the corresponding columns - form a square matrix of full rank. - - OUTPUT: - - - Information set of a systematic generator matrix of the code. - - EXAMPLES:: - - sage: G = matrix(GF(3),2,[1,2,0,\ - 2,1,1]) - sage: code = LinearCode(G) - sage: code.systematic_generator_matrix() - [1 2 0] - [0 0 1] - sage: code.information_set() - (0, 2) - """ - return self.encoder("Systematic").systematic_positions() - - def is_information_set(self, positions): - """ - Return whether the given positions form an information set. - - INPUT: - - - A list of positions, i.e. integers in the range 0 to `n-1` where `n` - is the length of `self`. - - OUTPUT: - - - A boolean indicating whether the positions form an information set. - - - EXAMPLES:: - - sage: G = matrix(GF(3),2,[1,2,0,\ - 2,1,1]) - sage: code = LinearCode(G) - sage: code.is_information_set([0,1]) - False - sage: code.is_information_set([0,2]) - True - """ - try: - self.encoder("Systematic", systematic_positions=tuple(positions)) - return True - except ValueError: - return False - - def is_permutation_automorphism(self,g): - r""" - Returns `1` if `g` is an element of `S_n` (`n` = length of self) and - if `g` is an automorphism of self. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(3), 3) - sage: g = SymmetricGroup(13).random_element() - sage: C.is_permutation_automorphism(g) - 0 - sage: MS = MatrixSpace(GF(2),4,8) - sage: G = MS([[1,0,0,0,1,1,1,0],[0,1,1,1,0,0,0,0],[0,0,0,0,0,0,0,1],[0,0,0,0,0,1,0,0]]) - sage: C = LinearCode(G) - sage: S8 = SymmetricGroup(8) - sage: g = S8("(2,3)") - sage: C.is_permutation_automorphism(g) - 1 - sage: g = S8("(1,2,3,4)") - sage: C.is_permutation_automorphism(g) - 0 - """ - basis = self.generator_matrix().rows() - H = self.parity_check_matrix() - V = H.column_space() - HGm = H*g.matrix() - for c in basis: - if HGm*c != V(0): - return False - return True - def is_permutation_equivalent(self,other,algorithm=None): """ Returns ``True`` if ``self`` and ``other`` are permutation equivalent @@ -1859,47 +1287,10 @@ def is_permutation_equivalent(self,other,algorithm=None): ans = B1.is_isomorphic(B2) if ans is not False: if algorithm=="verbose": - Sn = SymmetricGroup(n) - return True, Sn([i+1 for i in ans])**(-1) - return True - return False - - def is_self_dual(self): - """ - Returns ``True`` if the code is self-dual (in the usual Hamming inner - product) and ``False`` otherwise. - - EXAMPLES:: - - sage: C = codes.GolayCode(GF(2)) - sage: C.is_self_dual() - True - sage: C = codes.HammingCode(GF(2), 3) - sage: C.is_self_dual() - False - """ - return self == self.dual_code() - - def is_self_orthogonal(self): - """ - Returns ``True`` if this code is self-orthogonal and ``False`` - otherwise. - - A code is self-orthogonal if it is a subcode of its dual. - - EXAMPLES:: - - sage: C = codes.GolayCode(GF(2)) - sage: C.is_self_orthogonal() - True - sage: C = codes.HammingCode(GF(2), 3) - sage: C.is_self_orthogonal() - False - sage: C = codes.QuasiQuadraticResidueCode(11) # optional - gap_packages (Guava package) - sage: C.is_self_orthogonal() # optional - gap_packages (Guava package) - True - """ - return self.is_subcode(self.dual_code()) + Sn = SymmetricGroup(n) + return True, Sn([i+1 for i in ans])**(-1) + return True + return False def is_galois_closed(self): r""" @@ -1915,70 +1306,6 @@ def is_galois_closed(self): p = F.characteristic() return self == self.galois_closure(GF(p)) - def is_subcode(self, other): - """ - Returns ``True`` if ``self`` is a subcode of ``other``. - - EXAMPLES:: - - sage: C1 = codes.HammingCode(GF(2), 3) - sage: G1 = C1.generator_matrix() - sage: G2 = G1.matrix_from_rows([0,1,2]) - sage: C2 = LinearCode(G2) - sage: C2.is_subcode(C1) - True - sage: C1.is_subcode(C2) - False - sage: C3 = C1.extended_code() - sage: C1.is_subcode(C3) - False - sage: C4 = C1.punctured([1]) - sage: C4.is_subcode(C1) - False - sage: C5 = C1.shortened([1]) - sage: C5.is_subcode(C1) - False - sage: C1 = codes.HammingCode(GF(9,"z"), 3) - sage: G1 = C1.generator_matrix() - sage: G2 = G1.matrix_from_rows([0,1,2]) - sage: C2 = LinearCode(G2) - sage: C2.is_subcode(C1) - True - """ - G = self.generator_matrix() - for r in G.rows(): - if not(r in other): - return False - return True - - def cardinality(self): - r""" - Return the size of this code. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: C.cardinality() - 16 - sage: len(C) - 16 - """ - return self.base_ring().order()**self.dimension() - - __len__ = cardinality - - def length(self): - r""" - Returns the length of this code. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: C.length() - 7 - """ - return self._length - def _magma_init_(self, magma): r""" Retun a string representation in Magma of this linear code. @@ -2357,27 +1684,6 @@ def permutation_automorphism_group(self, algorithm="partition"): return PermutationGroup([x.get_perm() for x in gens]) raise NotImplementedError("The only algorithms implemented currently are 'gap', 'gap+verbose', and 'partition'.") - def permuted_code(self, p): - r""" - Returns the permuted code, which is equivalent to ``self`` via the - column permutation ``p``. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: G = C.permutation_automorphism_group(); G - Permutation Group with generators [(4,5)(6,7), (4,6)(5,7), (2,3)(6,7), (2,4)(3,5), (1,2)(5,6)] - sage: g = G("(2,3)(6,7)") - sage: Cg = C.permuted_code(g) - sage: Cg - [7, 4] linear code over GF(2) - sage: C.generator_matrix() == Cg.systematic_generator_matrix() - True - """ - G = copy(self.generator_matrix()) - G.permute_columns(p) - return LinearCode(G) - def punctured(self, L): r""" Returns a :class:`sage.coding.punctured_code` object from ``L``. @@ -2433,63 +1739,6 @@ def relative_distance(self): """ return self.minimum_distance() / self.length() - def rate(self): - r""" - Return the ratio of the number of information symbols to - the code length. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: C.rate() - 4/7 - """ - return self.dimension() / self.length() - - def redundancy_matrix(self): - r""" - Returns the non-identity columns of a systematic generator matrix for - ``self``. - - A systematic generator matrix is a generator matrix such that a subset - of its columns forms the identity matrix. This method returns the - remaining part of the matrix. - - For any given code, there can be many systematic generator matrices - (depending on which positions should form the identity). This method - will use the matrix returned by - :meth:`AbstractLinearCode.systematic_generator_matrix`. - - OUTPUT: - - - An `k \times (n-k)` matrix. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: C.generator_matrix() - [1 0 0 0 0 1 1] - [0 1 0 0 1 0 1] - [0 0 1 0 1 1 0] - [0 0 0 1 1 1 1] - sage: C.redundancy_matrix() - [0 1 1] - [1 0 1] - [1 1 0] - [1 1 1] - sage: C = LinearCode(matrix(GF(3),2,[1,2,0,\ - 2,1,1])) - sage: C.systematic_generator_matrix() - [1 2 0] - [0 0 1] - sage: C.redundancy_matrix() - [2] - [0] - """ - E = self.encoder("Systematic") - G = E.generator_matrix() - return G.delete_columns(E.systematic_positions()) - def shortened(self, L): r""" Returns the code shortened at the positions ``L``, where @@ -2609,10 +1858,7 @@ def weight_distribution(self, algorithm=None): guava_bin_dir = gap.eval('DirectoriesPackagePrograms("guava")[1]') guava_bin_dir = guava_bin_dir[guava_bin_dir.index('"') + 1:guava_bin_dir.rindex('"')] input = _dump_code_in_leon_format(self) + "::code" - import os - import subprocess lines = subprocess.check_output([os.path.join(guava_bin_dir, 'wtdist'), input]) - from six import StringIO # to use the already present output parser wts = [0] * (n + 1) for L in StringIO(bytes_to_str(lines)).readlines(): @@ -2628,60 +1874,6 @@ def weight_distribution(self, algorithm=None): spectrum = weight_distribution - def standard_form(self, return_permutation=True): - r""" - Returns a linear code which is permutation-equivalent to ``self`` and - admits a generator matrix in standard form. - - A generator matrix is in standard form if it is of the form `[I \vert - A]`, where `I` is the `k \times k` identity matrix. Any code admits a - generator matrix in systematic form, i.e. where a subset of the columns - form the identity matrix, but one might need to permute columns to allow - the identity matrix to be leading. - - INPUT: - - - ``return_permutation`` -- (default: ``True``) if ``True``, the column - permutation which brings ``self`` into the returned code is also - returned. - - OUTPUT: - - - A :class:`LinearCode` whose :meth:`systematic_generator_matrix` is - guaranteed to be of the form `[I \vert A]`. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: C.generator_matrix() - [1 0 0 0 0 1 1] - [0 1 0 0 1 0 1] - [0 0 1 0 1 1 0] - [0 0 0 1 1 1 1] - sage: Cs,p = C.standard_form() - sage: p - [] - sage: Cs is C - True - sage: C = LinearCode(matrix(GF(2), [[1,0,0,0,1,1,0],\ - [0,1,0,1,0,1,0],\ - [0,0,0,0,0,0,1]])) - sage: Cs, p = C.standard_form() - sage: p - [1, 2, 7, 3, 4, 5, 6] - sage: Cs.generator_matrix() - [1 0 0 0 0 1 1] - [0 1 0 0 1 0 1] - [0 0 1 0 0 0 0] - """ - E = self.encoder("Systematic") - if E.systematic_positions() == tuple(range(self.dimension())): - from sage.combinat.permutation import Permutation - return self, Permutation([]) - else: - perm = E.systematic_permutation() - return self.permuted_code(perm), perm - def support(self): r""" Returns the set of indices `j` where `A_j` is nonzero, where @@ -2704,50 +1896,6 @@ def support(self): V = VectorSpace(F,n+1) return V(self.weight_distribution()).support() - def syndrome(self, r): - r""" - Returns the syndrome of ``r``. - - The syndrome of ``r`` is the result of `H \times r` where `H` is - the parity check matrix of ``self``. If ``r`` belongs to ``self``, - its syndrome equals to the zero vector. - - INPUT: - - - ``r`` -- a vector of the same length as ``self`` - - OUTPUT: - - - a column vector - - EXAMPLES:: - - sage: MS = MatrixSpace(GF(2),4,7) - sage: G = MS([[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: r = vector(GF(2), (1,0,1,0,1,0,1)) - sage: r in C - True - sage: C.syndrome(r) - (0, 0, 0) - - If ``r`` is not a codeword, its syndrome is not equal to zero:: - - sage: r = vector(GF(2), (1,0,1,0,1,1,1)) - sage: r in C - False - sage: C.syndrome(r) - (0, 1, 1) - - Syndrome computation works fine on bigger fields:: - - sage: C = codes.random_linear_code(GF(59), 12, 4) - sage: c = C.random_element() - sage: C.syndrome(c) - (0, 0, 0, 0, 0, 0, 0, 0) - """ - return self.parity_check_matrix()*r - def weight_enumerator(self, names=None, bivariate=True): r""" Return the weight enumerator polynomial of ``self``. @@ -2807,23 +1955,6 @@ def weight_enumerator(self, names=None, bivariate=True): x, = R.gens() return sum(spec[i]*x**i for i in range(n+1)) - @cached_method - def zero(self): - r""" - Returns the zero vector of ``self``. - - EXAMPLES:: - - sage: C = codes.HammingCode(GF(2), 3) - sage: C.zero() - (0, 0, 0, 0, 0, 0, 0) - sage: C.sum(()) # indirect doctest - (0, 0, 0, 0, 0, 0, 0) - sage: C.sum((C.gens())) # indirect doctest - (1, 1, 1, 1, 1, 1, 1) - """ - return self.ambient_space().zero() - def zeta_polynomial(self, name="T"): r""" Returns the Duursma zeta polynomial of this code. @@ -2916,6 +2047,117 @@ def zeta_function(self, name="T"): T = RT.gen() return P/((1-T)*(1-q*T)) + def cosetGraph(self): + r""" + Return the coset graph of this linear code. + + The coset graph of a linear code `C` is the graph whose vertices + are the cosets of `C`, considered as a subgroup of the additive + group of the ambient vector space, and two cosets are adjacent + if they have representatives that differ in exactly one coordinate. + + EXAMPLES:: + + sage: C = codes.GolayCode(GF(3)) + sage: G = C.cosetGraph() + sage: G.is_distance_regular() + True + sage: C = codes.KasamiCode(8,2) + sage: G = C.cosetGraph() + sage: G.is_distance_regular() + True + + ALGORITHM: + + Instead of working with cosets we compute a (direct sum) complement + of `C`. Let `P` be the projection of the cosets to the newly found + subspace. Then two vectors are adjacent if they differ by + `\lambda P(e_i)` for some `i`. + + TESTS:: + + sage: C = codes.KasamiCode(4,2) + sage: C.cosetGraph() + Hamming Graph with parameters 4,2: Graph on 16 vertices + sage: M = matrix.identity(GF(3), 7) + sage: C = LinearCode(M) + sage: G = C.cosetGraph() + sage: G.vertices() + [0] + sage: G.edges() + [] + """ + from sage.matrix.constructor import matrix + from sage.graphs.graph import Graph + + F = self.base_field() + + def e(i): + v = [0]*self.length() + v[i-1] = 1 + return vector(F, v, immutable=True) + + # Handle special cases + if len(self.basis()) == self.length(): + G = Graph(1) + G.name(f"coset graph of {self.__repr__()}") + return G + if len(self.basis()) == 0: + from sage.graphs.graph_generators import GraphGenerators + return GraphGenerators.HammingGraph(self.length(), F.order()) + + # we need to find a basis for the complement + M = matrix(F, self.basis()) + M.echelonize() + C_basis = M.rows() + + # we use Steinitz exchange lemma on [e_1, ..., e_n] + # to obtain a basis of V which includes C_basis + U_basis = list(range(self.length())) # i represents e_{i+1} + for v in C_basis: + i = v.support()[0] + U_basis.remove(i) # swap e_{i+1} with v + U_basis = [e(i+1) for i in U_basis] + + V = VectorSpace(F, self.length()) + U = V.span(U_basis) + vertices = list(U) + + # build matrix whose columns are the basis of U and C + A = matrix(F, U_basis) + A = A.stack(matrix(F, C_basis)) + A = A.transpose() + Ainv = A.inverse() + + Pei = [] # projection of e_i on U + for i in range(1, self.length()+1): + ei = e(i) + if ei in U: + Pei.append(ei) + else: + a = Ainv * ei + # get zero vector and sum a[i]u_i to it + v = vector(F, [0]*self.length()) + for i in range(len(U_basis)): + v += a[i]*U_basis[i] + if not v.is_zero(): # don't care about 0 vectors + v.set_immutable() + Pei.append(v) + + lPei = [l*u for l in F for u in Pei if not l.is_zero()] + + edges = [] + for v in vertices: + v.set_immutable() + for u in lPei: + w = v + u + w.set_immutable() + edges.append((v, w)) + + G = Graph(edges, format='list_of_edges') + G.name(f"coset graph of {self.__repr__()}") + return G + ############################ linear codes python class ######################## @@ -3088,6 +2330,11 @@ def __init__(self, generator, d=None): self._dimension = generator.rank() self._minimum_distance = d + def __hash__(self): + Str = str(self) + G = self.generator_matrix() + return hash((Str, G)) + def _repr_(self): r""" See the docstring for :meth:`LinearCode`. @@ -3121,33 +2368,6 @@ def _latex_(self): return "[%s, %s]\\textnormal{ Linear code over }%s"\ % (self.length(), self.dimension(), self.base_ring()._latex_()) - def __hash__(self): - r""" - Returns the hash value of ``self``. - - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: hash(C) #random - 9015017528451745710 - - If ``C1`` and ``C2`` are two codes which only differ by the - coefficients of their generator matrices, their hashes are - different (we check that the bug found in :trac:`18813` is - fixed):: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C1 = LinearCode(G) - sage: G = Matrix(GF(2), [[1,0,0,1,0,1,0],[0,1,0,0,1,0,0],[0,0,1,1,0,1,0],[0,0,0,0,0,0,1]]) - sage: C2 = LinearCode(G) - sage: hash(C1) != hash(C2) - True - """ - Str = str(self) - G = self.generator_matrix() - return hash((Str, G)) ^ hash(Str) ^ hash(G) - def generator_matrix(self, encoder_name=None, **kwargs): r""" Returns a generator matrix of ``self``. @@ -3267,339 +2487,6 @@ def generator_matrix(self): return g -class LinearCodeSystematicEncoder(Encoder): - r""" - Encoder based on a generator matrix in systematic form for Linear codes. - - To encode an element of its message space, this encoder first builds a - generator matrix in systematic form. What is called systematic form here - is the reduced row echelon form of a matrix, which is not necessarily - `[I \vert H]`, where `I` is the identity block and `H` the parity block. - One can refer to :meth:`LinearCodeSystematicEncoder.generator_matrix` - for a concrete example. - Once such a matrix has been computed, it is used to encode any message - into a codeword. - - This encoder can also serve as the default encoder of a code defined by a - parity check matrix: if the :class:`LinearCodeSystematicEncoder` detects - that it is the default encoder, it computes a generator matrix as the - reduced row echelon form of the right kernel of the parity check matrix. - - INPUT: - - - ``code`` -- The associated code of this encoder. - - - ``systematic_positions`` -- (default: ``None``) the positions in codewords that - should correspond to the message symbols. A list of `k` distinct integers in - the range 0 to `n-1` where `n` is the length of the code and `k` its - dimension. The 0th symbol of a message will then be at position - ``systematic_positions[0]``, the 1st index at position - ``systematic_positions[1]``, etc. A ``ValueError`` is raised at - construction time if the supplied indices do not form an information set. - - EXAMPLES: - - The following demonstrates the basic usage of :class:`LinearCodeSystematicEncoder`:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0,0],\ - [1,0,0,1,1,0,0,0],\ - [0,1,0,1,0,1,0,0],\ - [1,1,0,1,0,0,1,1]]) - sage: C = LinearCode(G) - sage: E = codes.encoders.LinearCodeSystematicEncoder(C) - sage: E.generator_matrix() - [1 0 0 0 0 1 1 1] - [0 1 0 0 1 0 1 1] - [0 0 1 0 1 1 0 0] - [0 0 0 1 1 1 1 1] - sage: E2 = codes.encoders.LinearCodeSystematicEncoder(C, systematic_positions=[5,4,3,2]) - sage: E2.generator_matrix() - [1 0 0 0 0 1 1 1] - [0 1 0 0 1 0 1 1] - [1 1 0 1 0 0 1 1] - [1 1 1 0 0 0 0 0] - - An error is raised if one specifies systematic positions which do not form - an information set:: - - sage: E3 = codes.encoders.LinearCodeSystematicEncoder(C, systematic_positions=[0,1,6,7]) - Traceback (most recent call last): - ... - ValueError: systematic_positions are not an information set - - - We exemplify how to use :class:`LinearCodeSystematicEncoder` as the default - encoder. The following class is the dual of the repetition code:: - - sage: class DualRepetitionCode(sage.coding.linear_code.AbstractLinearCode): - ....: def __init__(self, field, length): - ....: sage.coding.linear_code.AbstractLinearCode.__init__(self,field, length, "Systematic", "Syndrome") - ....: - ....: def parity_check_matrix(self): - ....: return Matrix(self.base_field(), [1]*self.length()) - ....: - ....: def _repr_(self): - ....: return "Dual of the [%d, 1] Repetition Code over GF(%s)" % (self.length(), self.base_field().cardinality()) - ....: - sage: DualRepetitionCode(GF(3), 5).generator_matrix() - [1 0 0 0 2] - [0 1 0 0 2] - [0 0 1 0 2] - [0 0 0 1 2] - - - An exception is thrown if :class:`LinearCodeSystematicEncoder` is the default encoder but no - parity check matrix has been specified for the code:: - - sage: class BadCodeFamily(sage.coding.linear_code.AbstractLinearCode): - ....: def __init__(self, field, length): - ....: sage.coding.linear_code.AbstractLinearCode.__init__(self, field, length, "Systematic", "Syndrome") - ....: - ....: def _repr_(self): - ....: return "I am a badly defined code" - ....: - sage: BadCodeFamily(GF(3), 5).generator_matrix() - Traceback (most recent call last): - ... - ValueError: a parity check matrix must be specified if LinearCodeSystematicEncoder is the default encoder - """ - - def __init__(self, code, systematic_positions=None): - r""" - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: E = codes.encoders.LinearCodeSystematicEncoder(C) - sage: E - Systematic encoder for [7, 4] linear code over GF(2) - """ - super(LinearCodeSystematicEncoder, self).__init__(code) - self._systematic_positions = tuple(systematic_positions) if systematic_positions else None - if systematic_positions: - # Test that systematic_positions consists of integers in the right - # range. We test that len(systematic_positions) = code.dimension() - # in self.generator_matrix() to avoid possible infinite recursion. - if (not all( e in ZZ and e >= 0 and e < code.length() for e in systematic_positions)) \ - or len(systematic_positions) != len(set(systematic_positions)): - raise ValueError("systematic positions must be a tuple of distinct integers in the range 0 to n-1 where n is the length of the code") - # Test that the systematic positions are an information set - self.generator_matrix() - - - def __eq__(self, other): - r""" - Tests equality between LinearCodeSystematicEncoder objects. - - EXAMPLES:: - - sage: G = Matrix(GF(3), [[1,0,0,1,0,1,0,1,2],[0,1,0,2,2,0,1,1,0],[0,0,1,0,2,2,2,1,2]]) - sage: E1 = codes.encoders.LinearCodeSystematicEncoder(LinearCode(G)) - sage: E2 = codes.encoders.LinearCodeSystematicEncoder(LinearCode(G)) - sage: E1 == E2 - True - sage: E1.systematic_positions() - (0, 1, 2) - sage: E3 = codes.encoders.LinearCodeSystematicEncoder(LinearCode(G), systematic_positions=(2,5,6)) - sage: E3.systematic_positions() - (2, 5, 6) - sage: E1 == E3 - False - """ - return isinstance(other, LinearCodeSystematicEncoder)\ - and self.code() == other.code()\ - and self.systematic_positions() == other.systematic_positions() - - def _repr_(self): - r""" - Return a string representation of ``self``. - - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: E = codes.encoders.LinearCodeSystematicEncoder(C) - sage: E - Systematic encoder for [7, 4] linear code over GF(2) - """ - return "Systematic encoder for %s" % self.code() - - def _latex_(self): - r""" - Return a latex representation of ``self``. - - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: E = codes.encoders.LinearCodeSystematicEncoder(C) - sage: latex(E) - \textnormal{Systematic encoder for }[7, 4]\textnormal{ Linear code over }\Bold{F}_{2} - """ - return "\\textnormal{Systematic encoder for }%s" % self.code()._latex_() - - @cached_method - def generator_matrix(self): - r""" - Returns a generator matrix in systematic form of the associated code of ``self``. - - Systematic form here means that a subsets of the columns of the matrix - forms the identity matrix. - - .. NOTE:: - - The matrix returned by this method will not necessarily be `[I \vert H]`, where `I` - is the identity block and `H` the parity block. If one wants to know which columns - create the identity block, one can call :meth:`systematic_positions` - - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],\ - [1,0,0,1,1,0,0],\ - [0,1,0,1,0,1,0],\ - [1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: E = codes.encoders.LinearCodeSystematicEncoder(C) - sage: E.generator_matrix() - [1 0 0 0 0 1 1] - [0 1 0 0 1 0 1] - [0 0 1 0 1 1 0] - [0 0 0 1 1 1 1] - - We can ask for different systematic positions:: - - sage: E2 = codes.encoders.LinearCodeSystematicEncoder(C, systematic_positions=[5,4,3,2]) - sage: E2.generator_matrix() - [1 0 0 0 0 1 1] - [0 1 0 0 1 0 1] - [1 1 0 1 0 0 1] - [1 1 1 0 0 0 0] - - Another example where there is no generator matrix of the form `[I \vert H]`:: - - sage: G = Matrix(GF(2), [[1,1,0,0,1,0,1],\ - [1,1,0,0,1,0,0],\ - [0,0,1,0,0,1,0],\ - [0,0,1,0,1,0,1]]) - sage: C = LinearCode(G) - sage: E = codes.encoders.LinearCodeSystematicEncoder(C) - sage: E.generator_matrix() - [1 1 0 0 0 1 0] - [0 0 1 0 0 1 0] - [0 0 0 0 1 1 0] - [0 0 0 0 0 0 1] - """ - C = self.code() - # This if statement detects if this encoder is itself the default encoder. - # In this case, attempt building the generator matrix from the parity - # check matrix - if hasattr(self, "_use_pc_matrix"): - if self._use_pc_matrix == 1: - self._use_pc_matrix = 2 - return C.parity_check_matrix().right_kernel_matrix() - else: - raise ValueError("a parity check matrix must be specified if LinearCodeSystematicEncoder is the default encoder") - else: - self._use_pc_matrix = 1 - M = copy(C.generator_matrix()) - if not self._systematic_positions: - M.echelonize() - else: - k = M.nrows() # it is important that k is *not* computed as C.dimension() to avoid possible cyclic dependency - if len(self._systematic_positions) != k: - raise ValueError("systematic_positions must be a tuple of length equal to the dimension of the code") - # Permute the columns of M and bring to reduced row echelon formb - perm = self.systematic_permutation() - M.permute_columns(perm) - M.echelonize() - if M[:,:k].is_singular(): - raise ValueError("systematic_positions are not an information set") - M.permute_columns(perm.inverse()) - M.set_immutable() - return M - - def systematic_permutation(self): - r""" - Returns a permutation which would take the systematic positions into [0,..,k-1] - - EXAMPLES:: - - sage: C = LinearCode(matrix(GF(2), [[1,0,0,0,1,1,0],\ - [0,1,0,1,0,1,0],\ - [0,0,0,0,0,0,1]])) - sage: E = codes.encoders.LinearCodeSystematicEncoder(C) - sage: E.systematic_positions() - (0, 1, 6) - sage: E.systematic_permutation() - [1, 2, 7, 3, 4, 5, 6] - """ - n = self.code().length() - systematic_positions = self.systematic_positions() - k = len(systematic_positions) - lp = [ None ]*n - for (i,j) in zip(range(k), systematic_positions): - lp[i] = j - j = k - set_sys_pos = set(systematic_positions) - for i in range(n): - if not i in set_sys_pos: - lp[j] = i - j += 1 - from sage.combinat.permutation import Permutation - return Permutation([1 + e for e in lp]) - - def systematic_positions(self): - r""" - Returns a tuple containing the indices of the columns which form an - identity matrix when the generator matrix is in systematic form. - - EXAMPLES:: - - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],\ - [1,0,0,1,1,0,0],\ - [0,1,0,1,0,1,0],\ - [1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: E = codes.encoders.LinearCodeSystematicEncoder(C) - sage: E.systematic_positions() - (0, 1, 2, 3) - - We take another matrix with a less nice shape:: - - sage: G = Matrix(GF(2), [[1,1,0,0,1,0,1],\ - [1,1,0,0,1,0,0],\ - [0,0,1,0,0,1,0],\ - [0,0,1,0,1,0,1]]) - sage: C = LinearCode(G) - sage: E = codes.encoders.LinearCodeSystematicEncoder(C) - sage: E.systematic_positions() - (0, 2, 4, 6) - - The systematic positions correspond to the positions which carry information in a codeword:: - - sage: MS = E.message_space() - sage: m = MS.random_element() - sage: c = m * E.generator_matrix() - sage: pos = E.systematic_positions() - sage: info = MS([c[i] for i in pos]) - sage: m == info - True - - When constructing a systematic encoder with specific systematic - positions, then it is guaranteed that this method returns exactly those - positions (even if another choice might also be systematic):: - - sage: G = Matrix(GF(2), [[1,0,0,0],\ - [0,1,0,0],\ - [0,0,1,1]]) - sage: C = LinearCode(G) - sage: E = codes.encoders.LinearCodeSystematicEncoder(C, systematic_positions=[0,1,3]) - sage: E.systematic_positions() - (0, 1, 3) - """ - return self._systematic_positions if self._systematic_positions else self.generator_matrix().pivots() - - ####################### decoders ############################### class LinearCodeSyndromeDecoder(Decoder): @@ -3713,7 +2600,7 @@ class LinearCodeSyndromeDecoder(Decoder): sage: D = C.decoder("Syndrome", maximum_error_weight = 5) # long time sage: D.decoder_type() # long time {'complete', 'hard-decision', 'might-error'} - sage: D.decoding_radius() # long time + sage: D.decoding_radius() # long time 4 In that case, the decoder might still return an unexpected codeword, but diff --git a/src/sage/coding/linear_code_no_metric.py b/src/sage/coding/linear_code_no_metric.py new file mode 100644 index 00000000000..d5e1fdf1ec0 --- /dev/null +++ b/src/sage/coding/linear_code_no_metric.py @@ -0,0 +1,1360 @@ +r""" +Generic structures for linear codes of any metirc + +Class supporting methods available for linear codes over any metric (Hamming, +rank). +""" + +from copy import copy + +from sage.coding.abstract_code import AbstractCode +from sage.modules.module import Module +from sage.categories.modules import Modules +from sage.modules.free_module import VectorSpace +from sage.coding.encoder import Encoder +from sage.misc.cachefunc import cached_method +from sage.matrix.matrix_space import MatrixSpace +from sage.rings.integer import Integer +from sage.structure.parent import Parent +from sage.rings.integer_ring import ZZ + +class AbstractLinearCodeNoMetric(AbstractCode, Module): + r""" + Abstract class for linear codes of any metric. + + This class contains all the methods that can be used on any linear code + of any metric. Every abstract class of linear codes over some metric (e.g. + abstract class for linear codes over the Hamming metric, + :class:`sage.coding.linear_code.AbstractLinearCode`) should inherit from + this class. + + To create a new class of linear codes over some metrics, you need to: + + - inherit from AbstractLinearCodeNoMetric + + - call AbstractCode ``__init__`` method in the subclass constructor. + Example: ``super(SubclassName, self).__init__(length, "EncoderName", + "DecoderName", "metric")``. + + - add the following two lines on the class level:: + + _registered_encoders = {} + _registered_decoders = {} + + + - fill the dictionary of its encoders in ``sage.coding.__init__.py`` file. + Example: I want to link the encoder ``MyEncoderClass`` to ``MyNewCodeClass`` + under the name ``MyEncoderName``. + All I need to do is to write this line in the ``__init__.py`` file: + ``MyNewCodeClass._registered_encoders["NameOfMyEncoder"] = MyEncoderClass`` + and all instances of ``MyNewCodeClass`` will be able to use instances of + ``MyEncoderClass``. + + - fill the dictionary of its decoders in ``sage.coding.__init__`` file. + Example: I want to link the encoder ``MyDecoderClass`` to ``MyNewCodeClass`` + under the name ``MyDecoderName``. + All I need to do is to write this line in the ``__init__.py`` file: + ``MyNewCodeClass._registered_decoders["NameOfMyDecoder"] = MyDecoderClass`` + and all instances of ``MyNewCodeClass`` will be able to use instances of + ``MyDecoderClass``. + + - create a generic constructor representative of you abstract class. This + generic constructor is a class for unstructured linear codes given by some + generator and considered over the given metric. A good example of this is + :class:`sage.coding.linear_code.LinearCode`, which is a generic constructor + for :class:`sage.coding.linear_code.AbstractLinearCode`, an abstract class + for linear codes over the Hamming metric. + + - set a private field in the ``__init__`` method specifying the generic + constructor, (e.g. ``MyAbstractCode._generic_constructor = MyCode``) + + + It is assumed that the subclass codes are linear over ``base_field``. To + test this, it is recommended to add a test suite test to the generic + constructor. To do this, create a representative of your code `C` and run + ``TestSuite(C).run()``. A good example of this is in + :class:`sage.coding.linear_code.LinearCode`. + + + As AbstractLinearCodeNoMetric is not designed to be implemented, it does not + have any representation methods. You should implement ``_repr_`` and ``_latex_`` + methods in the subclass. + + .. WARNING:: + + A lot of methods of the abstract class rely on the knowledge of a generator matrix. + It is thus strongly recommended to set an encoder with a generator matrix implemented + as a default encoder. + + TESTS: + + If the name of the default decoder is not known by the class, it will raise + a exception:: + + sage: from sage.coding.linear_code_no_metric import AbstractLinearCodeNoMetric + sage: class MyCodeFamily(AbstractLinearCodeNoMetric): + ....: def __init__(self, field, length, dimension, generator_matrix): + ....: AbstractLinearCodeNoMetric.__init__(self, field, length, "Systematic", "Fail", "MyMetric") + ....: self._dimension = dimension + ....: self._generator_matrix = generator_matrix + ....: def generator_matrix(self): + ....: return self._generator_matrix + ....: def _repr_(self): + ....: return "[%d, %d] dummy code over GF(%s)" % (self.length(), self.dimension(), self.base_field().cardinality()) + + sage: generator_matrix = matrix(GF(17), 5, 10, + ....: {(i,i):1 for i in range(5)}) + sage: C = MyCodeFamily(GF(17), 10, 5, generator_matrix) + Traceback (most recent call last): + ... + ValueError: You must set a valid decoder as default decoder for this code, by filling in the dictionary of registered decoders + + If the name of the default encoder is not known by the class, it will raise + an exception:: + + sage: class MyCodeFamily2(sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric): + ....: def __init__(self, field, length, dimension, generator_matrix): + ....: sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric.__init__(self, field, length, "Fail", "Syndrome", "MyMetric") + ....: self._dimension = dimension + ....: self._generator_matrix = generator_matrix + ....: def generator_matrix(self): + ....: return self._generator_matrix + ....: def _repr_(self): + ....: return "[%d, %d] dummy code over GF(%s)" % (self.length(), self.dimension(), self.base_field().cardinality()) + + sage: C = MyCodeFamily2(GF(17), 10, 5, generator_matrix) + Traceback (most recent call last): + ... + ValueError: You must set a valid encoder as default encoder for this code, by filling in the dictionary of registered encoders + + A ring instead of a field:: + + sage: MyCodeFamily2(IntegerModRing(4), 4, 4, matrix.ones(4)) + Traceback (most recent call last): + ... + ValueError: 'base_field' must be a field (and Ring of integers modulo 4 is not one) + """ + _registered_encoders = {} + _registered_decoders = {} + + def __init__(self, base_field, length, default_encoder_name, default_decoder_name, metric='Hamming'): + """ + Initializes mandatory parameters that any linear code shares. + + This method only exists for inheritance purposes as it initializes + parameters that need to be known by every linear code. The class + :class:`sage.coding.linear_code_no_metric.AbstractLinearCodeNoMetric` + should never be directly instantiated. + + INPUT: + + - ``base_field`` -- the base field of ``self`` + + - ``length`` -- the length of ``self`` (a Python int or a Sage Integer, must be > 0) + + - ``default_encoder_name`` -- the name of the default encoder of ``self`` + + - ``default_decoder_name`` -- the name of the default decoder of ``self`` + + - ``metric`` -- (default: ``Hamming``) the metric of ``self`` + + EXAMPLES: + + sage: from sage.coding.linear_code_no_metric import AbstractLinearCodeNoMetric + sage: from sage.coding.linear_code import LinearCodeSyndromeDecoder + sage: class MyLinearCode(AbstractLinearCodeNoMetric): + ....: def __init__(self, field, length, dimension, generator_matrix): + ....: self._registered_decoders['Syndrome'] = LinearCodeSyndromeDecoder + ....: AbstractLinearCodeNoMetric.__init__(self, field, length, "Systematic", "Syndrome") + ....: self._dimension = dimension + ....: self._generator_matrix = generator_matrix + ....: def generator_matrix(self): + ....: return self._generator_matrix + ....: def _repr_(self): + ....: return "[%d, %d] dummy code over GF(%s)" % (self.length(), self.dimension(), self.base_field().cardinality()) + sage: C = MyLinearCode(GF(2), 1, 1, matrix(GF(2), [1])) + """ + + self._registered_encoders['Systematic'] = LinearCodeSystematicEncoder + + if not base_field.is_field(): + raise ValueError("'base_field' must be a field (and {} is not one)".format(base_field)) + if not default_encoder_name in self._registered_encoders: + raise ValueError("You must set a valid encoder as default encoder for this code, by filling in the dictionary of registered encoders") + if not default_decoder_name in self._registered_decoders: + raise ValueError("You must set a valid decoder as default decoder for this code, by filling in the dictionary of registered decoders") + + #if not self.dimension() <= length: + # raise ValueError("The dimension of the code can be at most its length, {}".format(length)) + + super(AbstractLinearCodeNoMetric, self).__init__(length, default_encoder_name, default_decoder_name, metric) + cat = Modules(base_field).FiniteDimensional().WithBasis().Finite() + facade_for = VectorSpace(base_field, self._length) + self.Element = type(facade_for.an_element()) #for when we made this a non-facade parent + Parent.__init__(self, base=base_field, facade=facade_for, category=cat) + + def base_field(self): + r""" + Return the base field of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: C.base_field() + Finite Field of size 2 + """ + return self.base_ring() + + def ambient_space(self): + r""" + Returns the ambient vector space of ``self``. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: C.ambient_space() + Vector space of dimension 7 over Finite Field of size 2 + """ + return VectorSpace(self.base_ring(),self.length()) + + def generator_matrix(self, encoder_name=None, **kwargs): + r""" + Returns a generator matrix of ``self``. + + INPUT: + + - ``encoder_name`` -- (default: ``None``) name of the encoder which will be + used to compute the generator matrix. The default encoder of ``self`` + will be used if default value is kept. + + - ``kwargs`` -- all additional arguments are forwarded to the construction of the + encoder that is used. + + EXAMPLES:: + + sage: G = matrix(GF(3),2,[1,-1,1,-1,1,1]) + sage: code = LinearCode(G) + sage: code.generator_matrix() + [1 2 1] + [2 1 1] + """ + E = self.encoder(encoder_name, **kwargs) + return E.generator_matrix() + + def __eq__(self, other): + r""" + Tests equality between two linear codes. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C1 = LinearCode(G) + sage: C1 == 5 + False + sage: C2 = LinearCode(G) + sage: C1 == C2 + True + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,1,1]]) + sage: C2 = LinearCode(G) + sage: C1 == C2 + False + sage: G = Matrix(GF(3), [[1,2,1,0,0,0,0]]) + sage: C3 = LinearCode(G) + sage: C1 == C3 + False + """ + # Fail without computing the generator matrix if possible: + if not (isinstance(other, AbstractLinearCodeNoMetric)\ + and self.length() == other.length()\ + and self.dimension() == other.dimension()\ + and self.base_ring() == other.base_ring()): + return False + # Check that basis elements of `other` are all in `self.` + # Since we're over a field and since the dimensions match, the codes + # must be equal. + # This implementation may avoid linear algebra altogether, if `self` + # implements an efficient way to obtain a parity check matrix, and in + # the worst case does only one system solving. + for c in other.gens(): + if not (c in self): + return False + return True + + def __ne__(self, other): + r""" + Tests inequality of ``self`` and ``other``. + + This is a generic implementation, which returns the inverse of ``__eq__`` for self. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C1 = LinearCode(G) + sage: C2 = LinearCode(G) + sage: C1 != C2 + False + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,1,1]]) + sage: C2 = LinearCode(G) + sage: C1 != C2 + True + """ + return not self == other + + def dimension(self): + r""" + Returns the dimension of this code. + + EXAMPLES:: + + sage: G = matrix(GF(2),[[1,0,0],[1,1,0]]) + sage: C = LinearCode(G) + sage: C.dimension() + 2 + + TESTS: + + Check that :trac:`21156` is fixed:: + + sage: from sage.coding.linear_code import AbstractLinearCode + sage: from sage.coding.encoder import Encoder + sage: class MonkeyCode(AbstractLinearCode): + ....: _registered_encoders = {} + ....: _registered_decoders = {} + ....: def __init__(self): + ....: super(MonkeyCode, self).__init__(GF(5), 10, "Monkey", "Syndrome") + ....: + sage: class MonkeyEncoder(Encoder): + ....: def __init__(self, code): + ....: super(MonkeyEncoder, self).__init__(C) + ....: @cached_method + ....: def generator_matrix(self): + ....: G = identity_matrix(GF(5), 5).augment(matrix(GF(5), 5, 7)) + ....: return G + ....: + sage: MonkeyCode._registered_encoders["Monkey"] = MonkeyEncoder + sage: C = MonkeyCode() + sage: C.dimension() + 5 + """ + try: + return self._dimension + except AttributeError: + dimension = self.generator_matrix().nrows() + self._dimension = dimension + return self._dimension + + def cardinality(self): + r""" + Return the size of this code. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: C.cardinality() + 16 + sage: len(C) + 16 + """ + return self.base_ring().order()**self.dimension() + + __len__ = cardinality + + def rate(self): + r""" + Return the ratio of the number of information symbols to + the code length. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: C.rate() + 4/7 + """ + return self.dimension() / self.length() + + @cached_method + def gens(self): + r""" + Returns the generators of this code as a list of vectors. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: C.gens() + [(1, 0, 0, 0, 0, 1, 1), (0, 1, 0, 0, 1, 0, 1), (0, 0, 1, 0, 1, 1, 0), (0, 0, 0, 1, 1, 1, 1)] + """ + return self.generator_matrix().rows() + + def basis(self): + r""" + Returns a basis of ``self``. + + OUTPUT: + + - ``Sequence`` - an immutable sequence whose universe is ambient space of ``self``. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: C.basis() + [ + (1, 0, 0, 0, 0, 1, 1), + (0, 1, 0, 0, 1, 0, 1), + (0, 0, 1, 0, 1, 1, 0), + (0, 0, 0, 1, 1, 1, 1) + ] + sage: C.basis().universe() + Vector space of dimension 7 over Finite Field of size 2 + """ + gens = self.gens() + from sage.structure.sequence import Sequence + return Sequence(gens, universe=self.ambient_space(), check = False, immutable=True, cr=True) + + @cached_method + def parity_check_matrix(self): + r""" + Returns the parity check matrix of ``self``. + + The parity check matrix of a linear code `C` corresponds to the + generator matrix of the dual code of `C`. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: Cperp = C.dual_code() + sage: C; Cperp + [7, 4] Hamming Code over GF(2) + [7, 3] linear code over GF(2) + sage: C.generator_matrix() + [1 0 0 0 0 1 1] + [0 1 0 0 1 0 1] + [0 0 1 0 1 1 0] + [0 0 0 1 1 1 1] + sage: C.parity_check_matrix() + [1 0 1 0 1 0 1] + [0 1 1 0 0 1 1] + [0 0 0 1 1 1 1] + sage: Cperp.parity_check_matrix() + [1 0 0 0 0 1 1] + [0 1 0 0 1 0 1] + [0 0 1 0 1 1 0] + [0 0 0 1 1 1 1] + sage: Cperp.generator_matrix() + [1 0 1 0 1 0 1] + [0 1 1 0 0 1 1] + [0 0 0 1 1 1 1] + """ + G = self.generator_matrix() + H = G.right_kernel() + M = H.basis_matrix() + M.set_immutable() + return M + + def syndrome(self, r): + r""" + Returns the syndrome of ``r``. + + The syndrome of ``r`` is the result of `H \times r` where `H` is + the parity check matrix of ``self``. If ``r`` belongs to ``self``, + its syndrome equals to the zero vector. + + INPUT: + + - ``r`` -- a vector of the same length as ``self`` + + OUTPUT: + + - a column vector + + EXAMPLES:: + + sage: MS = MatrixSpace(GF(2),4,7) + sage: G = MS([[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: r = vector(GF(2), (1,0,1,0,1,0,1)) + sage: r in C + True + sage: C.syndrome(r) + (0, 0, 0) + + If ``r`` is not a codeword, its syndrome is not equal to zero:: + + sage: r = vector(GF(2), (1,0,1,0,1,1,1)) + sage: r in C + False + sage: C.syndrome(r) + (0, 1, 1) + + Syndrome computation works fine on bigger fields:: + + sage: C = codes.random_linear_code(GF(59), 12, 4) + sage: c = C.random_element() + sage: C.syndrome(c) + (0, 0, 0, 0, 0, 0, 0, 0) + """ + return self.parity_check_matrix()*r + + def __contains__(self, v): + r""" + Returns True if `v` can be coerced into ``self``. Otherwise, returns False. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: vector((1, 0, 0, 0, 0, 1, 1)) in C # indirect doctest + True + sage: vector((1, 0, 0, 0, 2, 1, 1)) in C # indirect doctest + True + sage: vector((1, 0, 0, 0, 0, 1/2, 1)) in C # indirect doctest + False + """ + if not v in self.ambient_space() or len(v) != self.length(): + return False + return self.syndrome(v) == 0 + + def systematic_generator_matrix(self, systematic_positions=None): + """ + Return a systematic generator matrix of the code. + + A generator matrix of a code is called systematic if it contains + a set of columns forming an identity matrix. + + INPUT: + + - ``systematic_positions`` -- (default: ``None``) if supplied, the set + of systematic positions in the systematic generator matrix. See the + documentation for :class:`LinearCodeSystematicEncoder` details. + + EXAMPLES:: + + sage: G = matrix(GF(3), [[ 1, 2, 1, 0],\ + [ 2, 1, 1, 1]]) + sage: C = LinearCode(G) + sage: C.generator_matrix() + [1 2 1 0] + [2 1 1 1] + sage: C.systematic_generator_matrix() + [1 2 0 1] + [0 0 1 2] + + Specific systematic positions can also be requested: + + sage: C.systematic_generator_matrix(systematic_positions=[3,2]) + [1 2 0 1] + [1 2 1 0] + """ + systematic_positions = tuple(systematic_positions) if systematic_positions else None + return self.encoder("Systematic", systematic_positions=systematic_positions).generator_matrix() + + def standard_form(self, return_permutation=True): + r""" + Returns a linear code which is permutation-equivalent to ``self`` and + admits a generator matrix in standard form. + + A generator matrix is in standard form if it is of the form `[I \vert + A]`, where `I` is the `k \times k` identity matrix. Any code admits a + generator matrix in systematic form, i.e. where a subset of the columns + form the identity matrix, but one might need to permute columns to allow + the identity matrix to be leading. + + INPUT: + + - ``return_permutation`` -- (default: ``True``) if ``True``, the column + permutation which brings ``self`` into the returned code is also + returned. + + OUTPUT: + + - A :class:`LinearCode` whose :meth:`systematic_generator_matrix` is + guaranteed to be of the form `[I \vert A]`. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: C.generator_matrix() + [1 0 0 0 0 1 1] + [0 1 0 0 1 0 1] + [0 0 1 0 1 1 0] + [0 0 0 1 1 1 1] + sage: Cs,p = C.standard_form() + sage: p + [] + sage: Cs is C + True + sage: C = LinearCode(matrix(GF(2), [[1,0,0,0,1,1,0],\ + [0,1,0,1,0,1,0],\ + [0,0,0,0,0,0,1]])) + sage: Cs, p = C.standard_form() + sage: p + [1, 2, 7, 3, 4, 5, 6] + sage: Cs.generator_matrix() + [1 0 0 0 0 1 1] + [0 1 0 0 1 0 1] + [0 0 1 0 0 0 0] + """ + E = self.encoder("Systematic") + if E.systematic_positions() == tuple(range(self.dimension())): + from sage.combinat.permutation import Permutation + return self, Permutation([]) + else: + perm = E.systematic_permutation() + return self.permuted_code(perm), perm + + def redundancy_matrix(self): + r""" + Returns the non-identity columns of a systematic generator matrix for + ``self``. + + A systematic generator matrix is a generator matrix such that a subset + of its columns forms the identity matrix. This method returns the + remaining part of the matrix. + + For any given code, there can be many systematic generator matrices + (depending on which positions should form the identity). This method + will use the matrix returned by + :meth:`AbstractLinearCode.systematic_generator_matrix`. + + OUTPUT: + + - An `k \times (n-k)` matrix. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: C.generator_matrix() + [1 0 0 0 0 1 1] + [0 1 0 0 1 0 1] + [0 0 1 0 1 1 0] + [0 0 0 1 1 1 1] + sage: C.redundancy_matrix() + [0 1 1] + [1 0 1] + [1 1 0] + [1 1 1] + sage: C = LinearCode(matrix(GF(3),2,[1,2,0,\ + 2,1,1])) + sage: C.systematic_generator_matrix() + [1 2 0] + [0 0 1] + sage: C.redundancy_matrix() + [2] + [0] + """ + E = self.encoder("Systematic") + G = E.generator_matrix() + return G.delete_columns(E.systematic_positions()) + + @cached_method + def information_set(self): + """ + Return an information set of the code. + + Return value of this method is cached. + + A set of column positions of a generator matrix of a code + is called an information set if the corresponding columns + form a square matrix of full rank. + + OUTPUT: + + - Information set of a systematic generator matrix of the code. + + EXAMPLES:: + + sage: G = matrix(GF(3),2,[1,2,0,\ + 2,1,1]) + sage: code = LinearCode(G) + sage: code.systematic_generator_matrix() + [1 2 0] + [0 0 1] + sage: code.information_set() + (0, 2) + """ + return self.encoder("Systematic").systematic_positions() + + def is_information_set(self, positions): + """ + Return whether the given positions form an information set. + + INPUT: + + - A list of positions, i.e. integers in the range 0 to `n-1` where `n` + is the length of `self`. + + OUTPUT: + + - A boolean indicating whether the positions form an information set. + + + EXAMPLES:: + + sage: G = matrix(GF(3),2,[1,2,0,\ + 2,1,1]) + sage: code = LinearCode(G) + sage: code.is_information_set([0,1]) + False + sage: code.is_information_set([0,2]) + True + """ + try: + self.encoder("Systematic", systematic_positions=tuple(positions)) + return True + except ValueError: + return False + + def __iter__(self): + """ + Return an iterator over the elements of this linear code. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: [list(c) for c in C if c.hamming_weight() < 4] + [[0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 1, 1], + [0, 1, 0, 0, 1, 0, 1], [0, 0, 1, 0, 1, 1, 0], + [1, 1, 1, 0, 0, 0, 0], [1, 0, 0, 1, 1, 0, 0], + [0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 1, 0, 0, 1]] + + TESTS:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: L = list(C) + sage: L[10].is_immutable() + True + + """ + from sage.modules.finite_submodule_iter import \ + FiniteFieldsubspace_iterator + return FiniteFieldsubspace_iterator(self.generator_matrix(), immutable=True) + + def __getitem__(self, i): + r""" + Returns the `i`-th codeword of this code. + + The implementation of this depends on the implementation of the + :meth:`.__iter__` method. + + The implementation is as follows. Suppose that: + + - the primitive element of the base_ring of this code is `a`, + - the prime subfield is `p`, + - the field has order `p^m`, + - the code has dimension `k`, + - and the generator matrix is `G`. + + Then the :meth:`.__iter__` method returns the elements in this order: + + 1. first, the following ordered list is returned: + ``[i*a^0 * G[0] for i in range(p)]`` + 2. Next, the following ordered list is returned: + ``[i*a^0 * G[0] + a^1*G[0] for i in range(p)]`` + 3. This continues till we get + ``[(i*a^0 +(p-1)*a^1 +...+ (p-1)*a^(m-1))*G[0] for i in range(p)]`` + 4. Then, we move to G[1]: + ``[i*a^0 * G[0] + a^0*G[1] for i in range(p)]``, + and so on. + Hence the `i`-th element can be obtained by the p-adic expansion + of `i` as ``[i_0, i_1, ...,i_{m-1}, i_m, i_{m+1}, ..., i_{km-1}].`` + The element that is generated is: + + .. MATH:: + + \begin{aligned} + & (i_0 a^0 + i_1 a^1 + \cdots + i_{m-1} a^{m-1}) G[0] + \\ + & (i_m a^0 + i_{m+1} a^1 + \cdots + i_{2m-1} a^{m-1}) G[1] + \\ + & \vdots\\ + & (i_{(k-1)m} a^0 + \cdots + i_{km-1} a^{m-1}) G[k-1] + \end{aligned} + + EXAMPLES:: + + sage: G = Matrix(GF(3), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: C[24] + (2, 2, 0, 1, 2, 2, 0) + sage: C[24] == C.list()[24] + True + + TESTS:: + + sage: C = random_matrix(GF(25,'a'), 2, 7).row_space() + sage: C = LinearCode(C.basis_matrix()) + sage: Clist = C.list() + sage: all(C[i] == Clist[i] for i in range(len(C))) + True + + Check that only the indices less than the size of the code are + allowed:: + + sage: C[25**2] + Traceback (most recent call last): + ... + IndexError: The value of the index 'i' (=625) must be between + 0 and 'q^k -1' (=624), inclusive, where 'q' is the size of the + base field and 'k' is the dimension of the code. + + Check that codewords are immutable. See :trac:`16338`:: + + sage: C[0].is_immutable() + True + + """ + # IMPORTANT: If the __iter__() function implementation is changed + # then the implementation here must also be changed so that + # list(self)[i] and self[i] both return the same element. + + F = self.base_ring() + maxindex = F.order()**self.dimension()-1 + if i < 0 or i > maxindex: + raise IndexError("The value of the index 'i' (={}) must be between " + "0 and 'q^k -1' (={}), inclusive, where 'q' is " + "the size of the base field and 'k' is the " + "dimension of the code.".format(i, maxindex)) + + a = F.primitive_element() + m = F.degree() + p = F.prime_subfield().order() + A = [a ** k for k in range(m)] + G = self.generator_matrix() + N = self.dimension()*F.degree() # the total length of p-adic vector + ivec = Integer(i).digits(p, padto=N) + + codeword = 0 + row = 0 + for g in G: + codeword += sum(ivec[j+row*m]*A[j] for j in range(m)) * g + row += 1 + + # The codewords for a specific code can not change. So, we set them + # to be immutable. + codeword.set_immutable() + return codeword + + def __hash__(self): + r""" + Returns the hash value of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: hash(C) #random + 9015017528451745710 + + If ``C1`` and ``C2`` are two codes which only differ by the + coefficients of their generator matrices, their hashes are + different (we check that the bug found in :trac:`18813` is + fixed):: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C1 = LinearCode(G) + sage: G = Matrix(GF(2), [[1,0,0,1,0,1,0],[0,1,0,0,1,0,0],[0,0,1,1,0,1,0],[0,0,0,0,0,0,1]]) + sage: C2 = LinearCode(G) + sage: hash(C1) != hash(C2) + True + """ + Str = str(self) + G = self.generator_matrix() + return hash((Str, G)) ^ hash(Str) ^ hash(G) + + def is_subcode(self, other): + """ + Returns ``True`` if ``self`` is a subcode of ``other``. + + EXAMPLES:: + + sage: C1 = codes.HammingCode(GF(2), 3) + sage: G1 = C1.generator_matrix() + sage: G2 = G1.matrix_from_rows([0,1,2]) + sage: C2 = LinearCode(G2) + sage: C2.is_subcode(C1) + True + sage: C1.is_subcode(C2) + False + sage: C3 = C1.extended_code() + sage: C1.is_subcode(C3) + False + sage: C4 = C1.punctured([1]) + sage: C4.is_subcode(C1) + False + sage: C5 = C1.shortened([1]) + sage: C5.is_subcode(C1) + False + sage: C1 = codes.HammingCode(GF(9,"z"), 3) + sage: G1 = C1.generator_matrix() + sage: G2 = G1.matrix_from_rows([0,1,2]) + sage: C2 = LinearCode(G2) + sage: C2.is_subcode(C1) + True + """ + G = self.generator_matrix() + for r in G.rows(): + if not(r in other): + return False + return True + + def is_permutation_automorphism(self,g): + r""" + Returns `1` if `g` is an element of `S_n` (`n` = length of self) and + if `g` is an automorphism of self. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(3), 3) + sage: g = SymmetricGroup(13).random_element() + sage: C.is_permutation_automorphism(g) + 0 + sage: MS = MatrixSpace(GF(2),4,8) + sage: G = MS([[1,0,0,0,1,1,1,0],[0,1,1,1,0,0,0,0],[0,0,0,0,0,0,0,1],[0,0,0,0,0,1,0,0]]) + sage: C = LinearCode(G) + sage: S8 = SymmetricGroup(8) + sage: g = S8("(2,3)") + sage: C.is_permutation_automorphism(g) + 1 + sage: g = S8("(1,2,3,4)") + sage: C.is_permutation_automorphism(g) + 0 + """ + basis = self.generator_matrix().rows() + H = self.parity_check_matrix() + V = H.column_space() + HGm = H*g.matrix() + for c in basis: + if HGm*c != V(0): + return False + return True + + def permuted_code(self, p): + r""" + Returns the permuted code, which is equivalent to ``self`` via the + column permutation ``p``. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: G = C.permutation_automorphism_group(); G + Permutation Group with generators [(4,5)(6,7), (4,6)(5,7), (2,3)(6,7), (2,4)(3,5), (1,2)(5,6)] + sage: g = G("(2,3)(6,7)") + sage: Cg = C.permuted_code(g) + sage: Cg + [7, 4] linear code over GF(2) + sage: C.generator_matrix() == Cg.systematic_generator_matrix() + True + """ + if not hasattr(self, "_generic_constructor"): + raise NotImplementedException("Generic constructor not set for the class of codes") + G = copy(self.generator_matrix()) + G.permute_columns(p) + return self._generic_constructor(G) + + def dual_code(self): + r""" + Returns the dual code `C^{\perp}` of the code `C`, + + .. MATH:: + + C^{\perp} = \{ v \in V\ |\ v\cdot c = 0,\ \forall c \in C \}. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: C.dual_code() + [7, 3] linear code over GF(2) + sage: C = codes.HammingCode(GF(4, 'a'), 3) + sage: C.dual_code() + [21, 3] linear code over GF(4) + """ + if not hasattr(self, "_generic_constructor"): + raise NotImplementedException("Generic constructor not set for the class of codes") + return self._generic_constructor(self.parity_check_matrix()) + + def is_self_dual(self): + """ + Returns ``True`` if the code is self-dual (in the usual Hamming inner + product) and ``False`` otherwise. + + EXAMPLES:: + + sage: C = codes.GolayCode(GF(2)) + sage: C.is_self_dual() + True + sage: C = codes.HammingCode(GF(2), 3) + sage: C.is_self_dual() + False + """ + return self == self.dual_code() + + def is_self_orthogonal(self): + """ + Returns ``True`` if this code is self-orthogonal and ``False`` + otherwise. + + A code is self-orthogonal if it is a subcode of its dual. + + EXAMPLES:: + + sage: C = codes.GolayCode(GF(2)) + sage: C.is_self_orthogonal() + True + sage: C = codes.HammingCode(GF(2), 3) + sage: C.is_self_orthogonal() + False + sage: C = codes.QuasiQuadraticResidueCode(11) # optional - gap_packages (Guava package) + sage: C.is_self_orthogonal() # optional - gap_packages (Guava package) + True + """ + return self.is_subcode(self.dual_code()) + + @cached_method + def zero(self): + r""" + Returns the zero vector of ``self``. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(2), 3) + sage: C.zero() + (0, 0, 0, 0, 0, 0, 0) + sage: C.sum(()) # indirect doctest + (0, 0, 0, 0, 0, 0, 0) + sage: C.sum((C.gens())) # indirect doctest + (1, 1, 1, 1, 1, 1, 1) + """ + return self.ambient_space().zero() + + +####################### encoders ############################### + + +class LinearCodeSystematicEncoder(Encoder): + r""" + Encoder based on a generator matrix in systematic form for Linear codes. + + To encode an element of its message space, this encoder first builds a + generator matrix in systematic form. What is called systematic form here + is the reduced row echelon form of a matrix, which is not necessarily + `[I \vert H]`, where `I` is the identity block and `H` the parity block. + One can refer to :meth:`LinearCodeSystematicEncoder.generator_matrix` + for a concrete example. + Once such a matrix has been computed, it is used to encode any message + into a codeword. + + This encoder can also serve as the default encoder of a code defined by a + parity check matrix: if the :class:`LinearCodeSystematicEncoder` detects + that it is the default encoder, it computes a generator matrix as the + reduced row echelon form of the right kernel of the parity check matrix. + + INPUT: + + - ``code`` -- The associated code of this encoder. + + - ``systematic_positions`` -- (default: ``None``) the positions in codewords that + should correspond to the message symbols. A list of `k` distinct integers in + the range 0 to `n-1` where `n` is the length of the code and `k` its + dimension. The 0th symbol of a message will then be at position + ``systematic_positions[0]``, the 1st index at position + ``systematic_positions[1]``, etc. A ``ValueError`` is raised at + construction time if the supplied indices do not form an information set. + + EXAMPLES: + + The following demonstrates the basic usage of :class:`LinearCodeSystematicEncoder`:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0,0],\ + [1,0,0,1,1,0,0,0],\ + [0,1,0,1,0,1,0,0],\ + [1,1,0,1,0,0,1,1]]) + sage: C = LinearCode(G) + sage: E = codes.encoders.LinearCodeSystematicEncoder(C) + sage: E.generator_matrix() + [1 0 0 0 0 1 1 1] + [0 1 0 0 1 0 1 1] + [0 0 1 0 1 1 0 0] + [0 0 0 1 1 1 1 1] + sage: E2 = codes.encoders.LinearCodeSystematicEncoder(C, systematic_positions=[5,4,3,2]) + sage: E2.generator_matrix() + [1 0 0 0 0 1 1 1] + [0 1 0 0 1 0 1 1] + [1 1 0 1 0 0 1 1] + [1 1 1 0 0 0 0 0] + + An error is raised if one specifies systematic positions which do not form + an information set:: + + sage: E3 = codes.encoders.LinearCodeSystematicEncoder(C, systematic_positions=[0,1,6,7]) + Traceback (most recent call last): + ... + ValueError: systematic_positions are not an information set + + + We exemplify how to use :class:`LinearCodeSystematicEncoder` as the default + encoder. The following class is the dual of the repetition code:: + + sage: class DualRepetitionCode(sage.coding.linear_code.AbstractLinearCode): + ....: def __init__(self, field, length): + ....: sage.coding.linear_code.AbstractLinearCode.__init__(self,field, length, "Systematic", "Syndrome") + ....: + ....: def parity_check_matrix(self): + ....: return Matrix(self.base_field(), [1]*self.length()) + ....: + ....: def _repr_(self): + ....: return "Dual of the [%d, 1] Repetition Code over GF(%s)" % (self.length(), self.base_field().cardinality()) + ....: + sage: DualRepetitionCode(GF(3), 5).generator_matrix() + [1 0 0 0 2] + [0 1 0 0 2] + [0 0 1 0 2] + [0 0 0 1 2] + + + An exception is thrown if :class:`LinearCodeSystematicEncoder` is the default encoder but no + parity check matrix has been specified for the code:: + + sage: class BadCodeFamily(sage.coding.linear_code.AbstractLinearCode): + ....: def __init__(self, field, length): + ....: sage.coding.linear_code.AbstractLinearCode.__init__(self, field, length, "Systematic", "Syndrome") + ....: + ....: def _repr_(self): + ....: return "I am a badly defined code" + ....: + sage: BadCodeFamily(GF(3), 5).generator_matrix() + Traceback (most recent call last): + ... + ValueError: a parity check matrix must be specified if LinearCodeSystematicEncoder is the default encoder + """ + + def __init__(self, code, systematic_positions=None): + r""" + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: E = codes.encoders.LinearCodeSystematicEncoder(C) + sage: E + Systematic encoder for [7, 4] linear code over GF(2) + """ + super(LinearCodeSystematicEncoder, self).__init__(code) + self._systematic_positions = tuple(systematic_positions) if systematic_positions else None + if systematic_positions: + # Test that systematic_positions consists of integers in the right + # range. We test that len(systematic_positions) = code.dimension() + # in self.generator_matrix() to avoid possible infinite recursion. + if (not all( e in ZZ and e >= 0 and e < code.length() for e in systematic_positions)) \ + or len(systematic_positions) != len(set(systematic_positions)): + raise ValueError("systematic positions must be a tuple of distinct integers in the range 0 to n-1 where n is the length of the code") + # Test that the systematic positions are an information set + self.generator_matrix() + + + def __eq__(self, other): + r""" + Tests equality between LinearCodeSystematicEncoder objects. + + EXAMPLES:: + + sage: G = Matrix(GF(3), [[1,0,0,1,0,1,0,1,2],[0,1,0,2,2,0,1,1,0],[0,0,1,0,2,2,2,1,2]]) + sage: E1 = codes.encoders.LinearCodeSystematicEncoder(LinearCode(G)) + sage: E2 = codes.encoders.LinearCodeSystematicEncoder(LinearCode(G)) + sage: E1 == E2 + True + sage: E1.systematic_positions() + (0, 1, 2) + sage: E3 = codes.encoders.LinearCodeSystematicEncoder(LinearCode(G), systematic_positions=(2,5,6)) + sage: E3.systematic_positions() + (2, 5, 6) + sage: E1 == E3 + False + """ + return isinstance(other, LinearCodeSystematicEncoder)\ + and self.code() == other.code()\ + and self.systematic_positions() == other.systematic_positions() + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: E = codes.encoders.LinearCodeSystematicEncoder(C) + sage: E + Systematic encoder for [7, 4] linear code over GF(2) + """ + return "Systematic encoder for %s" % self.code() + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: E = codes.encoders.LinearCodeSystematicEncoder(C) + sage: latex(E) + \textnormal{Systematic encoder for }[7, 4]\textnormal{ Linear code over }\Bold{F}_{2} + """ + return "\\textnormal{Systematic encoder for }%s" % self.code()._latex_() + + @cached_method + def generator_matrix(self): + r""" + Returns a generator matrix in systematic form of the associated code of ``self``. + + Systematic form here means that a subsets of the columns of the matrix + forms the identity matrix. + + .. NOTE:: + + The matrix returned by this method will not necessarily be `[I \vert H]`, where `I` + is the identity block and `H` the parity block. If one wants to know which columns + create the identity block, one can call :meth:`systematic_positions` + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],\ + [1,0,0,1,1,0,0],\ + [0,1,0,1,0,1,0],\ + [1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: E = codes.encoders.LinearCodeSystematicEncoder(C) + sage: E.generator_matrix() + [1 0 0 0 0 1 1] + [0 1 0 0 1 0 1] + [0 0 1 0 1 1 0] + [0 0 0 1 1 1 1] + + We can ask for different systematic positions:: + + sage: E2 = codes.encoders.LinearCodeSystematicEncoder(C, systematic_positions=[5,4,3,2]) + sage: E2.generator_matrix() + [1 0 0 0 0 1 1] + [0 1 0 0 1 0 1] + [1 1 0 1 0 0 1] + [1 1 1 0 0 0 0] + + Another example where there is no generator matrix of the form `[I \vert H]`:: + + sage: G = Matrix(GF(2), [[1,1,0,0,1,0,1],\ + [1,1,0,0,1,0,0],\ + [0,0,1,0,0,1,0],\ + [0,0,1,0,1,0,1]]) + sage: C = LinearCode(G) + sage: E = codes.encoders.LinearCodeSystematicEncoder(C) + sage: E.generator_matrix() + [1 1 0 0 0 1 0] + [0 0 1 0 0 1 0] + [0 0 0 0 1 1 0] + [0 0 0 0 0 0 1] + """ + C = self.code() + # This if statement detects if this encoder is itself the default encoder. + # In this case, attempt building the generator matrix from the parity + # check matrix + if hasattr(self, "_use_pc_matrix"): + if self._use_pc_matrix == 1: + self._use_pc_matrix = 2 + return C.parity_check_matrix().right_kernel_matrix() + else: + raise ValueError("a parity check matrix must be specified if LinearCodeSystematicEncoder is the default encoder") + else: + self._use_pc_matrix = 1 + M = copy(C.generator_matrix()) + if not self._systematic_positions: + M.echelonize() + else: + k = M.nrows() # it is important that k is *not* computed as C.dimension() to avoid possible cyclic dependency + if len(self._systematic_positions) != k: + raise ValueError("systematic_positions must be a tuple of length equal to the dimension of the code") + # Permute the columns of M and bring to reduced row echelon formb + perm = self.systematic_permutation() + M.permute_columns(perm) + M.echelonize() + if M[:,:k].is_singular(): + raise ValueError("systematic_positions are not an information set") + M.permute_columns(perm.inverse()) + M.set_immutable() + return M + + def systematic_permutation(self): + r""" + Returns a permutation which would take the systematic positions into [0,..,k-1] + + EXAMPLES:: + + sage: C = LinearCode(matrix(GF(2), [[1,0,0,0,1,1,0],\ + [0,1,0,1,0,1,0],\ + [0,0,0,0,0,0,1]])) + sage: E = codes.encoders.LinearCodeSystematicEncoder(C) + sage: E.systematic_positions() + (0, 1, 6) + sage: E.systematic_permutation() + [1, 2, 7, 3, 4, 5, 6] + """ + n = self.code().length() + systematic_positions = self.systematic_positions() + k = len(systematic_positions) + lp = [ None ]*n + for (i,j) in zip(range(k), systematic_positions): + lp[i] = j + j = k + set_sys_pos = set(systematic_positions) + for i in range(n): + if not i in set_sys_pos: + lp[j] = i + j += 1 + from sage.combinat.permutation import Permutation + return Permutation([1 + e for e in lp]) + + def systematic_positions(self): + r""" + Returns a tuple containing the indices of the columns which form an + identity matrix when the generator matrix is in systematic form. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],\ + [1,0,0,1,1,0,0],\ + [0,1,0,1,0,1,0],\ + [1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: E = codes.encoders.LinearCodeSystematicEncoder(C) + sage: E.systematic_positions() + (0, 1, 2, 3) + + We take another matrix with a less nice shape:: + + sage: G = Matrix(GF(2), [[1,1,0,0,1,0,1],\ + [1,1,0,0,1,0,0],\ + [0,0,1,0,0,1,0],\ + [0,0,1,0,1,0,1]]) + sage: C = LinearCode(G) + sage: E = codes.encoders.LinearCodeSystematicEncoder(C) + sage: E.systematic_positions() + (0, 2, 4, 6) + + The systematic positions correspond to the positions which carry information in a codeword:: + + sage: MS = E.message_space() + sage: m = MS.random_element() + sage: c = m * E.generator_matrix() + sage: pos = E.systematic_positions() + sage: info = MS([c[i] for i in pos]) + sage: m == info + True + + When constructing a systematic encoder with specific systematic + positions, then it is guaranteed that this method returns exactly those + positions (even if another choice might also be systematic):: + + sage: G = Matrix(GF(2), [[1,0,0,0],\ + [0,1,0,0],\ + [0,0,1,1]]) + sage: C = LinearCode(G) + sage: E = codes.encoders.LinearCodeSystematicEncoder(C, systematic_positions=[0,1,3]) + sage: E.systematic_positions() + (0, 1, 3) + """ + return self._systematic_positions if self._systematic_positions else self.generator_matrix().pivots() diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py new file mode 100644 index 00000000000..a7be3571a69 --- /dev/null +++ b/src/sage/coding/linear_rank_metric.py @@ -0,0 +1,900 @@ +# -*- coding: utf-8 -*- +r""" +Generic structures for linear codes over the rank metric + +Rank Metric +=========== + +In coding theory, the most common metric is the Hamming metric, where distance +between two codewords is given by the number of positions in which they differ. +An alternative to this is the rank metric. Take two fields, `F_q` and `F_{q^m}`, +and define a code `C` to be a set of vectors of length `n` with entries from +`F_{q^m}`. Let `c` be a codeword. We can represent it as an `m \times n` matrix +`M` over `F_q`. + +A detailed description on the relationship between the two representations can +be found in :meth:`sage.coding.linear_rank_metric.to_matrix_representation` +and :meth:`sage.coding.linear_rank_metric.from_matrix_representation`. + +We can define a metric using the rank of the matrix representation of the +codewords. A distance between two codewords `a, b` is the rank of the matrix +representation of `a - b`. A weight of a codeword `c` is the rank of the matrix +representation of `c`. + +This module allows representing rank metric codes which are linear over the +big field `F_{q^m}`, i.e. the usual linearity condition when the codewords are +considered in vector form. One can also consider rank metric codes which are +only linear over `F_q`, but these are not currently supported in SageMath. + +Note that linear rank metric codes per the definition of this file are +mathematically just linear block codes, and so could be considered as a +:class:`sage.coding.linear_code.LinearCode`. However, since most of the +functionality of that class is specific to the Hamming metric, the two notions +are implemented as entirely different in SageMath. If you wish to investigate +Hamming-metric properties of a linear rank metric code ``C``, you can easily +convert it by calling ``C_hamm = LinearCode(C)``. + +Linear Rank Metric Code and Gabidulin Codes +=========================================== + +The class :class:`sage.coding.linear_rank_metric.LinearRankMetricCode` is the +analog of :class:`sage.coding.linear_code.LinearCode`, i.e. it is a generator +matrix-based representation of a linear rank metric code without specific +knowledge on the structure of the code. + +Gabidulin codes are the main family of structured linear rank metric codes. +These codes are the rank-metric analog of Reed-Solomon codes. + +``AbstractLinearRankMetricCode`` +-------------------------------- + +This is a base class designed to contain methods, features and parameters +shared by every linear rank metric code. For instance, generic algorithms for +computing the minimum distance, etc. Many of these algorithms are slow, +e.g. exponential in the code length. It also contains methods for swapping +between vector and matrix representation of elements. + +``AbstractLinearCodeNoMetric`` is an abstract class for linear rank metric codes, +so any linear rank metric code class should inherit from this class. +Also ``AbstractLinearCodeNoMetric`` should never itself be instantiated. + +See :class:`sage.coding.linear_rank_metric.AbstractLinearRankMetricCode` +for details and examples. + +``LinearRankMetricCode`` +------------------------ + +This class is used to represent arbitrary and unstructured linear rank metric +codes. It mostly relies directly on generic methods provided by +``AbstractLinearRankMetricCode``, which means that basic operations on the code +(e.g. computation of the minimum distance) will use slow algorithms. + +A ``LinearRankMetricCode`` is instantiated by providing a generator:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: C + [3, 2] linear rank metric code over GF(64)/GF(4) + sage: C.generator_matrix() + [1 1 0] + [0 0 1] + sage: c = vector(GF(64), (1, 1, 1)) + sage: c in C + True + +Further references +------------------ + +Read more about +`rank metric and Gabidulin codes `_ + +AUTHORS: + +- Marketa Slukova (2019-08-16): initial version + +TESTS:: + + sage: MS = MatrixSpace(GF(2),4,7) + sage: G = MS([[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: C == loads(dumps(C)) + True +""" + +# **************************************************************************** +# Copyright (C) 2019 MARKETA SLUKOVA +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.rings.integer import Integer +from sage.misc.cachefunc import cached_method +from sage.categories.fields import Fields +from sage.categories.modules import Modules +from sage.structure.parent import Parent +from sage.matrix.constructor import Matrix +from sage.modules.free_module import VectorSpace +from copy import copy +from sage.structure.element import is_Matrix, is_Vector +from sage.rings.integer_ring import ZZ +from sage.modules.free_module_element import vector +from sage.rings.infinity import Infinity + +from .linear_code_no_metric import AbstractLinearCodeNoMetric +from .linear_code import LinearCodeGeneratorMatrixEncoder +from .decoder import Decoder + +def to_matrix_representation(v, sub_field=None, basis=None): + r""" + Return a matrix representation of ``v`` over ``sub_field`` in terms of + ``basis``. + + Let `(b_1, b_2, \ldots, b_m)`, `b_i \in GF(q^m)`, be a basis of `GF(q^m)` as + a vector space over `GF(q)`. Take an element `x \in GF(q^m)`. We can write + `x` as `x = u_1 b_1 + u_2 b_2 + \ldots u_m b_m`, where `u_i \in GF(q)`. This + way we can represent an element from `GF(q^m)` as a vector of length `m` + over `GF(q)`. + + Given a vector ``v`` of length `n` over some field `F_{q^m}`, we can + represent each entry as a vector of length `m`, yielding an `m \times n` + matrix over ``sub_field``. In case ``sub_field`` is not given, we take the + prime subfield `F_p` of `F_{q^m}`. + + INPUT: + + - ``v`` -- a vector over some field `F_{q^m}` + + - ``sub_field`` -- (default: ``None``) a sub field of `F_{q^m}`. If not + specified, it is the prime subfield `F_p` of `F_{q^m}`. + + - ``basis`` -- (default: ``None``) a basis of `F_{q^m}` as a vector space over + ``sub_field``. If not specified, given that `q = p^s`, let + `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to + represent `F_{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + + EXAMPLES:: + + sage: from sage.coding.linear_rank_metric import to_matrix_representation + sage: x = GF(64).gen() + sage: a = vector(GF(64), (x + 1, x + 1, 1)) + sage: to_matrix_representation(a, GF(4)) + [1 1 1] + [1 1 0] + [0 0 0] + + sage: m = Matrix(GF(4), [[1, 1, 1], [1, 1, 0], [0, 0, 0]]) + sage: to_matrix_representation(m) + Traceback (most recent call last): + ... + TypeError: Input must be a vector + """ + if not is_Vector(v): + raise TypeError("Input must be a vector") + base_field = v.base_ring() + if not sub_field: + sub_field = base_field.prime_subfield() + n = v.length() + m = base_field.degree()//sub_field.degree() + extension, to_big_field, from_big_field = base_field.vector_space(sub_field, basis, map=True) + return Matrix(sub_field, m, n, lambda i, j: from_big_field(v[j])[i]) + +def from_matrix_representation(w, base_field=None, basis=None): + r""" + Return a vector representation of a matrix ``w`` over ``base_field`` in terms + of ``basis``. + + Given an `m \times n` matrix over `F_q` and some ``basis`` of `F_{q^m}` + over `F_q`, we can represent each of its columns as an element of `F_{q^m}`, + yielding a vector of length `n` over `F_q`. + + In case ``base_field`` is not given, we take `F_{q^m}`, the field extension of + `F_q` of degree `m`, the number of rows of ``w``. + + INPUT: + + - ``w`` -- a matrix over some field `F_q` + + - ``base_field`` -- (default: ``None``) an extension field of `F_q`. If not + specified, it is the field `F_{q^m}`, where `m` is the number of rows of + ``w``. + + - ``basis`` -- (default: ``None``) a basis of `F_{q^m}` as a vector space over + ``F_q``. If not specified, given that `q = p^s`, let + `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to + represent `F_{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + + EXAMPLES:: + + sage: from sage.coding.linear_rank_metric import from_matrix_representation + sage: m = Matrix(GF(4), [[1, 1, 1], [1, 1, 0], [0, 0, 0]]) + sage: from_matrix_representation(m) + (z6 + 1, z6 + 1, 1) + + sage: v = vector(GF(4), (1, 0, 0)) + sage: from_matrix_representation(v) + Traceback (most recent call last): + ... + TypeError: Input must be a matrix + """ + if not is_Matrix(w): + raise TypeError("Input must be a matrix") + sub_field = w.base_ring() + if not base_field: + base_field = sub_field.extension(w.nrows()) + v = [] + extension, to_big_field, from_big_field = base_field.vector_space(sub_field, basis, map=True) + for i in range(w.ncols()): + v.append(to_big_field(w.column(i))) + return vector(v) + +def rank_weight(c, sub_field=None, basis=None): + r""" + Return the rank of ``c`` as a matrix over ``sub_field``. + + If ``c`` is a vector over some field `F_{q^m}`, the function converts it + into a matrix over `F_q`. + + INPUT: + + - ``c`` -- a vector over some field `F_{q^m}`; or a matrix over `F_q` + + - ``sub_field`` -- (default: ``None``) a sub field of `F_{q^m}`. If not + specified, it is the prime subfield `F_p` of `F_{q^m}`. + + - ``basis`` -- (default: ``None``) a basis of `F_{q^m}` as a vector space over + ``sub_field``. If not specified, given that `q = p^s`, let + `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to + represent `F_{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + + EXAMPLES:: + + sage: from sage.coding.linear_rank_metric import rank_weight + sage: x = GF(64).gen() + sage: a = vector(GF(64), (x + 1, x + 1, 1)) + sage: rank_weight(a, GF(4)) + 2 + """ + if is_Vector(c): + c = to_matrix_representation(c, sub_field, basis) + return c.rank() + +def rank_distance(a, b, sub_field=None, basis=None): + r""" + Return the rank of ``a`` - ``b`` as a matrix over ``sub_field``. + + Take two vectors ``a``, ``b`` over some field `F_{q^m}`. This function + converts them to matrices over `F_q` and calculates the rank of their + difference. + + If ``sub_field`` is not specified, we take the prime subfield `F_q` of + `F_{q^m}`. + + INPUT: + + - ``a`` -- a vector over some field `F_{q^m}` + + - ``b`` -- a vector over some field `F_{q^m}` + + - ``sub_field`` -- (default: ``None``) a sub field of `F_{q^m}`. If not + specified, it is the prime subfield `F_p` of `F_{q^m}`. + + - ``basis`` -- (default: ``None``) a basis of `F_{q^m}` as a vector space over + ``sub_field``. If not specified, given that `q = p^s`, let + `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to + represent `F_{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + + EXAMPLES:: + + sage: from sage.coding.linear_rank_metric import rank_distance + sage: x = GF(64).gen() + sage: a = vector(GF(64), (x + 1, x + 1, 1)) + sage: b = vector(GF(64), (1, 0, 0)) + sage: rank_distance(a, b, GF(4)) + 2 + + sage: c = vector(GF(4), (1, 0, 0)) + sage: rank_distance(a, c, GF(4)) + Traceback (most recent call last): + ... + ValueError: The base field of (z6 + 1, z6 + 1, 1) and (1, 0, 0) has to be the same + + sage: d = Matrix(GF(64), (1, 0, 0)) + sage: rank_distance(a, d, GF(64)) + Traceback (most recent call last): + ... + TypeError: Both inputs have to be vectors + + sage: e = vector(GF(64), (1, 0)) + sage: rank_distance(a, e, GF(64)) + Traceback (most recent call last): + ... + ValueError: The length of (z6 + 1, z6 + 1, 1) and (1, 0) has to be the same + """ + if not (a.base_ring() == b.base_ring()): + raise ValueError("The base field of {} and {} has to be the same".format(a, b)) + if not (is_Vector(a) and is_Vector(b)): + raise TypeError("Both inputs have to be vectors") + if not len(a) == len(b): + raise ValueError("The length of {} and {} has to be the same".format(a, b)) + + a = to_matrix_representation(a, sub_field, basis) + b = to_matrix_representation(b, sub_field, basis) + return (a - b).rank() + + +class AbstractLinearRankMetricCode(AbstractLinearCodeNoMetric): + r""" + Abstract class for linear rank metric codes. + + This class contains methods that can be used on families of linear rank + metric codes. Every linear rank metric code class should inherit from this + abstract class. + + This class is intended for codes which are linear over the ``base_field``. + + Codewords of rank metric codes have two representations. They can either be + written as a vector of length `n` over `GF(q^m)`, or an `m \times n` matrix + over `GF(q)`. This implementation principally uses the vector representation. + However, one can always get the matrix representation using the + :meth:`sage.coding.linear_rank_metric.AbstractLinearRankMetricCode.to_matrix` + method. To go back to a vector, use the + :meth:`sage.coding.linear_rank_metric.AbstractLinearRankMetricCode.from_matrix` + method. + + Instructions on how to make a new family of rank metric codes is analogous + to making a new family of linear codes over the Hamming metric, instructions + for which are in :class:`sage.coding.linear_code.AbstractLinearCode`. For an + example on, see + :meth:`sage.coding.linear_rank_metric.AbstractLinearRankMetricCode.__init__` + + .. WARNING:: + + A lot of methods of the abstract class rely on the knowledge of a generator matrix. + It is thus strongly recommended to set an encoder with a generator matrix implemented + as a default encoder. + """ + _registered_encoders = {} + _registered_decoders = {} + + def __init__(self, base_field, sub_field, length, default_encoder_name, + default_decoder_name, basis=None): + r""" + Initialize mandatory parameters that every linear rank metric code has. + + This method only exists for inheritance purposes as it initializes + parameters that need to be known by every linear rank metric code. + The class :class:`sage.coding.linear_rank_metric.AbstractLinearRankMetricCode` + should never be directly instantiated. + + INPUT: + + - ``base_field`` -- the base field of ``self`` + + - ``sub_field`` -- the sub field of ``self`` + + - ``length`` -- the length of ``self`` (a Python int or a Sage Integer), + must be > 0 and at most the degree of the field extension + + - ``default_encoder_name`` -- the name of the default encoder of ``self`` + + - ``default_decoder_name`` -- the name of the default decoder of ``self`` + + - ``basis`` -- (default: ``None``) a basis of `F_{q^m}` as a vector space over + ``sub_field``. If not specified, given that `q = p^s`, let + `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to + represent `F_{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + + EXAMPLES: + + The following example demonstrates how to use subclass + `AbstractLinearRankMetricCode` for representing a new family of rank + metric codes. The example is a rank repetition code:: + + sage: from sage.coding.linear_rank_metric import AbstractLinearRankMetricCode + sage: class RankRepetitionCode(AbstractLinearRankMetricCode): + ....: def __init__(self, base_field, sub_field, length): + ....: sage.coding.linear_rank_metric.AbstractLinearRankMetricCode.__init__(self, base_field, sub_field, length, "GeneratorMatrix", "NearestNeighbor") + ....: beta = base_field.gen() + ....: self._generator_matrix = matrix(base_field, [[ beta^i for i in range(length) ]]) + ....: def generator_matrix(self): + ....: return self._generator_matrix + ....: def _repr_(self): + ....: return "[%d, %d] rank-metric repetition code over GF(%s)" % (self.length(), self.dimension(), self.base_field().cardinality()) + + We now instantiate a member of our newly made code family:: + + sage: C = RankRepetitionCode(GF(8), GF(2), 3) + + We can check its existence and parameters:: + + sage: C + [3, 1] rank-metric repetition code over GF(8) + + We can encode a vector:: + + sage: word = vector(C.base_field(), [1]) + sage: E = codes.encoders.LinearCodeSystematicEncoder(C) + sage: codeword = E(word) + sage: codeword + (1, z3, z3^2) + + We can get the matrix representation of the codeword:: + + sage: C.matrix_form_of_vector(codeword) + [1 0 0] + [0 1 0] + [0 0 1] + + We can decode the vector representation of the codeword:: + + sage: D = codes.decoders.LinearRankMetricCodeNearestNeighborDecoder(C) + sage: D.decode_to_code(codeword) + (1, z3, z3^2) + sage: D.decode_to_message(codeword) + (1) + + We can check that it is truly a part of the framework category:: + + sage: C.parent() + + sage: C.category() + Category of facade finite dimensional vector spaces with basis over Finite Field in z3 of size 2^3 + + And any method that works on rank metric linear codes works for our new dummy code:: + + sage: C.minimum_distance() + 3 + sage: C.metric() + 'rank' + + TESTS: + + If ``sub_field`` is not a field, an error is raised:: + + sage: C = RankRepetitionCode(GF(8), ZZ, 3) + Traceback (most recent call last): + ... + ValueError: 'sub_field' must be a field (and Integer Ring is not one) + + If ``sub_field`` is not a subfield of ``base_field``, an error is raised:: + + sage: C = RankRepetitionCode(GF(8), GF(3), 2) + Traceback (most recent call last): + ... + ValueError: 'sub_field' has to be a subfield of 'base_field' + """ + self._registered_decoders["NearestNeighbor"] = LinearRankMetricCodeNearestNeighborDecoder + + if not sub_field.is_field(): + raise ValueError("'sub_field' must be a field (and {} is not one)".format(sub_field)) + if not (sub_field.degree().divides(base_field.degree()) and (sub_field.prime_subfield() == base_field.prime_subfield())): + raise ValueError("'sub_field' has to be a subfield of 'base_field'") + m = base_field.degree() // sub_field.degree() + self._extension_degree = m + self._sub_field = sub_field + + self._generic_constructor = LinearRankMetricCode + super(AbstractLinearRankMetricCode, self).__init__(base_field, length, default_encoder_name, default_decoder_name, "rank") + + def sub_field(self): + r""" + Return the sub field of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: C.sub_field() + Finite Field in z2 of size 2^2 + """ + return self._sub_field + + def extension_degree(self): + r""" + Return `m`, the degree of the field extension of ``self``. + + Let ``base_field`` be `GF(q^m)` and ``sub_field`` be `GF(q)`. Then this + function returns `m`. + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: C.extension_degree() + 3 + """ + + return self._extension_degree + + def field_extension(self): + r""" + Return the field extension of ``self``. + + Let ``base_field`` be some field `F_{q^m}` and ``sub_field`` `F_{q}`. + This function returns the vector space of dimension `m` over `F_{q}`. + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: C.field_extension() + Vector space of dimension 3 over Finite Field in z2 of size 2^2 + """ + return self.base_field().vector_space(self.sub_field(), map=False) + + def rank_distance_between_vectors(self, left, right): + r""" + Return the rank of the matrix of ``left`` - ``right``. + + INPUT: + + - ``left`` -- a vector over the ``base_field`` of ``self`` + + - ``right`` -- a vector over the ``base_field`` of ``self`` + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: x = GF(64).gen() + sage: a = vector(GF(64), (x + 1, x + 1, 1)) + sage: b = vector(GF(64), (1, 0, 0)) + sage: C.rank_distance_between_vectors(a, b) + 2 + """ + return rank_distance(left, right, self.sub_field()) + + def minimum_distance(self): + r""" + Return the minimum distance of ``self``. + + This algorithm simply iterates over all the elements of the code and + returns the minimum weight. + + EXAMPLES:: + + sage: F. = GF(8) + sage: G = Matrix(F, [[1,a,a^2,0]]) + sage: C = codes.LinearRankMetricCode(G, GF(2)) + sage: C.minimum_distance() + 3 + """ + d = Infinity + for c in self: + if c == self.zero(): + continue + d = min(self.rank_weight_of_vector(c), d) + return d + + def rank_weight_of_vector(self, word): + r""" + Return the weight of the word, i.e. its rank. + + INPUT: + + - ``word`` -- a vector over the ``base_field`` of ``self`` + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: x = GF(64).gen() + sage: a = vector(GF(64), (x + 1, x + 1, 1)) + sage: C.rank_weight_of_vector(a) + 2 + """ + return rank_weight(word, self.sub_field()) + + def matrix_form_of_vector(self, word): + r""" + Return the matrix representation of a word. + + INPUT: + + - ``word`` -- a vector over the ``base_field`` of ``self`` + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: x = GF(64).gen() + sage: a = vector(GF(64), (x + 1, x + 1, 1)) + sage: C.matrix_form_of_vector(a) + [1 1 1] + [1 1 0] + [0 0 0] + """ + return to_matrix_representation(word, self.sub_field()) + + def vector_form_of_matrix(self, word): + r""" + Return the vector representation of a word. + + INPUT: + + - ``word`` -- a matrix over the ``sub_field`` of ``self`` + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: x = GF(64).gen() + sage: m = Matrix(GF(4), [[1, 1, 1], [1, 1, 0], [0, 0, 0]]) + sage: C.vector_form_of_matrix(m) + (z6 + 1, z6 + 1, 1) + """ + return from_matrix_representation(word, self.base_field()) + + +class LinearRankMetricCode(AbstractLinearRankMetricCode): + r""" + Linear rank metric codes over a finite field, represented using a + generator matrix. + + This class should be used for arbitrary and unstructured linear rank metric + codes. This means that basic operations on the code, such as the computation + of the minimum distance, will use generic, slow algorithms. + + If you are looking for constructing a code from a more specific family, see + if the family has been implemented by investigating ``codes.``. These + more specific classes use properties particular to that family to allow + faster algorithms, and could also have family-specific methods. + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: C + [3, 2] linear rank metric code over GF(64)/GF(4) + sage: C.base_field() + Finite Field in z6 of size 2^6 + sage: C.sub_field() + Finite Field in z2 of size 2^2 + sage: C.length() + 3 + sage: C.dimension() + 2 + sage: C[2] + (z6, z6, 0) + sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) + sage: word = vector(C.base_field(), [1, 0]) + sage: E(word) + (1, 1, 0) + """ + + def __init__(self, generator, sub_field=None, basis=None): + r""" + See the docstring for :meth:`LinearRankMetricCode`. + + INPUT: + + - ``generator`` -- a generator matrix over the ``base_field`` with + dimension `k \times n`, where `k` is the dimension of the code and + `n` its length; or a code over a finite field + + - ``sub_field`` -- (default: ``None``) the sub field of ``self``, if not + specified, it is the prime field of ``base_field`` + + - ``basis`` -- (default: ``None``) a basis of `F_{q^m}` as a vector space over + ``sub_field``. If not specified, given that `q = p^s`, let + `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to + represent `F_{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) # indirect doctest + sage: C + [3, 2] linear rank metric code over GF(64)/GF(4) + """ + base_field = generator.base_ring() + if not base_field.is_field(): + raise ValueError("'generator' must be defined on a field (not a ring)") + + if not sub_field: + sub_field = base_field.prime_subfield() + + try: + gen_basis = None + if hasattr(generator,"nrows"): # generator matrix case + if generator.rank() < generator.nrows(): + gen_basis = generator.row_space().basis() + else: + gen_basis = generator.basis() # vector space etc. case + if not gen_basis is None: + from sage.matrix.constructor import matrix + generator = matrix(base_field, gen_basis) + if generator.nrows() == 0: + raise ValueError("this linear code contains no non-zero vector") + except AttributeError: + # Assume input is an AbstractLinearRankMetricCode, extract its generator matrix + generator = generator.generator_matrix() + + self._generator_matrix = generator + self._length = generator.ncols() + super(LinearRankMetricCode, self).__init__(base_field, sub_field, self._length, "GeneratorMatrix", "NearestNeighbor", basis) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: C + [3, 2] linear rank metric code over GF(64)/GF(4) + """ + R = self.base_field() + S = self.sub_field() + if R and S in Fields(): + return "[%s, %s] linear rank metric code over GF(%s)/GF(%s)"%(self.length(), self.dimension(), R.cardinality(), S.cardinality()) + else: + return "[%s, %s] linear rank metric code over %s/%s"%(self.length(), self.dimension(), R, S) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: latex(C) + [3, 2]\textnormal{ Linear rank metric code over }\Bold{F}_{2^{6}}/\Bold{F}_{2^{2}} + """ + return "[%s, %s]\\textnormal{ Linear rank metric code over }%s/%s"\ + % (self.length(), self.dimension(), self.base_field()._latex_(), self.sub_field()._latex_()) + + def generator_matrix(self, encoder_name=None, **kwargs): + r""" + Return a generator matrix of ``self``. + + INPUT: + + - ``encoder_name`` -- (default: ``None``) name of the encoder which will be + used to compute the generator matrix. ``self._generator_matrix`` + will be returned if default value is kept. + + - ``kwargs`` -- all additional arguments are forwarded to the construction of the + encoder that is used. + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: C.generator_matrix() + [1 1 0] + [0 0 1] + """ + if encoder_name is None or encoder_name is 'GeneratorMatrix': + g = self._generator_matrix + else: + g = super(LinearRankMetricCode, self).generator_matrix(encoder_name, **kwargs) + g.set_immutable() + return g + + +####################### decoders ############################### +class LinearRankMetricCodeNearestNeighborDecoder(Decoder): + r""" + Construct a decoder for Linear Rank Metric Codes. + + This decoder will decode to the nearest codeword found. + """ + + def __init__(self, code): + r""" + + INPUT: + + - ``code`` -- A code associated to this decoder + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: D = codes.decoders.LinearRankMetricCodeNearestNeighborDecoder(C) + sage: D + Nearest neighbor decoder for [3, 2] linear rank metric code over GF(64)/GF(4) + """ + super(LinearRankMetricCodeNearestNeighborDecoder, self).__init__(code, code.ambient_space(), \ + code._default_encoder_name) + + def __eq__(self, other): + r""" + Test equality between LinearRankMetricCodeNearestNeighborDecoder objects. + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: D1 = codes.decoders.LinearRankMetricCodeNearestNeighborDecoder(C) + sage: D2 = codes.decoders.LinearRankMetricCodeNearestNeighborDecoder(C) + sage: D1 == D2 + True + """ + return isinstance(other, LinearRankMetricCodeNearestNeighborDecoder)\ + and self.code() == other.code() + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: D = codes.decoders.LinearRankMetricCodeNearestNeighborDecoder(C) + sage: D + Nearest neighbor decoder for [3, 2] linear rank metric code over GF(64)/GF(4) + """ + return "Nearest neighbor decoder for %s" % self.code() + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(64), [[1,1,0], [0,0,1]]) + sage: C = codes.LinearRankMetricCode(G, GF(4)) + sage: D = codes.decoders.LinearRankMetricCodeNearestNeighborDecoder(C) + sage: latex(D) + \textnormal{Nearest neighbor decoder for }[3, 2]\textnormal{ Linear rank metric code over }\Bold{F}_{2^{6}}/\Bold{F}_{2^{2}} + """ + return "\\textnormal{Nearest neighbor decoder for }%s" % self.code()._latex_() + + def decode_to_code(self, r): + r""" + Corrects the errors in ``word`` and returns a codeword. + + INPUT: + + - ``r`` -- a codeword of ``self`` + + OUTPUT: + + - a vector of ``self``'s message space + + EXAMPLES:: + + sage: F. = GF(4) + sage: G = Matrix(F, [[1,1,0]]) + sage: C = codes.LinearRankMetricCode(G, GF(2)) + sage: D = codes.decoders.LinearRankMetricCodeNearestNeighborDecoder(C) + sage: D.decode_to_code(vector(F, [a, a, 1])) + (a, a, 0) + """ + C = self.code() + c_min = C.zero() + h_min = C.rank_weight_of_vector(r) + for c in C: + if C.rank_weight_of_vector(c-r) < h_min: + h_min = C.rank_weight_of_vector(c-r) + c_min = c + c_min.set_immutable() + return c_min + + def decoding_radius(self): + r""" + Return maximal number of errors ``self`` can decode. + + EXAMPLES:: + + sage: F. = GF(8) + sage: G = Matrix(F, [[1,a,a^2,0]]) + sage: C = codes.LinearRankMetricCode(G, GF(2)) + sage: D = codes.decoders.LinearRankMetricCodeNearestNeighborDecoder(C) + sage: D.decoding_radius() + 1 + """ + return (self.code().minimum_distance()-1) // 2 + +####################### registration ############################### + +LinearRankMetricCode._registered_encoders["GeneratorMatrix"] = LinearCodeGeneratorMatrixEncoder diff --git a/src/sage/coding/punctured_code.py b/src/sage/coding/punctured_code.py index eea926bf884..36a881d6d0a 100644 --- a/src/sage/coding/punctured_code.py +++ b/src/sage/coding/punctured_code.py @@ -239,7 +239,7 @@ def random_element(self, *args, **kwds): Returns a random codeword of ``self``. This method does not trigger the computation of - ``self``'s :meth:`sage.coding.linear_code.generator_matrix`. + ``self``'s :meth:`sage.coding.linear_code_no_metric.generator_matrix`. INPUT: diff --git a/src/sage/coding/reed_muller_code.py b/src/sage/coding/reed_muller_code.py index bf24a23838e..d5bc085fe52 100644 --- a/src/sage/coding/reed_muller_code.py +++ b/src/sage/coding/reed_muller_code.py @@ -24,7 +24,6 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from operator import mul from sage.matrix.constructor import matrix diff --git a/src/sage/coding/source_coding/huffman.py b/src/sage/coding/source_coding/huffman.py index 3849ff4cb46..884b50648a6 100644 --- a/src/sage/coding/source_coding/huffman.py +++ b/src/sage/coding/source_coding/huffman.py @@ -31,8 +31,6 @@ from collections import defaultdict -import six - from sage.structure.sage_object import SageObject ########################################################################### @@ -251,7 +249,7 @@ def __init__(self, source): # index of each alphabetic symbol self._index = None - if isinstance(source, six.string_types): + if isinstance(source, str): self._build_code(frequency_table(source)) elif isinstance(source, dict): self._build_code(source) diff --git a/src/sage/combinat/abstract_tree.py b/src/sage/combinat/abstract_tree.py index ad4d62778dc..e017b51dd36 100644 --- a/src/sage/combinat/abstract_tree.py +++ b/src/sage/combinat/abstract_tree.py @@ -1925,7 +1925,7 @@ def __setitem__(self, idx, value): sage: with x.clone() as x: ....: x[0] = OrderedTree([[]]) Traceback (most recent call last): - ....: + ... IndexError: list assignment index out of range sage: x = OrderedTree([]); x = OrderedTree([x,x]); x = OrderedTree([x,x]); x = OrderedTree([x,x]) diff --git a/src/sage/combinat/affine_permutation.py b/src/sage/combinat/affine_permutation.py index fdecf4e1a4b..f550c1413e0 100644 --- a/src/sage/combinat/affine_permutation.py +++ b/src/sage/combinat/affine_permutation.py @@ -12,8 +12,6 @@ #***************************************************************************** from __future__ import print_function, division -from six.moves import range - from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod from sage.misc.constant_function import ConstantFunction diff --git a/src/sage/combinat/algebraic_combinatorics.py b/src/sage/combinat/algebraic_combinatorics.py index f0ff485a5a2..599913ffdb8 100644 --- a/src/sage/combinat/algebraic_combinatorics.py +++ b/src/sage/combinat/algebraic_combinatorics.py @@ -36,6 +36,7 @@ - :class:`~sage.algebras.affine_nil_temperley_lieb.AffineNilTemperleyLiebTypeA` - :ref:`sage.combinat.descent_algebra` - :ref:`sage.combinat.diagram_algebras` +- :ref:`sage.combinat.blob_algebra` Combinatorial Representation Theory ----------------------------------- diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py index 258f528777d..40f1e598466 100644 --- a/src/sage/combinat/all.py +++ b/src/sage/combinat/all.py @@ -148,7 +148,6 @@ 'LabelledRootedTree', 'LabelledRootedTrees')) from .combination import Combinations -from .cartesian_product import CartesianProduct from .set_partition import SetPartition, SetPartitions from .set_partition_ordered import OrderedSetPartition, OrderedSetPartitions @@ -234,3 +233,7 @@ 'GrowthDiagramRSK', 'GrowthDiagramBurge', 'GrowthDiagramBinWord', 'GrowthDiagramDomino', 'GrowthDiagramYoungFibonacci', 'GrowthDiagramSylvester']) + +# Path Tableaux +lazy_import('sage.combinat.path_tableaux', 'catalog', as_='path_tableaux') + diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index 1489cc7221c..cd248abcdd9 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -31,8 +31,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import division -from six.moves import range, zip -from six import add_metaclass import copy from sage.misc.classcall_metaclass import ClasscallMetaclass @@ -79,8 +77,8 @@ def _inplace_height_function_gyration(hf): hf[i,j] -= 2 -@add_metaclass(InheritComparisonClasscallMetaclass) -class AlternatingSignMatrix(Element): +class AlternatingSignMatrix(Element, + metaclass=InheritComparisonClasscallMetaclass): r""" An alternating sign matrix. @@ -1798,8 +1796,7 @@ def _is_a_cover(mt0, mt1): register_unpickle_override('sage.combinat.alternating_sign_matrix', 'MonotoneTriangles_n', MonotoneTriangles) -@add_metaclass(ClasscallMetaclass) -class ContreTableaux(Parent): +class ContreTableaux(Parent, metaclass=ClasscallMetaclass): """ Factory class for the combinatorial class of contre tableaux of size `n`. @@ -1960,8 +1957,7 @@ def _previous_column_iterator(column, height, max_value): return _next_column_iterator(new_column, height) -@add_metaclass(ClasscallMetaclass) -class TruncatedStaircases(Parent): +class TruncatedStaircases(Parent, metaclass=ClasscallMetaclass): """ Factory class for the combinatorial class of truncated staircases of size ``n`` with last column ``last_column``. diff --git a/src/sage/combinat/backtrack.py b/src/sage/combinat/backtrack.py index d4420497073..7bf2aef3c30 100644 --- a/src/sage/combinat/backtrack.py +++ b/src/sage/combinat/backtrack.py @@ -1,50 +1,14 @@ r""" Backtracking -This library contains generic tools for constructing large sets whose +This library contains a generic tool for constructing large sets whose elements can be enumerated by exploring a search space with a (lazy) tree or graph structure. - :class:`GenericBacktracker`: Depth first search through a tree described by a ``children`` function, with branch pruning, etc. -Deprecated classes (use :func:`RecursivelyEnumeratedSet` instead): - -- :class:`SearchForest`: Depth and breadth first - search through a tree described by a ``children`` function. - -- :class:`TransitiveIdeal`: Depth first search through a - graph described by a ``neighbours`` relation. - -- :class:`TransitiveIdealGraded`: Breadth first search - through a graph described by a ``neighbours`` relation. - -Deprecation details: - -- ``SearchForest(seeds, succ)`` keeps the same behavior as before - :trac:`6637` and is now the same as ``RecursivelyEnumeratedSet(seeds, - succ, structure='forest', enumeration='depth')``. - -- ``TransitiveIdeal(succ, seeds)`` keeps the same behavior as before - :trac:`6637` and is now the same as ``RecursivelyEnumeratedSet(seeds, - succ, structure=None, enumeration='naive')``. - -- ``TransitiveIdealGraded(succ, seeds, max_depth)`` keeps the same behavior - as before :trac:`6637` and is now the same as - ``RecursivelyEnumeratedSet(seeds, succ, structure=None, - enumeration='breadth', max_depth=max_depth)``. - -.. todo:: - - - For now the code of :class:`SearchForest` is still in - ``sage/combinat/backtrack.py``. It should be moved in - ``sage/sets/recursively_enumerated_set.pyx`` into a class named - :class:`RecursivelyEnumeratedSet_forest` in a later ticket. - - - Deprecate ``TransitiveIdeal`` and ``TransitiveIdealGraded``. - - - Once the deprecation has been there for enough time: delete - ``TransitiveIdeal`` and ``TransitiveIdealGraded``. +This module has mostly been superseded by ``RecursivelyEnumeratedSet``. """ #***************************************************************************** @@ -63,47 +27,20 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.categories.enumerated_sets import EnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.monoids import Monoids -from sage.structure.parent import Parent -from sage.misc.prandom import randint -from sage.misc.abstract_method import abstract_method from sage.categories.commutative_additive_semigroups import ( CommutativeAdditiveSemigroups) from sage.structure.unique_representation import UniqueRepresentation from sage.rings.integer_ring import ZZ -from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_generic - - -def _imap_and_filter_none(function, iterable): - r""" - Return an iterator over the elements ``function(x)``, where ``x`` - iterates through ``iterable``, such that ``function(x)`` is not - ``None``. - - EXAMPLES:: - - sage: from sage.combinat.backtrack import _imap_and_filter_none - sage: p = _imap_and_filter_none(lambda x: x if is_prime(x) else None, range(15)) - sage: [next(p), next(p), next(p), next(p), next(p), next(p)] - [2, 3, 5, 7, 11, 13] - sage: p = _imap_and_filter_none(lambda x: x+x, ['a','b','c','d','e']) - sage: [next(p), next(p), next(p), next(p), next(p)] - ['aa', 'bb', 'cc', 'dd', 'ee'] - """ - for x in iterable: - x = function(x) - if x is not None: - yield x - +from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest class GenericBacktracker(object): r""" A generic backtrack tool for exploring a search space organized as a tree, with branch pruning, etc. - See also :class:`SearchForest` and :class:`TransitiveIdeal` for + See also :class:`RecursivelyEnumeratedSet_forest` and :class:`TransitiveIdeal` for handling simple special cases. """ def __init__(self, initial_data, initial_state): @@ -154,622 +91,12 @@ def __iter__(self): if state is not None: stack.append( self._rec(obj, state) ) -def search_forest_iterator(roots, children, algorithm='depth'): - r""" - Return an iterator on the nodes of the forest having the given - roots, and where ``children(x)`` returns the children of the node ``x`` - of the forest. Note that every node of the tree is returned, - not simply the leaves. - - INPUT: - - - ``roots`` -- a list (or iterable) - - ``children`` -- a function returning a list (or iterable) - - ``algorithm`` -- ``'depth'`` or ``'breadth'`` (default: ``'depth'``) - - EXAMPLES: - - We construct the prefix tree of binary sequences of length at most - three, and enumerate its nodes:: - - sage: from sage.combinat.backtrack import search_forest_iterator - sage: list(search_forest_iterator([[]], lambda l: [l+[0], l+[1]] - ....: if len(l) < 3 else [])) - [[], [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0], - [0, 1, 1], [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]] - - By default, the nodes are iterated through by depth first search. - We can instead use a breadth first search (increasing depth):: - - sage: list(search_forest_iterator([[]], lambda l: [l+[0], l+[1]] - ....: if len(l) < 3 else [], - ....: algorithm='breadth')) - [[], - [0], [1], - [0, 0], [0, 1], [1, 0], [1, 1], - [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], - [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]] - - This allows for iterating trough trees of infinite depth:: - - sage: it = search_forest_iterator([[]], lambda l: [l+[0], l+[1]], algorithm='breadth') - sage: [ next(it) for i in range(16) ] - [[], - [0], [1], [0, 0], [0, 1], [1, 0], [1, 1], - [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], - [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1], - [0, 0, 0, 0]] - - Here is an iterator through the prefix tree of sequences of - letters in `0,1,2` without repetitions, sorted by length; the - leaves are therefore permutations:: - - sage: list(search_forest_iterator([[]], lambda l: [l + [i] for i in range(3) if i not in l], - ....: algorithm='breadth')) - [[], - [0], [1], [2], - [0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1], - [0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]] - """ - # Little trick: the same implementation handles both depth and - # breadth first search. Setting position to -1 makes a depth search - # (you ask the children for the last node you met). Setting - # position on 0 makes a breadth search (enumerate all the - # descendants of a node before going on to the next father) - if algorithm == 'depth': - position = -1 - else: - position = 0 - - # Invariant: - # - for breadth first search: stack[i] contains an iterator over the nodes - # of depth ``i`` in the tree - # - for depth first search: stack[i] contains an iterator over the children - # of the node at depth ``i-1`` in the current branch (assuming a virtual - # father of all roots at depth ``-1``) - stack = [iter(roots)] - while stack: - try: - node = next(stack[position]) - except StopIteration: - # If there are no more, go back up the tree - # We also need to check if we've exhausted all - # possibilities - stack.pop(position) - continue - - yield node - stack.append( iter(children(node)) ) - -class SearchForest(Parent): - r""" - The enumerated set of the nodes of the forest having the given - ``roots``, and where ``children(x)`` returns the children of the - node ``x`` of the forest. - - See also :class:`GenericBacktracker`, :class:`TransitiveIdeal`, - and :class:`TransitiveIdealGraded`. - - INPUT: - - - ``roots`` -- a list (or iterable) - - ``children`` -- a function returning a list (or iterable, or iterator) - - ``post_process`` -- a function defined over the nodes of the - forest (default: no post processing) - - ``algorithm`` -- ``'depth'`` or ``'breadth'`` (default: ``'depth'``) - - ``category`` -- a category (default: :class:`EnumeratedSets`) - - The option ``post_process`` allows for customizing the nodes that - are actually produced. Furthermore, if ``f(x)`` returns ``None``, - then ``x`` won't be output at all. - - EXAMPLES: - - We construct the set of all binary sequences of length at most - three, and list them:: - - sage: from sage.combinat.backtrack import SearchForest - sage: S = SearchForest( [[]], - ....: lambda l: [l+[0], l+[1]] if len(l) < 3 else [], - ....: category=FiniteEnumeratedSets()) - sage: S.list() - [[], - [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0], [0, 1, 1], - [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]] - - ``SearchForest`` needs to be explicitly told that the set is - finite for the following to work:: - - sage: S.category() - Category of finite enumerated sets - sage: S.cardinality() - 15 - - We proceed with the set of all lists of letters in ``0,1,2`` - without repetitions, ordered by increasing length (i.e. using a - breadth first search through the tree):: - - sage: from sage.combinat.backtrack import SearchForest - sage: tb = SearchForest( [[]], - ....: lambda l: [l + [i] for i in range(3) if i not in l], - ....: algorithm = 'breadth', - ....: category=FiniteEnumeratedSets()) - sage: tb[0] - [] - sage: tb.cardinality() - 16 - sage: list(tb) - [[], - [0], [1], [2], - [0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1], - [0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]] - - For infinite sets, this option should be set carefully to ensure - that all elements are actually generated. The following example - builds the set of all ordered pairs `(i,j)` of nonnegative - integers such that `j\leq 1`:: - - sage: from sage.combinat.backtrack import SearchForest - sage: I = SearchForest([(0,0)], - ....: lambda l: [(l[0]+1, l[1]), (l[0], 1)] - ....: if l[1] == 0 else [(l[0], l[1]+1)]) - - With a depth first search, only the elements of the form `(i,0)` - are generated:: - - sage: depth_search = I.depth_first_search_iterator() - sage: [next(depth_search) for i in range(7)] - [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0)] - - Using instead breadth first search gives the usual anti-diagonal - iterator:: - - sage: breadth_search = I.breadth_first_search_iterator() - sage: [next(breadth_search) for i in range(15)] - [(0, 0), - (1, 0), (0, 1), - (2, 0), (1, 1), (0, 2), - (3, 0), (2, 1), (1, 2), (0, 3), - (4, 0), (3, 1), (2, 2), (1, 3), (0, 4)] - - .. rubric:: Deriving subclasses - - The class of a parent `A` may derive from :class:`SearchForest` so - that `A` can benefit from enumeration tools. As a running example, - we consider the problem of enumerating integers whose binary - expansion have at most three nonzero digits. For example, `3 = - 2^1 + 2^0` has two nonzero digits. `15 = 2^3 + 2^2 + 2^1 + 2^0` - has four nonzero digits. In fact, `15` is the smallest integer - which is not in the enumerated set. - - To achieve this, we use ``SearchForest`` to enumerate binary tuples - with at most three nonzero digits, apply a post processing to - recover the corresponding integers, and discard tuples finishing - by zero. - - A first approach is to pass the ``roots`` and ``children`` - functions as arguments to :meth:`SearchForest.__init__`:: - - sage: from sage.combinat.backtrack import SearchForest - sage: class A(UniqueRepresentation, SearchForest): - ....: def __init__(self): - ....: SearchForest.__init__(self, [()], - ....: lambda x : [x+(0,), x+(1,)] if sum(x) < 3 else [], - ....: lambda x : sum(x[i]*2^i for i in range(len(x))) if sum(x) != 0 and x[-1] != 0 else None, - ....: algorithm = 'breadth', - ....: category=InfiniteEnumeratedSets()) - sage: MyForest = A(); MyForest - An enumerated set with a forest structure - sage: MyForest.category() - Category of infinite enumerated sets - sage: p = iter(MyForest) - sage: [next(p) for i in range(30)] - [1, 2, 3, 4, 6, 5, 7, 8, 12, 10, 14, 9, 13, 11, 16, 24, 20, 28, 18, 26, 22, 17, 25, 21, 19, 32, 48, 40, 56, 36] - - An alternative approach is to implement ``roots`` and ``children`` - as methods of the subclass (in fact they could also be attributes - of `A`). Namely, ``A.roots()`` must return an iterable containing - the enumeration generators, and ``A.children(x)`` must return an - iterable over the children of `x`. Optionally, `A` can have a - method or attribute such that ``A.post_process(x)`` returns the - desired output for the node ``x`` of the tree:: - - sage: from sage.combinat.backtrack import SearchForest - sage: class A(UniqueRepresentation, SearchForest): - ....: def __init__(self): - ....: SearchForest.__init__(self, algorithm = 'breadth', - ....: category=InfiniteEnumeratedSets()) - ....: - ....: def roots(self): - ....: return [()] - ....: - ....: def children(self, x): - ....: if sum(x) < 3: - ....: return [x+(0,), x+(1,)] - ....: else: - ....: return [] - ....: - ....: def post_process(self, x): - ....: if sum(x) == 0 or x[-1] == 0: - ....: return None - ....: else: - ....: return sum(x[i]*2^i for i in range(len(x))) - sage: MyForest = A(); MyForest - An enumerated set with a forest structure - sage: MyForest.category() - Category of infinite enumerated sets - sage: p = iter(MyForest) - sage: [next(p) for i in range(30)] - [1, 2, 3, 4, 6, 5, 7, 8, 12, 10, 14, 9, 13, 11, 16, 24, 20, 28, 18, 26, 22, 17, 25, 21, 19, 32, 48, 40, 56, 36] - - .. warning:: - - A :class:`SearchForest` instance is picklable if and only if - the input functions are themselves picklable. This excludes - anonymous or interactively defined functions:: - - sage: def children(x): - ....: return [x+1] - sage: S = SearchForest( [1], children, category=InfiniteEnumeratedSets()) - sage: dumps(S) - Traceback (most recent call last): - ... - PicklingError: Can't pickle <...function...>: attribute lookup ... failed - - Let us now fake ``children`` being defined in a Python module:: - - sage: import __main__ - sage: __main__.children = children - sage: S = SearchForest( [1], children, category=InfiniteEnumeratedSets()) - sage: loads(dumps(S)) - An enumerated set with a forest structure - """ - def __init__(self, roots = None, children = None, post_process = None, - algorithm = 'depth', facade = None, category=None): - r""" - TESTS:: - - sage: from sage.combinat.backtrack import SearchForest - sage: S = SearchForest(NN, lambda x : [], lambda x: x^2 if x.is_prime() else None) - sage: S.category() - Category of enumerated sets - """ - if roots is not None: - self._roots = roots - if children is not None: - self.children = children - if post_process is not None: - self.post_process = post_process - self._algorithm = algorithm - Parent.__init__(self, facade = facade, category = EnumeratedSets().or_subcategory(category)) - - __len__ = None - - def _repr_(self): - r""" - TESTS:: - - sage: from sage.combinat.backtrack import SearchForest - sage: SearchForest( [1], lambda x: [x+1]) - An enumerated set with a forest structure - """ - return "An enumerated set with a forest structure" - - def roots(self): - r""" - Return an iterable over the roots of ``self``. - - EXAMPLES:: - - sage: from sage.combinat.backtrack import SearchForest - sage: I = SearchForest([(0,0)], lambda l: [(l[0]+1, l[1]), (l[0], 1)] if l[1] == 0 else [(l[0], l[1]+1)]) - sage: [i for i in I.roots()] - [(0, 0)] - sage: I = SearchForest([(0,0),(1,1)], lambda l: [(l[0]+1, l[1]), (l[0], 1)] if l[1] == 0 else [(l[0], l[1]+1)]) - sage: [i for i in I.roots()] - [(0, 0), (1, 1)] - """ - return self._roots - - @abstract_method - def children(self, x): - r""" - Return the children of the element ``x`` - - The result can be a list, an iterable, an iterator, or even a - generator. - - EXAMPLES:: - - sage: from sage.combinat.backtrack import SearchForest - sage: I = SearchForest([(0,0)], lambda l: [(l[0]+1, l[1]), (l[0], 1)] if l[1] == 0 else [(l[0], l[1]+1)]) - sage: [i for i in I.children((0,0))] - [(1, 0), (0, 1)] - sage: [i for i in I.children((1,0))] - [(2, 0), (1, 1)] - sage: [i for i in I.children((1,1))] - [(1, 2)] - sage: [i for i in I.children((4,1))] - [(4, 2)] - sage: [i for i in I.children((4,0))] - [(5, 0), (4, 1)] - """ - - def __iter__(self): - r""" - Return an iterator over the elements of ``self``. - - EXAMPLES:: - - sage: from sage.combinat.backtrack import SearchForest - sage: def children(l): - ....: return [l+[0], l+[1]] - ....: - sage: C = SearchForest(([],), children) - sage: f = C.__iter__() - sage: next(f) - [] - sage: next(f) - [0] - sage: next(f) - [0, 0] - """ - iter = search_forest_iterator(self.roots(), - self.children, - algorithm = self._algorithm) - if hasattr(self, "post_process"): - iter = _imap_and_filter_none(self.post_process, iter) - return iter - - def depth_first_search_iterator(self): - r""" - Return a depth first search iterator over the elements of ``self`` - - EXAMPLES:: - - sage: from sage.combinat.backtrack import SearchForest - sage: f = SearchForest([[]], - ....: lambda l: [l+[0], l+[1]] if len(l) < 3 else []) - sage: list(f.depth_first_search_iterator()) - [[], [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0], [0, 1, 1], [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]] - """ - return iter(self) - - def breadth_first_search_iterator(self): - r""" - Return a breadth first search iterator over the elements of ``self`` - - EXAMPLES:: - - sage: from sage.combinat.backtrack import SearchForest - sage: f = SearchForest([[]], - ....: lambda l: [l+[0], l+[1]] if len(l) < 3 else []) - sage: list(f.breadth_first_search_iterator()) - [[], [0], [1], [0, 0], [0, 1], [1, 0], [1, 1], [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]] - sage: S = SearchForest([(0,0)], - ....: lambda x : [(x[0], x[1]+1)] if x[1] != 0 else [(x[0]+1,0), (x[0],1)], - ....: post_process = lambda x: x if ((is_prime(x[0]) and is_prime(x[1])) and ((x[0] - x[1]) == 2)) else None) - sage: p = S.breadth_first_search_iterator() - sage: [next(p), next(p), next(p), next(p), next(p), next(p), next(p)] - [(5, 3), (7, 5), (13, 11), (19, 17), (31, 29), (43, 41), (61, 59)] - """ - iter = search_forest_iterator(self.roots(), self.children, algorithm='breadth') - if hasattr(self, "post_process"): - iter = _imap_and_filter_none(self.post_process, iter) - return iter - - def _elements_of_depth_iterator_rec(self, depth=0): - r""" - Return an iterator over the elements of ``self`` of given depth. - An element of depth `n` can be obtained applying `n` times the - children function from a root. This function is not affected - by post processing. - - EXAMPLES:: - - sage: from sage.combinat.backtrack import SearchForest - sage: I = SearchForest([(0,0)], lambda l: [(l[0]+1, l[1]), (l[0], 1)] if l[1] == 0 else [(l[0], l[1]+1)]) - sage: list(I._elements_of_depth_iterator_rec(8)) - [(8, 0), (7, 1), (6, 2), (5, 3), (4, 4), (3, 5), (2, 6), (1, 7), (0, 8)] - sage: I = SearchForest([[]], lambda l: [l+[0], l+[1]] if len(l) < 3 else []) - sage: list(I._elements_of_depth_iterator_rec(0)) - [[]] - sage: list(I._elements_of_depth_iterator_rec(1)) - [[0], [1]] - sage: list(I._elements_of_depth_iterator_rec(2)) - [[0, 0], [0, 1], [1, 0], [1, 1]] - sage: list(I._elements_of_depth_iterator_rec(3)) - [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]] - sage: list(I._elements_of_depth_iterator_rec(4)) - [] - """ - if depth == 0: - for node in self.roots(): - yield node - else: - for father in self._elements_of_depth_iterator_rec(depth - 1): - for node in self.children(father): - yield node - - def elements_of_depth_iterator(self, depth=0): - r""" - Return an iterator over the elements of ``self`` of given depth. - An element of depth `n` can be obtained applying `n` times the - children function from a root. - - EXAMPLES:: - - sage: from sage.combinat.backtrack import SearchForest - sage: S = SearchForest([(0,0)] , - ....: lambda x : [(x[0], x[1]+1)] if x[1] != 0 else [(x[0]+1,0), (x[0],1)], - ....: post_process = lambda x: x if ((is_prime(x[0]) and is_prime(x[1])) - ....: and ((x[0] - x[1]) == 2)) else None) - sage: p = S.elements_of_depth_iterator(8) - sage: next(p) - (5, 3) - sage: S = SearchForest(NN, lambda x : [], - ....: lambda x: x^2 if x.is_prime() else None) - sage: p = S.elements_of_depth_iterator(0) - sage: [next(p), next(p), next(p), next(p), next(p)] - [4, 9, 25, 49, 121] - """ - iter = self._elements_of_depth_iterator_rec(depth) - if hasattr(self, "post_process"): - iter = _imap_and_filter_none(self.post_process, iter) - return iter - - def __contains__(self, elt): - r""" - Return ``True`` if ``elt`` is in ``self``. - - .. warning:: - - This is achieved by iterating through the elements until - ``elt`` is found. In particular, this method will never - stop when ``elt`` is not in ``self`` and ``self`` is - infinite. - - EXAMPLES:: - - sage: from sage.combinat.backtrack import SearchForest - sage: S = SearchForest( [[]], lambda l: [l+[0], l+[1]] if len(l) < 3 else [], category=FiniteEnumeratedSets()) - sage: [4] in S - False - sage: [1] in S - True - sage: [1,1,1,1] in S - False - sage: all(S.__contains__(i) for i in iter(S)) - True - sage: S = SearchForest([1], lambda x: [x+1], category=InfiniteEnumeratedSets()) - sage: 1 in S - True - sage: 732 in S - True - sage: -1 in S # not tested : Will never stop - - The algorithm uses a random enumeration of the nodes of the - forest. This choice was motivated by examples in which both - depth first search and breadth first search failed. The - following example enumerates all ordered pairs of nonnegative - integers, starting from an infinite set of roots, where each - roots has an infinite number of children:: - - sage: from sage.combinat.backtrack import SearchForest - sage: S = SearchForest(Family(NN, lambda x : (x, 0)), - ....: lambda x : Family(PositiveIntegers(), lambda y : (x[0], y)) if x[1] == 0 else []) - sage: p = S.depth_first_search_iterator() - sage: [next(p), next(p), next(p), next(p), next(p), next(p), next(p)] - [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)] - sage: p = S.breadth_first_search_iterator() - sage: [next(p), next(p), next(p), next(p), next(p), next(p), next(p)] - [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0)] - sage: (0,0) in S - True - sage: (1,1) in S - True - sage: (10,10) in S - True - sage: (42,18) in S - True - - We now consider the same set of all ordered pairs of - nonnegative integers but constructed in a different way. There - still are infinitely many roots, but each node has a single - child. From each root starts an infinite branch of breadth - `1`:: - - sage: S = SearchForest(Family(NN, lambda x : (x, 0)) , lambda x : [(x[0], x[1]+1)]) - sage: p = S.depth_first_search_iterator() - sage: [next(p), next(p), next(p), next(p), next(p), next(p), next(p)] - [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)] - sage: p = S.breadth_first_search_iterator() - sage: [next(p), next(p), next(p), next(p), next(p), next(p), next(p)] - [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0)] - sage: (0,0) in S - True - sage: (1,1) in S - True - sage: (10,10) in S - True - sage: (37,11) in S - True - """ - stack = [iter(self.roots())] - while stack: - position = randint(0,len(stack)-1) - try: - node = next(stack[position]) - except StopIteration: - stack.pop(position) - continue - - if node == elt: - return True - stack.append( iter(self.children(node)) ) - return False - - def map_reduce(self, map_function = None, - reduce_function = None, - reduce_init = None): - r""" - Apply a Map/Reduce algorithm on ``self`` - - INPUT: - - - ``map_function`` -- a function from the element of ``self`` to some - set with a reduce operation (e.g.: a monoid). The default value is - the constant function ``1``. - - - ``reduce_function`` -- the reduce function (e.g.: the addition of a - monoid). The default value is ``+``. - - - ``reduce_init`` -- the initialisation of the reduction (e.g.: the - neutral element of the monoid). The default value is ``0``. - - .. note:: - - the effect of the default values is to compute the cardinality - of ``self``. - - EXAMPLES:: - - sage: seeds = [([i],i, i) for i in range(1,10)] - sage: def succ(t): - ....: list, sum, last = t - ....: return [(list + [i], sum + i, i) for i in range(1, last)] - sage: F = RecursivelyEnumeratedSet(seeds, succ, - ....: structure='forest', enumeration='depth') - - sage: y = var('y') - sage: def map_function(t): - ....: li, sum, _ = t - ....: return y ^ sum - sage: reduce_function = lambda x,y: x + y - sage: F.map_reduce(map_function, reduce_function, 0) - y^45 + y^44 + y^43 + 2*y^42 + 2*y^41 + 3*y^40 + 4*y^39 + 5*y^38 + 6*y^37 + 8*y^36 + 9*y^35 + 10*y^34 + 12*y^33 + 13*y^32 + 15*y^31 + 17*y^30 + 18*y^29 + 19*y^28 + 21*y^27 + 21*y^26 + 22*y^25 + 23*y^24 + 23*y^23 + 23*y^22 + 23*y^21 + 22*y^20 + 21*y^19 + 21*y^18 + 19*y^17 + 18*y^16 + 17*y^15 + 15*y^14 + 13*y^13 + 12*y^12 + 10*y^11 + 9*y^10 + 8*y^9 + 6*y^8 + 5*y^7 + 4*y^6 + 3*y^5 + 2*y^4 + 2*y^3 + y^2 + y - - Here is an example with the default values:: - - sage: F.map_reduce() - 511 - - .. SEEALSO:: :mod:`sage.parallel.map_reduce` - """ - import sage.parallel.map_reduce - return sage.parallel.map_reduce.RESetMapReduce( - forest = self, - map_function = map_function, - reduce_function = reduce_function, - reduce_init = reduce_init).run() - - -class PositiveIntegerSemigroup(UniqueRepresentation, SearchForest): +class PositiveIntegerSemigroup(UniqueRepresentation, RecursivelyEnumeratedSet_forest): r""" The commutative additive semigroup of positive integers. This class provides an example of algebraic structure which - inherits from :class:`SearchForest`. It builds the positive + inherits from :class:`RecursivelyEnumeratedSet_forest`. It builds the positive integers a la Peano, and endows it with its natural commutative additive semigroup structure. @@ -805,7 +132,7 @@ def __init__(self): sage: from sage.combinat.backtrack import PositiveIntegerSemigroup sage: PP = PositiveIntegerSemigroup() """ - SearchForest.__init__(self, facade = ZZ, category=(InfiniteEnumeratedSets(), CommutativeAdditiveSemigroups(), Monoids())) + RecursivelyEnumeratedSet_forest.__init__(self, facade = ZZ, category=(InfiniteEnumeratedSets(), CommutativeAdditiveSemigroups(), Monoids())) def roots(self): r""" @@ -847,231 +174,3 @@ def one(self): 1 """ return self.first() - -class TransitiveIdeal(RecursivelyEnumeratedSet_generic): - r""" - Generic tool for constructing ideals of a relation. - - INPUT: - - - ``relation`` -- a function (or callable) returning a list (or iterable) - - ``generators`` -- a list (or iterable) - - Returns the set `S` of elements that can be obtained by repeated - application of ``relation`` on the elements of ``generators``. - - Consider ``relation`` as modeling a directed graph (possibly with - loops, cycles, or circuits). Then `S` is the ideal generated by - ``generators`` under this relation. - - Enumerating the elements of `S` is achieved by depth first search - through the graph. The time complexity is `O(n+m)` where `n` is - the size of the ideal, and `m` the number of edges in the - relation. The memory complexity is the depth, that is the maximal - distance between a generator and an element of `S`. - - See also :class:`SearchForest` and :class:`TransitiveIdealGraded`. - - EXAMPLES:: - - sage: from sage.combinat.backtrack import TransitiveIdeal - sage: [i for i in TransitiveIdeal(lambda i: [i+1] if i<10 else [], [0])] - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - - sage: [i for i in TransitiveIdeal(lambda i: [mod(i+1,3)], [0])] - [0, 1, 2] - sage: [i for i in TransitiveIdeal(lambda i: [mod(i+2,3)], [0])] - [0, 2, 1] - sage: [i for i in TransitiveIdeal(lambda i: [mod(i+2,10)], [0])] - [0, 2, 4, 6, 8] - sage: sorted(i for i in TransitiveIdeal(lambda i: [mod(i+3,10),mod(i+5,10)], [0])) - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - sage: sorted(i for i in TransitiveIdeal(lambda i: [mod(i+4,10),mod(i+6,10)], [0])) - [0, 2, 4, 6, 8] - sage: [i for i in TransitiveIdeal(lambda i: [mod(i+3,9)], [0,1])] - [0, 1, 3, 4, 6, 7] - - sage: [p for p in TransitiveIdeal(lambda x:[x],[Permutation([3,1,2,4]), Permutation([2,1,3,4])])] - [[2, 1, 3, 4], [3, 1, 2, 4]] - - We now illustrate that the enumeration is done lazily, by depth first - search:: - - sage: C = TransitiveIdeal(lambda x: [x-1, x+1], (-10, 0, 10)) - sage: f = C.__iter__() - sage: [ next(f) for i in range(6) ] - [0, 1, 2, 3, 4, 5] - - We compute all the permutations of 3:: - - sage: sorted(p for p in TransitiveIdeal(attrcall("permutohedron_succ"), [Permutation([1,2,3])])) - [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] - - We compute all the permutations which are larger than [3,1,2,4], - [2,1,3,4] in the right permutohedron:: - - sage: sorted(p for p in TransitiveIdeal(attrcall("permutohedron_succ"), - ....: [Permutation([3,1,2,4]), Permutation([2,1,3,4])])) - [[2, 1, 3, 4], [2, 1, 4, 3], [2, 3, 1, 4], [2, 3, 4, 1], - [2, 4, 1, 3], [2, 4, 3, 1], [3, 1, 2, 4], [3, 1, 4, 2], - [3, 2, 1, 4], [3, 2, 4, 1], [3, 4, 1, 2], [3, 4, 2, 1], - [4, 2, 1, 3], [4, 2, 3, 1], [4, 3, 1, 2], [4, 3, 2, 1]] - - Using TransitiveIdeal people have been using the ``__contains__`` - method provided from the ``__iter__`` method. We need to make sure that - this continues to work:: - - sage: T = TransitiveIdeal(lambda a:[a+7,a+5], [0]) - sage: 12 in T - True - - """ - def __init__(self, succ, generators): - r""" - TESTS:: - - sage: from sage.combinat.backtrack import TransitiveIdeal - sage: C = TransitiveIdeal(factor, (1, 2, 3)) - sage: C._succ - - sage: C._generators - (1, 2, 3) - sage: loads(dumps(C)) # should test for equality with C, but equality is not implemented - """ - RecursivelyEnumeratedSet_generic.__init__(self, seeds=generators, successors=succ, enumeration='naive') - self._generators = self._seeds - self._succ = self.successors - - def __iter__(self): - r""" - Return an iterator on the elements of ``self``. - - TESTS:: - - sage: from sage.combinat.backtrack import TransitiveIdeal - sage: C = TransitiveIdeal(lambda x: [1,2], ()) - sage: list(C) # indirect doctest - [] - - sage: C = TransitiveIdeal(lambda x: [1,2], (1,)) - sage: list(C) # indirect doctest - [1, 2] - - sage: C = TransitiveIdeal(lambda x: [], (1,2)) - sage: list(C) # indirect doctest - [1, 2] - - """ - return self.naive_search_iterator() - -class TransitiveIdealGraded(RecursivelyEnumeratedSet_generic): - r""" - Generic tool for constructing ideals of a relation. - - INPUT: - - - ``relation`` -- a function (or callable) returning a list (or iterable) - - - ``generators`` -- a list (or iterable) - - - ``max_depth`` -- (Default: infinity) Specifies the maximal depth to - which elements are computed - - Return the set `S` of elements that can be obtained by repeated - application of ``relation`` on the elements of ``generators``. - - Consider ``relation`` as modeling a directed graph (possibly with - loops, cycles, or circuits). Then `S` is the ideal generated by - ``generators`` under this relation. - - Enumerating the elements of `S` is achieved by breadth first search - through the graph; hence elements are enumerated by increasing - distance from the generators. The time complexity is `O(n+m)` - where `n` is the size of the ideal, and `m` the number of edges in - the relation. The memory complexity is the depth, that is the - maximal distance between a generator and an element of `S`. - - See also :class:`SearchForest` and :class:`TransitiveIdeal`. - - EXAMPLES:: - - sage: from sage.combinat.backtrack import TransitiveIdealGraded - sage: [i for i in TransitiveIdealGraded(lambda i: [i+1] if i<10 else [], [0])] - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - - We now illustrate that the enumeration is done lazily, by breadth first search:: - - sage: C = TransitiveIdealGraded(lambda x: [x-1, x+1], (-10, 0, 10)) - sage: f = C.__iter__() - - The elements at distance 0 from the generators:: - - sage: sorted([ next(f) for i in range(3) ]) - [-10, 0, 10] - - The elements at distance 1 from the generators:: - - sage: sorted([ next(f) for i in range(6) ]) - [-11, -9, -1, 1, 9, 11] - - The elements at distance 2 from the generators:: - - sage: sorted([ next(f) for i in range(6) ]) - [-12, -8, -2, 2, 8, 12] - - The enumeration order between elements at the same distance is not specified. - - We compute all the permutations which are larger than [3,1,2,4] or - [2,1,3,4] in the permutohedron:: - - sage: sorted(p for p in TransitiveIdealGraded(attrcall("permutohedron_succ"), - ....: [Permutation([3,1,2,4]), Permutation([2,1,3,4])])) - [[2, 1, 3, 4], [2, 1, 4, 3], [2, 3, 1, 4], [2, 3, 4, 1], - [2, 4, 1, 3], [2, 4, 3, 1], [3, 1, 2, 4], [3, 1, 4, 2], - [3, 2, 1, 4], [3, 2, 4, 1], [3, 4, 1, 2], [3, 4, 2, 1], - [4, 2, 1, 3], [4, 2, 3, 1], [4, 3, 1, 2], [4, 3, 2, 1]] - """ - def __init__(self, succ, generators, max_depth=float("inf")): - r""" - TESTS:: - - sage: from sage.combinat.backtrack import TransitiveIdealGraded - sage: C = TransitiveIdealGraded(factor, (1, 2, 3)) - sage: C._succ - - sage: C._generators - (1, 2, 3) - sage: loads(dumps(C)) # should test for equality with C, but equality is not implemented - """ - RecursivelyEnumeratedSet_generic.__init__(self, seeds=generators, successors=succ, enumeration='breadth', max_depth=max_depth) - self._generators = self._seeds - self._succ = self.successors - - def __iter__(self): - r""" - Return an iterator on the elements of ``self``. - - TESTS:: - - sage: from sage.combinat.backtrack import TransitiveIdealGraded - sage: C = TransitiveIdealGraded(lambda x: [1,2], ()) - sage: list(C) # indirect doctest - [] - - sage: C = TransitiveIdealGraded(lambda x: [1,2], (1,)) - sage: list(C) # indirect doctest - [1, 2] - - sage: C = TransitiveIdealGraded(lambda x: [], (1,2)) - sage: list(C) # indirect doctest - [1, 2] - - :: - - sage: fn = lambda i: [i+1] if i<10 else [] - sage: C = TransitiveIdealGraded(fn, [0], max_depth=1) - sage: list(C) - [0, 1] - """ - return self.breadth_first_search_iterator() - diff --git a/src/sage/combinat/baxter_permutations.py b/src/sage/combinat/baxter_permutations.py index f31ad486eb7..8464f8d27b3 100644 --- a/src/sage/combinat/baxter_permutations.py +++ b/src/sage/combinat/baxter_permutations.py @@ -2,7 +2,6 @@ """ Baxter permutations """ -from six.moves import range from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent diff --git a/src/sage/combinat/binary_recurrence_sequences.py b/src/sage/combinat/binary_recurrence_sequences.py index 9b3aa121faa..ae911dfc154 100644 --- a/src/sage/combinat/binary_recurrence_sequences.py +++ b/src/sage/combinat/binary_recurrence_sequences.py @@ -16,7 +16,7 @@ [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1] sage: R.period(4) #the period of the fibonacci sequence modulo 4 6 - sage: R.pthpowers(2, 10**30) # long time (7 seconds) -- in fact these are all squares, c.f. [BMS06] + sage: R.pthpowers(2, 10**10) # long time (7 seconds) -- in fact these are all squares, c.f. [BMS06] [0, 1, 2, 12] sage: S = BinaryRecurrenceSequence(8,1) #a Lucas sequence @@ -24,7 +24,7 @@ 148 sage: S(5) % 73 == S(5 +148) %73 True - sage: S.pthpowers(3,10**30) # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^30 + sage: S.pthpowers(3,10**10) # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^10 [0, 1, 2] sage: T = BinaryRecurrenceSequence(2,0,1,2) @@ -59,15 +59,14 @@ from __future__ import division -from six.moves import range - from sage.structure.sage_object import SageObject from sage.matrix.constructor import matrix +from sage.modules.free_module_element import vector from sage.rings.number_field.number_field import QuadraticField from sage.rings.finite_rings.integer_mod_ring import Integers from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.integer import Integer -from sage.arith.all import gcd, lcm, next_prime, is_prime, next_prime_power, legendre_symbol +from sage.arith.all import lcm, next_prime, is_prime, next_prime_power, legendre_symbol from sage.functions.log import log from sage.functions.other import sqrt @@ -131,7 +130,6 @@ def __init__(self, b, c, u0=0, u1=1): self._PGoodness = {} #dictionary to cache primes that are "good" by some prime power self._ell = 1 #variable that keeps track of the last prime power to be used as a goodness - def __repr__(self): """ Give string representation of the class. @@ -160,13 +158,10 @@ def __eq__(self, other): sage: T = BinaryRecurrenceSequence(3,3,2,2) sage: R == T False - """ - return (self.u0 == other.u0) and (self.u1 == other.u1) and (self.b == other.b) and (self.c == other.c) def __call__(self, n, modulus=0): - """ Give the nth term of a binary recurrence sequence, possibly mod some modulus. @@ -191,12 +186,12 @@ def __call__(self, n, modulus=0): 9 sage: R(101)%12 9 - """ R = Integers(modulus) - F = matrix(R, [[0,1],[self.c,self.b]]) # F*[u_{n}, u_{n+1}]^T = [u_{n+1}, u_{n+2}]^T (T indicates transpose). - v = matrix(R, [[self.u0],[self.u1]]) - return list(F**n*v)[0][0] + F = matrix(R, [[0, 1], [self.c, self.b]]) + # F*[u_{n}, u_{n+1}]^T = [u_{n+1}, u_{n+2}]^T (T indicates transpose). + v = vector(R, [self.u0, self.u1]) + return list(F**n * v)[0] def is_degenerate(self): """ @@ -239,16 +234,13 @@ def is_degenerate(self): True sage: T.is_arithmetic() True - """ - - if (self.b**2+4*self.c) != 0: - - if (self.b**2+4*self.c).is_square(): - A = sqrt((self.b**2+4*self.c)) - + D = self.b**2 + 4 * self.c + if D != 0: + if D.is_square(): + A = sqrt(D) else: - K = QuadraticField((self.b**2+4*self.c), 'x') + K = QuadraticField(D, 'x') A = K.gen() aa = (self.u1 - self.u0*(self.b + A)/2)/(A) #called `a` in Docstring @@ -256,17 +248,15 @@ def is_degenerate(self): #(b+A)/2 is called alpha in Docstring, (b-A)/2 is called beta in Docstring - if (self.b - A) != 0: - if ((self.b+A)/(self.b-A))**(6) == 1: + if self.b != A: + if ((self.b+A)/(self.b-A))**6 == 1: return True else: return True - if aa*bb*(self.b + A)*(self.b - A) == 0: - return True - return False - return True + return aa*bb*(self.b + A)*(self.b - A) == 0 + return True def is_geometric(self): """ @@ -283,15 +273,13 @@ def is_geometric(self): [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] sage: S.is_geometric() True - """ - #If [u_0, u_1]^T is an eigenvector for the incrementation matrix F = [[0,1],[c,b]], then the sequence #is geometric, ie we can write u_n = a*r^n for some a and r. #We decide if u0, u1, u2 = b*u1+c*u0 are in geometric progression by whether u1^2 = (b*u1+c*u0)*u0 - return bool((self.u1)**2 == (self.b*self.u1 + self.c*self.u0)*self.u0) + return (self.u1)**2 == (self.b*self.u1 + self.c*self.u0)*self.u0 def is_quasigeometric(self): """ @@ -320,24 +308,20 @@ def is_quasigeometric(self): sage: R.is_quasigeometric() True """ - - #First test if F is singular... i.e. beta = 0 + # First test if F is singular... i.e. beta = 0 if self.c == 0: return True - #Otherwise test if alpha/beta is a root of unity that is not 1 - else: - if (self.b**2+4*self.c) != 0: #thus alpha/beta != 1 - - if (self.b**2+4*self.c).is_square(): - A = sqrt((self.b**2+4*self.c)) - - else: - K = QuadraticField((self.b**2+4*self.c), 'x') - A = K.gen() - - if ((self.b+A)/(self.b-A))**(6) == 1: - return True + # Otherwise test if alpha/beta is a root of unity that is not 1 + D = self.b**2 + 4 * self.c + if D != 0: # thus alpha/beta != 1 + if D.is_square(): + A = sqrt(D) + else: + K = QuadraticField(D, 'x') + A = K.gen() + if ((self.b+A)/(self.b-A))**6 == 1: + return True return False @@ -358,11 +342,7 @@ def is_arithmetic(self): sage: S.is_arithmetic() True """ - - #Test if u_1-u_0 = u_2-u_1 = u_3-u_2 - - return bool(self(1) - self(0) == self(2) - self(1) == self(3) - self(2)) - + return (self(1) - self(0) == self(2) - self(1) == self(3) - self(2)) def period(self, m): """ @@ -410,7 +390,7 @@ def period(self, m): sage: S.period(17) 8 - Note: the answer is cached. + .. NOTE:: The answer is cached. """ #If we have already computed the period mod m, then we return the stored value. @@ -420,8 +400,8 @@ def period(self, m): else: R = Integers(m) - A = matrix(R, [[0,1],[self.c,self.b]]) - w = matrix(R, [[self.u0],[self.u1]]) + A = matrix(R, [[0, 1], [self.c, self.b]]) + w = vector(R, [self.u0, self.u1]) Fac = list(m.factor()) Periods = {} @@ -435,7 +415,8 @@ def period(self, m): #multiplying the period mod p by powers of p. for i in Fac: - p = i[0]; e = i[1] + p = i[0] + e = i[1] #first compute the period mod p if p in self._period_dict: perp = self._period_dict[p] @@ -459,7 +440,7 @@ def period(self, m): Mfac = p1fac #check if the trace is 2, then the order is a multiple of p dividing p*(p-1) - elif (FF).trace() == 2: + elif FF.trace() == 2: M = p-1 Mfac = p1fac F = F**p #replace F by F^p as now we only need to determine the factor dividing (p-1) @@ -469,12 +450,12 @@ def period(self, m): M = (p+1)*(p-1) p2fac = list((p+1).factor()) #factor the (p+1) and (p-1) terms separately and then combine for speed Mfac_dic = {} - for i in list(p1fac + p2fac): - if i[0] not in Mfac_dic: - Mfac_dic[i[0]] = i[1] + for i0, i1 in list(p1fac + p2fac): + if i0 not in Mfac_dic: + Mfac_dic[i0] = i1 else: - Mfac_dic[i[0]] = Mfac_dic[i[0]] + i[1] - Mfac = [(i,Mfac_dic[i]) for i in Mfac_dic] + Mfac_dic[i0] += i1 + Mfac = list(Mfac_dic.items()) #Now use a fast order algorithm to compute the period. We know that the period divides #M = i_1*i_2*...*i_l where the i_j denote not necessarily distinct prime factors. As @@ -482,19 +463,19 @@ def period(self, m): #all factors have been iterated over, the result is the period mod p. Mfac = list(Mfac) - C=[] + C = [] #expand the list of prime factors so every factor is with multiplicity 1 - for i in range(len(Mfac)): - for j in range(Mfac[i][1]): - C.append(Mfac[i][0]) + for i0, i1 in Mfac: + for j in range(i1): + C.append(i0) Mfac = C n = M - for i in Mfac: - b = Integer(n/i) - if F**b*v == v: + for ii in Mfac: + b = n // ii + if F**b * v == v: n = b perp = n @@ -517,12 +498,11 @@ def period(self, m): #take the lcm of the periods mod all distinct primes dividing m period = 1 for p in Periods: - period = lcm(Periods[p],period) + period = lcm(Periods[p], period) self._period_dict[m] = period #cache the period mod m return period - def pthpowers(self, p, Bound): """ Find the indices of proveably all pth powers in the recurrence sequence bounded by Bound. @@ -544,15 +524,15 @@ def pthpowers(self, p, Bound): EXAMPLES:: sage: R = BinaryRecurrenceSequence(1,1) #the Fibonacci sequence - sage: R.pthpowers(2, 10**30) # long time (7 seconds) -- in fact these are all squares, c.f. [BMS2006]_ + sage: R.pthpowers(2, 10**10) # long time (7 seconds) -- in fact these are all squares, c.f. [BMS2006]_ [0, 1, 2, 12] sage: S = BinaryRecurrenceSequence(8,1) #a Lucas sequence - sage: S.pthpowers(3,10**30) # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^30 + sage: S.pthpowers(3,10**10) # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^10 [0, 1, 2] sage: Q = BinaryRecurrenceSequence(3,3,2,1) - sage: Q.pthpowers(11,10**30) # long time (7.5 seconds) + sage: Q.pthpowers(11,10**10) # long time (7.5 seconds) [1] If the sequence is degenerate, and there are no ``p`` th powers, returns `[]`. Otherwise, if @@ -578,10 +558,11 @@ def pthpowers(self, p, Bound): sage: L.pthpowers(2,10**30) [] - NOTE: This function is primarily optimized in the range where ``Bound`` is much larger than ``p``. + .. NOTE:: + This function is primarily optimized in the range where + ``Bound`` is much larger than ``p``. """ - #Thanks to Jesse Silliman for helpful conversations! #Reset the dictionary of good primes, as this depends on p @@ -601,7 +582,7 @@ def pthpowers(self, p, Bound): no_powers = False break if no_powers: - if _is_p_power(self.u0,p): + if _is_p_power(self.u0, p): return [0] return [] else: @@ -621,7 +602,7 @@ def pthpowers(self, p, Bound): #Look at classes n = k mod p, for k = 1,...,p. - for k in range(1,p+1): + for k in range(1, p + 1): #The linear equation alpha^(k-1)*u_0 + (k+pm)*(alpha^(k-1)*u1 - u0*alpha^k) #must thus be a pth power. This is a linear equation in m, namely, A + B*m, where @@ -660,10 +641,11 @@ def pthpowers(self, p, Bound): ell = p + 1 while not is_prime(ell): - ell = ell + p + ell += p F = GF(ell) - a0 = F(self.u0); a1 = F(self.u1) #a0 and a1 are variables for terms in sequence + a0 = F(self.u0) + a1 = F(self.u1) #a0 and a1 are variables for terms in sequence bf, cf = F(self.b), F(self.c) for n in range(Bound): # n is the index of the a0 @@ -694,7 +676,7 @@ def pthpowers(self, p, Bound): #Try to get good data mod M2 #patience of how long we should search for a "good prime" - patience = 0.01 * _estimated_time(lcm(M2,p*next_prime_power(qq)), M1, len(cong), p) + patience = 0.01 * _estimated_time(lcm(M2, p*next_prime_power(qq)), M1, len(cong), p) tries = 0 #This loop uses primes to get a small set of congruences mod M2. @@ -731,19 +713,19 @@ def pthpowers(self, p, Bound): cong = list(cong) qqold = qq qq = next_prime_power(qq) - M2 = lcm(M2,p*qq) + M2 = lcm(M2, p * qq) break else: qq = next_prime_power(qq) - M2 = lcm(M2,p*qq) + M2 = lcm(M2, p * qq) cong = list(cong) break #Document how long each element of cong has been there for i in cong: if i in Possible_count: - Possible_count[i] = Possible_count[i] + 1 + Possible_count[i] += 1 else: Possible_count[i] = 1 @@ -753,7 +735,7 @@ def pthpowers(self, p, Bound): if Possible_count[i] == 7: n = Integer(i) if n < Bound: - if _is_p_power(self(n),p): + if _is_p_power(self(n), p): powers.append(n) #check for a contradiction @@ -767,7 +749,6 @@ def pthpowers(self, p, Bound): def _prime_powers(N): - """ Find the prime powers dividing ``N``. @@ -789,25 +770,21 @@ def _prime_powers(N): sage: sage.combinat.binary_recurrence_sequences._prime_powers(65537) [65537] - """ - output = sorted([i ** j for i, j in N.factor()]) - return output + return sorted(i**j for i, j in N.factor()) -#This function finds the largest prime power divisor of an integer N def _largest_ppower_divisor(N): - """ Find the largest prime power divisor of N. INPUT: - - ``N`` -- an integer (of which the largest prime power divisor will be found) + - ``N`` -- an integer OUTPUT: - - The largest prime power dividing ``N``. + The largest prime power dividing ``N``. EXAMPLES:: @@ -815,16 +792,11 @@ def _largest_ppower_divisor(N): 53 sage: sage.combinat.binary_recurrence_sequences._largest_ppower_divisor(65537) 65537 - """ - - output = _prime_powers(N)[-1] - - return output + return _prime_powers(N)[-1] def _goodness(n, R, p): - """ Return the goodness of ``n`` for the sequence ``R`` and the prime ``p`` -- that is the largest non-``p`` prime power dividing ``period(n)``. @@ -852,13 +824,10 @@ def _goodness(n, R, p): 4 sage: R.period(13) #the period of R mod 13 is divisible by 7 28 - """ - - #The period of R mod ell + # The period of R mod ell K = R.period(n) - - return _largest_ppower_divisor(K/gcd(K,p)) + return _largest_ppower_divisor(K // K.gcd(p)) def _next_good_prime(p, R, qq, patience, qqold): @@ -914,14 +883,13 @@ def _next_good_prime(p, R, qq, patience, qqold): #Possible_Primes keeps track of possible primes satisfying our goodness requirements we might return Possible_Primes = [] - #check to see if anything in R._PGoodness fits our goodness requirements for j in R._PGoodness: if (qqold < j <= qq) and len(R._PGoodness[j]): Possible_Primes.append(R._PGoodness[j][0]) #If we found good primes, we take the smallest - if Possible_Primes != []: + if Possible_Primes: q = min(Possible_Primes) n = _goodness(q, R, p) del R._PGoodness[n][0] #if we are going to use it, then we delete it from R._PGoodness @@ -1002,9 +970,8 @@ def _is_p_power_mod(a, p, N): if v >= e: continue - #otherwise, it can only be a pth power if v is a multiple of p. - - if v % p != 0: + # otherwise, it can only be a pth power if v is a multiple of p. + if v % p: return False #in this cse it is a pth power if x is a pth power mod q^(e-v), so let x = aa, @@ -1039,7 +1006,7 @@ def _is_p_power_mod(a, p, N): #We use the strong statement of Hensel's lemma, which implies that if p is odd #and aa is a pth power mod p^2, then aa is a pth power mod any higher power of p - if p % 2 == 1: + if p % 2: #ZZ/(p^2)ZZ^\times is abstractly isomorphic to ZZ/(p)ZZ cross ZZ/(p-1)ZZ. then #aa is a pth power mod p^2 if (aa)^(p*(p-1)/p) == 1, ie if aa^(p-1) == 1. @@ -1101,12 +1068,12 @@ def _estimated_time(M2, M1, length, p): return (length * (Q/p)**NPrimes).n() + #Find the list of necessary congruences for the index n of binary recurrence #sequence R using the fact that the reduction mod ell must be a pth power def _find_cong1(p, R, ell): - """ - Find the list of permissible indices `n` for which `u_n = y^p` mod ``ell``. + Find the list of permissible indices `n` for which `u_n = y^p` mod ``ell``. INPUT: @@ -1125,18 +1092,16 @@ def _find_cong1(p, R, ell): sage: R = BinaryRecurrenceSequence(1,1) sage: sage.combinat.binary_recurrence_sequences._find_cong1(7, R, 29) ([0, 1, 2, 12, 13], 14) - """ - F = GF(ell) - u0 = F(R.u0); u1 = F(R.u1) + u0 = F(R.u0) + u1 = F(R.u1) bf, cf = F(R.b), F(R.c) - a0 = u0; a1 = u1 #a0 and a1 are variables for terms in sequence + a0 = u0 + a1 = u1 #a0 and a1 are variables for terms in sequence #The set of pth powers mod ell - PPowers = set([]) - for i in F: - PPowers.add(i**p) + PPowers = set(i**p for i in F) #The period of R mod ell modu = R.period(ell) @@ -1159,11 +1124,9 @@ def _find_cong1(p, R, ell): return cong1, modu -#check for when a is a perfect pth power def _is_p_power(a, p): - """ - Determine whether ``a`` is a ``p`` th power. + Determine whether ``a`` is a perfect ``p`` th power. INPUT: @@ -1175,17 +1138,14 @@ def _is_p_power(a, p): - True if ``a`` is a ``p`` th power; else False. - EXAMPLES:: sage: sage.combinat.binary_recurrence_sequences._is_p_power(2**7,7) True sage: sage.combinat.binary_recurrence_sequences._is_p_power(2**7*3**2,7) False - """ - - return (int(a**(1/p))**p == a) - - - + return int(a**(1/p))**p == a + # slower tentative ? + # _, test = Integer(a).nth_root(p, truncate_mode=True) + # return test diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index 07e9ec1a28d..01a1b0bdc13 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -23,10 +23,6 @@ # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -# python3 -from __future__ import division, absolute_import -from six import add_metaclass - from sage.structure.list_clone import ClonableArray from sage.combinat.abstract_tree import (AbstractClonableTree, AbstractLabelledClonableTree) @@ -45,8 +41,8 @@ from sage.misc.cachefunc import cached_method -@add_metaclass(InheritComparisonClasscallMetaclass) -class BinaryTree(AbstractClonableTree, ClonableArray): +class BinaryTree(AbstractClonableTree, ClonableArray, + metaclass=InheritComparisonClasscallMetaclass): """ Binary trees. @@ -2017,15 +2013,9 @@ def in_order_traversal_iter(self): if self.is_empty(): yield self return - # TODO:: PYTHON 3 - # yield from self[0].in_order_traversal_iter() - for left_subtree in self[0].in_order_traversal_iter(): - yield left_subtree + yield from self[0].in_order_traversal_iter() yield self - # TODO:: PYTHON 3 - # yield from self[1].in_order_traversal_iter() - for right_subtree in self[1].in_order_traversal_iter(): - yield right_subtree + yield from self[1].in_order_traversal_iter() def in_order_traversal(self, node_action=None, leaf_action=None): r""" @@ -3184,7 +3174,6 @@ def over(self, bt): else: return B([self[0], self[1].over(bt)]) - __div__ = over __truediv__ = over @combinatorial_map(name="Under operation on Binary Trees") diff --git a/src/sage/combinat/blob_algebra.py b/src/sage/combinat/blob_algebra.py new file mode 100644 index 00000000000..8d719b29103 --- /dev/null +++ b/src/sage/combinat/blob_algebra.py @@ -0,0 +1,681 @@ +# -*- coding: utf-8 -*- +r""" +Blob Algebras + +AUTHORS: + +- Travis Scrimshaw (2020-05-16): Initial version +""" + +# **************************************************************************** +# Copyright (C) 2020 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element import Element, get_coercion_model +from sage.structure.richcmp import richcmp +#from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass +from sage.misc.cachefunc import cached_method +from sage.misc.misc import powerset +from sage.functions.other import binomial +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.categories.algebras import Algebras +from sage.combinat.diagram_algebras import (TemperleyLiebDiagrams, diagram_latex, + TL_diagram_ascii_art) +from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.dyck_word import DyckWords + +#@add_metaclass(InheritComparisonClasscallMetaclass) +class BlobDiagram(Element): + r""" + A blob diagram. + + A blob diagram consists of a perfect matching of the set + `\{1, \ldots, n\} \sqcup \{-1, \ldots, -n\}` such that the result + is a noncrossing matching (a :class:`Temperley-Lieb diagram + `), divided + into two sets of pairs: one for the pairs with blobs and one for + those without. The blobed pairs must either be either the leftmost + propagating strand or to the left of it and not nested. + """ + def __init__(self, parent, marked, unmarked): + r""" + Initialize ``self``. + + TESTS:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BD4 = BlobDiagrams(4) + sage: B = BD4([[1,-3]], [[2,-4], [3,4], [-1,-2]]) + sage: TestSuite(B).run() + """ + Element.__init__(self, parent) + self.marked = tuple(sorted([tuple(sorted(pair)) for pair in marked])) + self.unmarked = tuple(sorted([tuple(sorted(pair)) for pair in unmarked])) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BD4 = BlobDiagrams(4) + sage: BD4([[1,-3]], [[2,-4], [3,4], [-1,-2]]) + ({{-3, 1}}, {{-4, 2}, {-2, -1}, {3, 4}}) + """ + return '({{{}}}, {{{}}})'.format(', '.join('{' + repr(X)[1:-1] + '}' + for X in self.marked), + ', '.join('{' + repr(X)[1:-1] + '}' + for X in self.unmarked)) + + def __hash__(self): + r""" + Return the hash of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BD4 = BlobDiagrams(4) + sage: B = BD4([[1,-3]], [[2,-4], [3,4], [-1,-2]]) + sage: hash(B) in [hash(D) for D in BD4] + True + sage: len(set([hash(D) for D in BD4])) == len(BD4) + True + """ + return hash((self.marked, self.unmarked)) + + def _richcmp_(self, other, op): + r""" + Compare ``self`` to ``other`` with operation ``op``. + + EXAMPLES:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BD4 = BlobDiagrams(4) + sage: B = BD4([[1,-3]], [[2,-4], [3,4], [-1,-2]]) + sage: any(B == D for D in BD4) + True + sage: B2 = BD4([], [[1,-3], [2,-4], [3,4], [-1,-2]]) + sage: B == B2 + False + sage: B != B2 + True + sage: sorted(BlobDiagrams(3)) + [({}, {{-3, -2}, {-1, 1}, {2, 3}}), + ({}, {{-3, -2}, {-1, 3}, {1, 2}}), + ({}, {{-3, 1}, {-2, -1}, {2, 3}}), + ({}, {{-3, 3}, {-2, -1}, {1, 2}}), + ({}, {{-3, 3}, {-2, 2}, {-1, 1}}), + ({{-3, 1}}, {{-2, -1}, {2, 3}}), + ({{-3, 3}}, {{-2, -1}, {1, 2}}), + ({{-2, -1}}, {{-3, 1}, {2, 3}}), + ({{-2, -1}}, {{-3, 3}, {1, 2}}), + ({{-1, 1}}, {{-3, -2}, {2, 3}}), + ({{-1, 1}}, {{-3, 3}, {-2, 2}}), + ({{-1, 3}}, {{-3, -2}, {1, 2}}), + ({{1, 2}}, {{-3, -2}, {-1, 3}}), + ({{1, 2}}, {{-3, 3}, {-2, -1}}), + ({{-3, 1}, {-2, -1}}, {{2, 3}}), + ({{-3, 3}, {-2, -1}}, {{1, 2}}), + ({{-3, 3}, {1, 2}}, {{-2, -1}}), + ({{-2, -1}, {1, 2}}, {{-3, 3}}), + ({{-1, 3}, {1, 2}}, {{-3, -2}}), + ({{-3, 3}, {-2, -1}, {1, 2}}, {})] + """ + return richcmp((len(self.marked), self.marked, self.unmarked), + (len(other.marked), other.marked, other.unmarked), + op) + + def temperley_lieb_diagram(self): + r""" + Return the Temperley-Lieb diagram corresponding to ``self``. + + EXAMPLES:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BD4 = BlobDiagrams(4) + sage: B = BD4([[1,-3]], [[2,-4], [3,4], [-1,-2]]) + sage: B.temperley_lieb_diagram() + {{-4, 2}, {-3, 1}, {-2, -1}, {3, 4}} + """ + return self.parent()._TL_diagrams(self.marked + self.unmarked) + +class BlobDiagrams(Parent, UniqueRepresentation): + r""" + The set of all blob diagrams. + """ + def __init__(self, n): + r""" + Initialize ``self``. + + TESTS:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BD4 = BlobDiagrams(4) + sage: TestSuite(BD4).run() + """ + self._n = n + self._TL_diagrams = TemperleyLiebDiagrams(n) + Parent.__init__(self, category=FiniteEnumeratedSets()) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BlobDiagrams(4) + Blob diagrams of order 4 + """ + return "Blob diagrams of order {}".format(self._n) + + def cardinality(self): + r""" + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BD4 = BlobDiagrams(4) + sage: BD4.cardinality() + 70 + """ + return binomial(2*self._n, self._n) + + def order(self): + r""" + Return the order of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BD4 = BlobDiagrams(4) + sage: BD4.order() + 4 + """ + return self._n + + @cached_method + def base_set(self): + r""" + Return the base set of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BD4 = BlobDiagrams(4) + sage: sorted(BD4.base_set()) + [-4, -3, -2, -1, 1, 2, 3, 4] + """ + return frozenset(range(1,self._n+1)).union(range(-self._n,0)) + + def _element_constructor_(self, marked, unmarked=None): + r""" + Construct an element of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BD4 = BlobDiagrams(4) + sage: BD4([[1,-3]], [[-1,-2], [2,3], [-4,4]]) + ({{-3, 1}}, {{-4, 4}, {-2, -1}, {2, 3}}) + sage: BD4([[(1,-3)], ([-1,-2], (2,3), [-4,4])]) + ({{-3, 1}}, {{-4, 4}, {-2, -1}, {2, 3}}) + """ + if unmarked is None: + marked, unmarked = marked + ret = self.element_class(self, marked, unmarked) + if ret not in self: + raise ValueError("not a blob diagram of order {}".format(self._n)) + return ret + + def __contains__(self, X): + r""" + Check if ``X`` is contained in ``self``. + + EXAMPLES:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BD4 = BlobDiagrams(4) + sage: BD4([[1,-3], [-1,-2]], [[2,-4], [3,4]]) # indirect doctest + ({{-3, 1}, {-2, -1}}, {{-4, 2}, {3, 4}}) + sage: BD4([[1,4], [-1,-2], [-3,-4]], [[2,3]]) # indirect doctest + ({{-4, -3}, {-2, -1}, {1, 4}}, {{2, 3}}) + + sage: BD4([[1,-2], [-1,-3]], [[2,-4], [3,4]]) # crossing strands + Traceback (most recent call last): + ... + ValueError: not a blob diagram of order 4 + sage: BD4([[1,-4], [-1,-2]], [[2,-3], [3,4]]) # crossing strands + Traceback (most recent call last): + ... + ValueError: not a blob diagram of order 4 + sage: BD4([[1,-2], [-1,-3]], [[3,-4], [2,4]]) # crossing strands + Traceback (most recent call last): + ... + ValueError: not a blob diagram of order 4 + sage: BD4([[1,-3], [-1,-2], [3,4]], [[2,-4]]) # trapped blob cup + Traceback (most recent call last): + ... + ValueError: not a blob diagram of order 4 + sage: BD4([[-1,3], [1,2], [-3,-4]], [[-2,4]]) # trapped blob cap + Traceback (most recent call last): + ... + ValueError: not a blob diagram of order 4 + sage: BD4([[1,4], [-1,-2], [-3,-4], [2,3]], []) # nested blob cup + Traceback (most recent call last): + ... + ValueError: not a blob diagram of order 4 + sage: BD4([[-1,-4], [1,2], [3,4], [-2,-3]], []) # nested blob cap + Traceback (most recent call last): + ... + ValueError: not a blob diagram of order 4 + sage: BD4([[3,-3]], [[1,-1],[2,-2],[4,-4]]) # trapped propogating line + Traceback (most recent call last): + ... + ValueError: not a blob diagram of order 4 + """ + if not isinstance(X, BlobDiagram): + return False + # Check that it is a Temperley-Lieb diagram + TL = X.marked + X.unmarked # the TL diagram + if TL not in self._TL_diagrams: + return False + # Check left escaping + for x, y in X.marked: + if x > 0: # Must be a cup + for P in TL: + if P[1] < 0: # P is a cap + continue + if P[1] < x: + if P[0] < 0: # A propogating line to the left + return False + else: # Note that P[1] != x + if 0 < P[0] < x: # A nesting line + return False + elif y < 0: # Must be a cap + for P in TL: + if P[0] > 0: # P is a cup + continue + if P[0] > y: + if P[1] > 0: # A propogating line to the left + return False + else: # Note that P[0] != y + if 0 > P[1] > y: # A nesting line + return False + else: # Must be a propogating line + if any(P[0] < 0 and P[1] > 0 and P[1] < y for P in TL): + return False + return True + + def __iter__(self): + r""" + Iterate over ``self``. + + EXAMPLES:: + + sage: from sage.combinat.blob_algebra import BlobDiagrams + sage: BD3 = BlobDiagrams(3) + sage: for b in BD3: b + ({}, {{-3, 3}, {-2, -1}, {1, 2}}) + ({{1, 2}}, {{-3, 3}, {-2, -1}}) + ({{-2, -1}}, {{-3, 3}, {1, 2}}) + ({{-2, -1}, {1, 2}}, {{-3, 3}}) + ({{-3, 3}}, {{-2, -1}, {1, 2}}) + ({{-3, 3}, {1, 2}}, {{-2, -1}}) + ({{-3, 3}, {-2, -1}}, {{1, 2}}) + ({{-3, 3}, {-2, -1}, {1, 2}}, {}) + ({}, {{-3, -2}, {-1, 3}, {1, 2}}) + ({{1, 2}}, {{-3, -2}, {-1, 3}}) + ({{-1, 3}}, {{-3, -2}, {1, 2}}) + ({{-1, 3}, {1, 2}}, {{-3, -2}}) + ({}, {{-3, 1}, {-2, -1}, {2, 3}}) + ({{-3, 1}}, {{-2, -1}, {2, 3}}) + ({{-2, -1}}, {{-3, 1}, {2, 3}}) + ({{-3, 1}, {-2, -1}}, {{2, 3}}) + ({}, {{-3, -2}, {-1, 1}, {2, 3}}) + ({{-1, 1}}, {{-3, -2}, {2, 3}}) + ({}, {{-3, 3}, {-2, 2}, {-1, 1}}) + ({{-1, 1}}, {{-3, 3}, {-2, 2}}) + """ + for D in DyckWords(self._n): + markable = set() + unmarked = [] + unpaired = [] + # Determine the pairing and which pairings are markable + for i,d in enumerate(D): + if i >= self._n: + i = -2*self._n + i + else: + i += 1 + if d == 1: + unpaired.append(i) + else: # d == 0 + m = unpaired.pop() + if not unpaired: + markable.add((m, i)) + else: + unmarked.append((m, i)) + for X in powerset(markable): + yield self.element_class(self, X, unmarked + list(markable.difference(X))) + + Element = BlobDiagram + +class BlobAlgebra(CombinatorialFreeModule): + r""" + The blob algebra. + + The *blob algebra* (also known as the Temperley-Lieb algebra of type `B` + in [ILZ2018]_, but is a quotient of the Temperley-Lieb algebra of type `B` + defined in [Graham1985]_) is a diagram-type algebra introduced in + [MS1994]_ whose basis consists of :class:`Temperley-Lieb diagrams + `, noncrossing + perfect matchings, that may contain blobs on strands that can be + deformed so that the blob touches the left side (which we can think of + as a frozen pole). + + The form we give here has 3 parameters, the natural one from the + :class:`Temperley-Lieb algebra `, + one for the idempotent relation, and one for a loop with a blob. + + INPUT: + + - ``k`` -- the order + - ``q1`` -- the loop parameter + - ``q2`` -- the idempotent parameter + - ``q3`` -- the blob loop parameter + + EXAMPLES:: + + sage: R. = ZZ[] + sage: B4 = algebras.Blob(4, q, r, s) + sage: B = list(B4.basis()) + sage: B[2] + B({{-4, -3}}, {{-2, -1}, {1, 2}, {3, 4}}) + sage: B[4] + B({{3, 4}}, {{-4, -3}, {-2, -1}, {1, 2}}) + sage: B[2] * B[4] + q*r*s*B({}, {{-4, -3}, {-2, -1}, {1, 2}, {3, 4}}) + + REFERENCES: + + - [MS1994]_ + - [ILZ2018]_ + """ + @staticmethod + def __classcall_private__(cls, k, q1, q2, q3, base_ring=None, prefix='B'): + r""" + Normalize input to ensure a unique representation. + + TESTS:: + + sage: R. = ZZ[] + sage: B3 = algebras.Blob(3, q, r, s) + sage: Bp = algebras.Blob(3, q, r, s, R, prefix='B') + sage: B3 is Bp + True + """ + if base_ring is None: + base_ring = get_coercion_model().common_parent(q1, q2, q3) + q1 = base_ring(q1) + q2 = base_ring(q2) + q3 = base_ring(q3) + return super(BlobAlgebra, cls).__classcall__(cls, k, q1, q2, q3, base_ring, prefix) + + def __init__(self, k, q1, q2, q3, base_ring, prefix): + r""" + Initialize ``self``. + + TESTS:: + + sage: R. = ZZ[] + sage: B4 = algebras.Blob(4, q, r, s) + sage: TestSuite(B4).run() + + sage: B3 = algebras.Blob(3, q, r, s) + sage: B = list(B3.basis()) + sage: TestSuite(B3).run(elements=B) # long time + """ + self._q1 = q1 + self._q2 = q2 + self._q3 = q3 + diagrams = BlobDiagrams(k) + cat = Algebras(base_ring.category()).FiniteDimensional().WithBasis() + CombinatorialFreeModule.__init__(self, base_ring, diagrams, category=cat, + prefix=prefix, bracket=False) + + def _ascii_art_term(self, diagram): + r""" + Return an ascii art representation of ``diagram``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: B2 = algebras.Blob(2, q, r, s) + sage: x = B2.an_element() + sage: ascii_art(x) # indirect doctest + o o o o o o + 2* `-` + 3* `-` + 2* `0` + .-. .0. .-. + o o o o o o + """ + return TL_diagram_ascii_art(diagram.marked+diagram.unmarked, use_unicode=False, + blobs=diagram.marked) + + def _unicode_art_term(self, diagram): + r""" + Return a unicode art representation of ``diagram``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: B2 = algebras.Blob(2, q, r, s) + sage: x = B2.an_element() + sage: unicode_art(x) # indirect doctest + ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ + 2* ╰─╯ + 3* ╰─╯ + 2* ╰●╯ + ╭─╮ ╭●╮ ╭─╮ + ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ + """ + return TL_diagram_ascii_art(diagram.marked+diagram.unmarked, use_unicode=True, + blobs=diagram.marked) + + def _latex_term(self, diagram): + r""" + Return a latex representation of ``diagram``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: B2 = algebras.Blob(2, q, r, s) + sage: latex(B2.an_element()) # indirect doctest + 2\begin{tikzpicture}[scale = 0.5,thick, baseline={(0,-1ex/2)}] + \tikzstyle{vertex} = [shape = circle, minimum size = 7pt, inner sep = 1pt] + \node[vertex] (G--2) at (1.5, -1) [shape = circle, draw] {}; + \node[vertex] (G--1) at (0.0, -1) [shape = circle, draw] {}; + \node[vertex] (G-1) at (0.0, 1) [shape = circle, draw] {}; + \node[vertex] (G-2) at (1.5, 1) [shape = circle, draw] {}; + \draw[] (G--2) .. controls +(-0.5, 0.5) and +(0.5, 0.5) .. (G--1); + \draw[] (G-1) .. controls +(0.5, -0.5) and +(-0.5, -0.5) .. (G-2); + \end{tikzpicture} + + 3\begin{tikzpicture}[scale = 0.5,thick, baseline={(0,-1ex/2)}] + \tikzstyle{vertex} = [shape = circle, minimum size = 7pt, inner sep = 1pt] + \node[vertex] (G--2) at (1.5, -1) [shape = circle, draw] {}; + \node[vertex] (G--1) at (0.0, -1) [shape = circle, draw] {}; + \node[vertex] (G-1) at (0.0, 1) [shape = circle, draw] {}; + \node[vertex] (G-2) at (1.5, 1) [shape = circle, draw] {}; + \draw[blue,very thick] (G--2) .. controls +(-0.5, 0.5) and +(0.5, 0.5) .. node[midway,circle,fill,scale=0.6] {} (G--1); + \draw[] (G-1) .. controls +(0.5, -0.5) and +(-0.5, -0.5) .. (G-2); + \end{tikzpicture} + + 2\begin{tikzpicture}[scale = 0.5,thick, baseline={(0,-1ex/2)}] + \tikzstyle{vertex} = [shape = circle, minimum size = 7pt, inner sep = 1pt] + \node[vertex] (G-1) at (0.0, 1) [shape = circle, draw] {}; + \node[vertex] (G-2) at (1.5, 1) [shape = circle, draw] {}; + \node[vertex] (G--2) at (1.5, -1) [shape = circle, draw] {}; + \node[vertex] (G--1) at (0.0, -1) [shape = circle, draw] {}; + \draw[blue,very thick] (G-1) .. controls +(0.5, -0.5) and +(-0.5, -0.5) .. node[midway,circle,fill,scale=0.6] {} (G-2); + \draw[] (G--2) .. controls +(-0.5, 0.5) and +(0.5, 0.5) .. (G--1); + \end{tikzpicture} + """ + def edge_options(P): + if P[1] < P[0]: + P = [P[1], P[0]] + if tuple(P) in diagram.marked: + return 'blue,very thick' + return '' + def edge_additions(P): + if P[1] < P[0]: + P = [P[1], P[0]] + if tuple(P) in diagram.marked: + return 'node[midway,circle,fill,scale=0.6] {} ' + return '' + return diagram_latex(diagram.marked+diagram.unmarked, + edge_options=edge_options, + edge_additions=edge_additions) + + def order(self): + r""" + Return the order of ``self``. + + The order of a partition algebra is defined as half of the number + of nodes in the diagrams. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: B4 = algebras.Blob(4, q, r, s) + sage: B4.order() + 4 + """ + return self._indices.order() + + @cached_method + def one_basis(self): + r""" + Return the index of the basis element `1`. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: B4 = algebras.Blob(4, q, r, s) + sage: B4.one_basis() + ({}, {{-4, 4}, {-3, 3}, {-2, 2}, {-1, 1}}) + """ + B = self._indices + return B.element_class(B, [], [[i, -i] for i in range(1, self.order()+1)]) + + def product_on_basis(self, top, bot): + r""" + Return the product of the basis elements indexed by ``top`` + and ``bot``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: B4 = algebras.Blob(4, q, r, s) + sage: B = B4.basis() + sage: BD = B.keys() + sage: BD[2] + ({{-4, -3}}, {{-2, -1}, {1, 2}, {3, 4}}) + sage: BD[4] + ({{3, 4}}, {{-4, -3}, {-2, -1}, {1, 2}}) + sage: B4.product_on_basis(BD[2], BD[4]) + q*r*s*B({}, {{-4, -3}, {-2, -1}, {1, 2}, {3, 4}}) + sage: all(len((x*y).support()) == 1 for x in B for y in B) + True + """ + ret_lists = [[], []] + coeff = self.base_ring().one() + top_marked = set(top.marked) + top_unmarked = set(top.unmarked) + bot_marked = set(bot.marked) + bot_unmarked = set(bot.unmarked) + + for top_set, is_unmarked in [(top_marked, 0), (top_unmarked, 1)]: + while top_set: + # We are starting a new strand + cur, stop = top_set.pop() # note that cur < stop + unmarked = is_unmarked + #print(top_set, unmarked, cur, stop) + if cur > 0: # Both are anchored to the top + ret_lists[unmarked].append((cur, stop)) + continue + anchored = bool(stop > 0) # Possibly only stop is anchored + + # Follow the path from cur until we either reach stop or + # we break out of the loop because both ends are anchored + while anchored or cur != stop: + #print(anchored, unmarked, cur, stop) + cur = -cur # Move cur to the bottom diagram + for X in bot_marked: + if cur in X: + if unmarked: + unmarked = 0 + else: + coeff *= self._q2 + prev = cur + cur = X[1-X.index(prev)] + bot_marked.remove(X) + break + for X in bot_unmarked: + if cur in X: + prev = cur + cur = X[1-X.index(prev)] + bot_unmarked.remove(X) + break + if cur < 0: # cur is anchored at the bottom + if anchored: + ret_lists[unmarked].append((stop, cur)) + break + else: + anchored = True + stop, cur = cur, stop # stop is now anchored to the bottom + continue + cur = -cur # bring cur back to the top diagram + for X in top_marked: + if cur in X: + if unmarked: + unmarked = 0 + else: + coeff *= self._q2 + prev = cur + cur = X[1-X.index(prev)] + top_marked.remove(X) + break + for X in top_unmarked: + if cur in X: + prev = cur + cur = X[1-X.index(prev)] + top_unmarked.remove(X) + break + if cur > 0: # cur is anchored at the top + if anchored: + ret_lists[unmarked].append((stop, cur)) + break + else: + anchored = True + stop, cur = cur, stop # stop is now anchored to the top + if cur == stop: # We have found a (marked) loop + if unmarked: + coeff *= self._q1 + else: + coeff *= self._q3 + # Everything remaining in the bottom sets are just anchored + # at the bottom, (i.e., are of the form {-i, -j}). + ret_lists[0].extend(bot_marked) + ret_lists[1].extend(bot_unmarked) + + if coeff == 0: + return self.zero() + diagram = self._indices.element_class(self._indices, ret_lists[0], ret_lists[1]) + return self._from_dict({diagram: coeff}, remove_zeros=False) + diff --git a/src/sage/combinat/cartesian_product.py b/src/sage/combinat/cartesian_product.py index 87d7037d517..e3310cfa56a 100644 --- a/src/sage/combinat/cartesian_product.py +++ b/src/sage/combinat/cartesian_product.py @@ -17,96 +17,15 @@ # **************************************************************************** from __future__ import absolute_import -from six.moves import range - from sage.categories.enumerated_sets import EnumeratedSets from sage.sets.set_from_iterator import EnumeratedSetFromIterator -from inspect import isgenerator import sage.misc.prandom as rnd from sage.misc.mrange import xmrange_iter, _is_finite, _len from .ranker import unrank from sage.rings.infinity import infinity -def CartesianProduct(*iters): - """ - This is deprecated. Use :obj:`cartesian_product` instead. - - EXAMPLES:: - - sage: cp = CartesianProduct([1,2], [3,4]); cp - doctest:...: DeprecationWarning: CartesianProduct is deprecated. Use - cartesian_product instead - See http://trac.sagemath.org/18411 for details. - The Cartesian product of ({1, 2}, {3, 4}) - sage: cp.list() - [(1, 3), (1, 4), (2, 3), (2, 4)] - - Note that you must not use a generator-type object that is - returned by a function (using "yield"). They cannot be copied or - rewound (you cannot jump back to the beginning), but this is - necessary to construct the Cartesian product:: - - sage: def a(n): yield 1*n; yield 2*n - sage: def b(): yield 'a'; yield 'b' - sage: CartesianProduct(a(3), b()).list() - Traceback (most recent call last): - ... - ValueError: generators are not allowed, see the - documentation (type "CartesianProduct?") for a workaround - - The usage of iterable is also deprecated, so the following will no longer be - supported:: - - sage: from sage.combinat.misc import IterableFunctionCall - sage: C = CartesianProduct(IterableFunctionCall(a, 3), IterableFunctionCall(b)) - doctest:...: DeprecationWarning: Usage of IterableFunctionCall in - CartesianProduct is deprecated. You can use EnumeratedSetFromIterator - (in sage.sets.set_from_iterator) instead. - See http://trac.sagemath.org/18411 for details. - sage: list(C) - doctest:...: UserWarning: Sage is not able to determine whether the - factors of this Cartesian product are finite. The lexicographic ordering - might not go through all elements. - [(3, 'a'), (3, 'b'), (6, 'a'), (6, 'b')] - - You might use - :class:`~sage.sets.set_from_iterator.EnumeratedSetFromIterator` for that - purpose.:: - - sage: from sage.sets.set_from_iterator import EnumeratedSetFromIterator - sage: A = EnumeratedSetFromIterator(a, (3,), category=FiniteEnumeratedSets()) - sage: B = EnumeratedSetFromIterator(b, category=FiniteEnumeratedSets()) - sage: C = cartesian_product([A, B]) - sage: C.list() - [(3, 'a'), (3, 'b'), (6, 'a'), (6, 'b')] - """ - if any(isgenerator(i) for i in iters): - raise ValueError('generators are not allowed, see the documentation '+ - '(type "CartesianProduct?") for a workaround') - - from sage.misc.superseded import deprecation - deprecation(18411, "CartesianProduct is deprecated. Use cartesian_product instead") - - from sage.combinat.misc import IterableFunctionCall - deprecate_ifc = False - iiters = [] - for a in iters: - if isinstance(a, IterableFunctionCall): - deprecate_ifc = True - iiters.append(EnumeratedSetFromIterator(a.f, a.args, a.kwargs)) - else: - iiters.append(a) - iters = tuple(iiters) - - if deprecate_ifc: - deprecation(18411, """Usage of IterableFunctionCall in CartesianProduct is deprecated. You can use EnumeratedSetFromIterator (in sage.sets.set_from_iterator) instead.""") - - from sage.categories.cartesian_product import cartesian_product - return cartesian_product(iters) - - class CartesianProduct_iters(EnumeratedSetFromIterator): r""" Cartesian product of finite sets. diff --git a/src/sage/combinat/chas/fsym.py b/src/sage/combinat/chas/fsym.py index 59f7dde8c74..36ef2fdbe35 100644 --- a/src/sage/combinat/chas/fsym.py +++ b/src/sage/combinat/chas/fsym.py @@ -339,7 +339,6 @@ def duality_pairing(self, x, y): sage: parent(z) Rational Field """ - x = self(x) y = self.dual_basis()(y) return self.base_ring().sum(coeff * y[t] for (t, coeff) in x) diff --git a/src/sage/combinat/chas/wqsym.py b/src/sage/combinat/chas/wqsym.py index 59a9dccf7e9..0f1a7af870a 100644 --- a/src/sage/combinat/chas/wqsym.py +++ b/src/sage/combinat/chas/wqsym.py @@ -34,7 +34,7 @@ # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # **************************************************************************** from sage.misc.cachefunc import cached_method @@ -246,7 +246,7 @@ def an_element(self): sage: M.an_element() M[{1}] + 2*M[{1}, {2}] """ - return self([[1]]) + 2*self([[1],[2]]) + return self([[1]]) + 2 * self([[1], [2]]) def some_elements(self): """ @@ -263,7 +263,7 @@ def some_elements(self): u = self.one() o = self([[1]]) s = self.base_ring().an_element() - return [u, o, self([[1,2]]), o + self([[1],[2]]), u + s*o] + return [u, o, self([[1, 2]]), o + self([[1], [2]]), u + s * o] class WordQuasiSymmetricFunctions(UniqueRepresentation, Parent): @@ -1184,9 +1184,6 @@ def product_on_basis(self, x, y): m = max(max(part) for part in x) # The degree of x x = [set(part) for part in x] yshift = [[val + m for val in part] for part in y] - - def union(X, Y): - return X.union(Y) return self.sum_of_monomials(ShuffleProduct(x, yshift, K)) def coproduct_on_basis(self, x): diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index fcb53e3ac3e..f4eaa4c1295 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -33,8 +33,6 @@ # **************************************************************************** from __future__ import print_function -from six.moves import range - import itertools import time from itertools import islice @@ -1665,7 +1663,7 @@ def c_matrix(self,show_warnings=True): [ 0 -1 0] sage: S = ClusterSeed(['A',4]) - sage: S.use_g_vectors(False); S.use_fpolys(False); S.use_c_vectors(False); S.use_d_vectors(False); S.track_mutations(False); + sage: S.use_g_vectors(False); S.use_fpolys(False); S.use_c_vectors(False); S.use_d_vectors(False); S.track_mutations(False); sage: S.c_matrix() Traceback (most recent call last): ... @@ -1806,7 +1804,7 @@ def _d_mutate(self, k): def coefficient(self,k): r""" - Return the *coefficient* of ``self`` at index ``k``, + Return the *coefficient* of ``self`` at index ``k``, or vertex ``k`` if ``k`` is not an index. EXAMPLES:: @@ -3216,7 +3214,7 @@ def set_cluster( self, cluster, force=False ): """ if len(cluster) < self._n + self._m: raise ValueError('The number of given cluster variables is wrong') - if self._use_fpolys: + if self._use_fpolys: if any(c not in FractionField(self._R) for c in cluster): raise ValueError('The cluster variables are not all contained in %s'%FractionField(self._R)) if not force: # if already have f_polynomials, using set_cluster might yield data inconsistent with them. @@ -3226,7 +3224,7 @@ def set_cluster( self, cluster, force=False ): for x in cluster][0:self._n] self._is_principal = None else: - print("Warning: clusters not being tracked so this command is ignored.") + print("Warning: clusters not being tracked so this command is ignored.") def reset_cluster( self ): r""" @@ -4393,7 +4391,7 @@ def LLM_gen_set(self, size_limit=-1): OUTPUT: - An array of elements in the upper cluster algebra. + An array of elements in the upper cluster algebra. EXAMPLES:: @@ -4425,7 +4423,8 @@ def _compute_compatible_vectors(self, vd): r""" Return a list of compatible vectors for each vector in the vector decomposition ``vd``. - Compatibility is defined as in [LLM]_ with respect to the matrix `B`. + Compatibility is defined as in [LLM2014]_ with respect to the + matrix `B`. INPUT: @@ -4435,7 +4434,7 @@ def _compute_compatible_vectors(self, vd): OUTPUT: - a 2-dimensional array containing all the vectors compatible with each vector in ``vd.`` + a 2-dimensional array containing all the vectors compatible with each vector in ``vd.`` .. NOTE:: @@ -4482,7 +4481,7 @@ def _compute_compatible_vectors(self, vd): E.append([i, j]) elif B[i][j] < 0: E.append([j, i]) - # Checks for edges to frozen vertices. + # Checks for edges to frozen vertices. num_frozens = num_rows - num_cols for k in range(num_frozens): for j in range(i, num_cols): @@ -4525,7 +4524,7 @@ def _compute_compatible_vectors(self, vd): def _produce_upper_cluster_algebra_element(self, vd, cList): r""" - Takes the compatible vectors and uses them to produce a Laurent polynomial in the upper cluster algebra. + Takes the compatible vectors and uses them to produce a Laurent polynomial in the upper cluster algebra. EXAMPLES:: @@ -4554,16 +4553,15 @@ def _produce_upper_cluster_algebra_element(self, vd, cList): #Computes the Laurent Polynomial for each vector in the decomposition. finalP = [] #Laurent polynomial for each vector in {0,1}^n - for i in range(len(vd)): - final = 1 + for i in range(len(vd)): numerator = 0 - if cList[i] != []: + if cList[i]: #If the vector in vd is negative then it did not contribute any compatible vectors. It will only contribute a Laurent monomial. This is the case when cList[i]=[] #Each compatible sequence gives a term in the numerator of the Laurent polynomial. - for s in cList[i]: + for s in cList[i]: term = 1 - #Calulates the monomial in the term. - for j in range(num_rows): + #Calulates the monomial in the term. + for j in range(num_rows): x = R.gen(j) expn = 0 #The exponent is determined by the vectors a,s, and the matrix B. @@ -4571,23 +4569,24 @@ def _produce_upper_cluster_algebra_element(self, vd, cList): expn += (vd[i][0][k]-s[k])*max(0, B[j][k])+s[k]*max(0, -B[j][k]) term *= x ** expn numerator += term - #Gives a numerator for the negative vector, or else the product would be zero. + #Gives a numerator for the negative vector, or else the product would be zero. else: numerator = 1 - - #Uses the vectors in vd to calculates the denominator of the Laurent. + + #Uses the vectors in vd to calculates the denominator of the Laurent. denominator = 1 for l in range(num_cols): denominator = denominator * (R.gen(l))**vd[i][0][l] - #Each copy of a vector in vd contributes a factor of the Laurent polynomial calculated from it. + #Each copy of a vector in vd contributes a factor of the Laurent polynomial calculated from it. final = (numerator/denominator)**vd[i][1] finalP.append(final) laurentP = 1 - #The UCA element for the vector a is the product of the elements produced from the vectors in its decomposition. + #The UCA element for the vector a is the product of the elements produced from the vectors in its decomposition. for p in finalP: laurentP *= p return laurentP + def _bino(n, k): """ Binomial coefficient which we define as zero for negative n. @@ -4815,7 +4814,7 @@ def _vector_decomposition(a, length): A decomposition of `a` into vectors `b_i \in \{0,1\}^n` such that `a= \sum c_i b_i` for `c_i \in \ZZ.` - Returns an array of tuples `\right[b_i,c_i\left].` + Returns an array of tuples `\right[b_i,c_i\left].` EXAMPLES:: @@ -4847,7 +4846,7 @@ def _vector_decomposition(a, length): mini = min(mini, api) diff = maxi - mini - # Creates a copy of a that will be edited when decomposing the vector. + # Creates a copy of a that will be edited when decomposing the vector. ap = copy(a_plus) if maxi == 0 == mini: ap = [] @@ -4877,13 +4876,13 @@ def _vector_decomposition(a, length): cols[i].reverse() mat = matrix(cols) - # Adds a zero to the end of every vector for each frozen vertex. + # Adds a zero to the end of every vector for each frozen vertex. froz_mat = matrix(length - mat.nrows(), mat.ncols()) mat = mat.stack(froz_mat) mat = mat.transpose() vects = mat.rows() - # Collects identical decomposition vectors and counts their multiplicities. + # Collects identical decomposition vectors and counts their multiplicities. while vects: vect = vects[0] count = vects.count(vect) @@ -4901,7 +4900,7 @@ def _power_set(n): - `n` -- an integer. - OUTPUT: + OUTPUT: A 2-dimensional array containing all elements of `\{0,1\}^n`. @@ -4980,7 +4979,7 @@ def _multi_concatenate(l1, l2): [0, 1, 2, 8], [3, 4, 5, 6], [3, 4, 5, 7], - [3, 4, 5, 8]] + [3, 4, 5, 8]] """ plist = [] for i in l1: diff --git a/src/sage/combinat/cluster_algebra_quiver/mutation_class.py b/src/sage/combinat/cluster_algebra_quiver/mutation_class.py index d19b17e0ced..0105e820940 100644 --- a/src/sage/combinat/cluster_algebra_quiver/mutation_class.py +++ b/src/sage/combinat/cluster_algebra_quiver/mutation_class.py @@ -18,7 +18,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six.moves import range import time from sage.groups.perm_gps.partn_ref.refinement_graphs import search_tree, get_orbits diff --git a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py index 7e7ad2f2fe4..693c5f3ea88 100644 --- a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py @@ -1,4 +1,6 @@ r""" +Helper functions for mutation types of quivers + This file contains helper functions for detecting the mutation type of a cluster algebra or quiver. @@ -18,10 +20,12 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six.moves import range import os +import pickle + from copy import copy + from sage.misc.all import cached_function from sage.misc.flatten import flatten from sage.graphs.all import DiGraph @@ -1267,7 +1271,6 @@ def load_data(n, user=True): ('BKO', (((1, 0), (3, -1)), ((2, 1), (1, -3)))), ('BP_', (((0, 1), (2, -2)), ((1, 2), (1, -3)), ((2, 0), (3, -1))))])] """ - from six.moves import cPickle from sage.env import DOT_SAGE, SAGE_SHARE # we check @@ -1281,7 +1284,7 @@ def load_data(n, user=True): filename = os.path.join(path, 'cluster_algebra_quiver', 'mutation_classes_%s.dig6'%n) try: with open(filename, 'rb') as fobj: - data_new = cPickle.load(fobj) + data_new = pickle.load(fobj) except Exception: # File does not exist, corrupt pickle, wrong Python version... pass diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 5c86061663d..fd2bc616018 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -37,8 +37,6 @@ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range - from sage.structure.sage_object import SageObject from copy import copy from sage.rings.all import ZZ, CC, infinity @@ -1080,7 +1078,7 @@ def free_vertices(self): ['a', 'c', 'e'] """ return self._nlist - + def frozen_vertices(self): """ Return the list of frozen vertices of ``self``. @@ -1319,7 +1317,7 @@ def mutate(self, data, inplace=True): - ``sequence`` -- a vertex of ``self``, an iterator of vertices of ``self``, a function which takes in the ClusterQuiver and returns a vertex or an iterator of vertices, - or a string of the parameter wanting to be called on ClusterQuiver that will return a vertex or + or a string of the parameter wanting to be called on ClusterQuiver that will return a vertex or an iterator of vertices. - ``inplace`` -- (default: True) if False, the result is returned, otherwise ``self`` is modified. @@ -1384,7 +1382,7 @@ def mutate(self, data, inplace=True): [ 0 0 0 1] [ 0 0 -1 0] [ 0 -1 1 0] - sage: Q2.mutate('a'); Q2.b_matrix() + sage: Q2.mutate('a'); Q2.b_matrix() [ 0 -1 0 0] [ 1 0 0 0] [ 0 0 0 1] diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py index 22f34600c86..c2fb7bd6674 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py @@ -17,7 +17,8 @@ # *************************************************************************** from __future__ import division, print_function, absolute_import -from six.moves import range +import os +import pickle from sage.structure.sage_object import SageObject from copy import copy @@ -2254,8 +2255,6 @@ def _save_data_dig6(n, types='ClassicalExceptional', verbose=False): sage: save_quiver_data(2,up_to=False, verbose=False) # indirect doctest """ - import os.path - from six.moves import cPickle data = {} possible_types = ['Classical', 'ClassicalExceptional', 'Exceptional'] if types not in possible_types: @@ -2274,7 +2273,7 @@ def _save_data_dig6(n, types='ClassicalExceptional', verbose=False): sage_makedirs(types_path) from sage.misc.temporary_file import atomic_write with atomic_write(types_file, binary=True) as f: - cPickle.dump(data, f) + pickle.dump(data, f) if verbose: keys = sorted(data.keys(),key=str) print("\nThe following types are saved to file", types_file,"and will now be used to determine quiver mutation types:") diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index dc5bd6ef7f8..2227ee6f293 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -7,7 +7,6 @@ generalized to `G \wr S_n` """ import itertools -from six.moves import range from sage.structure.element import MultiplicativeGroupElement from sage.structure.parent import Parent diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index d5532574e9f..1338661bbc8 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -147,8 +147,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import -from six.moves import range -from six import iteritems, add_metaclass from sage.interfaces.all import maxima from sage.rings.all import ZZ, QQ, Integer, infinity @@ -171,8 +169,10 @@ def bell_number(n, algorithm='flint', **options): r""" - Return the `n`-th Bell number (the number of ways to partition a set - of `n` elements into pairwise disjoint nonempty subsets). + Return the `n`-th Bell number. + + This is the number of ways to partition a set + of `n` elements into pairwise disjoint nonempty subsets. INPUT: @@ -587,8 +587,12 @@ def eulerian_number(n, k, algorithm='recursive'): sage: [eulerian_number(6,i,"formula") for i in range(6)] [1, 57, 302, 302, 57, 1] + sage: [eulerian_number(3,i) for i in range(-1, 4)] + [0, 1, 4, 1, 0] """ n = ZZ(n) + if k < 0 or k > n - 1: + return ZZ.zero() if k == 0 or k == n - 1: return ZZ.one() if algorithm == "recursive": @@ -707,6 +711,7 @@ def fibonacci(n, algorithm="pari"): else: raise ValueError("no algorithm {}".format(algorithm)) + def lucas_number1(n, P, Q): r""" Return the `n`-th Lucas number "of the first kind" (this is not @@ -773,6 +778,7 @@ def lucas_number1(n, P, Q): from sage.libs.gap.libgap import libgap return libgap.Lucas(P, Q, n)[0].sage() + def lucas_number2(n, P, Q): r""" Return the `n`-th Lucas number "of the second kind" (this is not @@ -856,9 +862,11 @@ def stirling_number1(n, k): def stirling_number2(n, k, algorithm=None): r""" Return the `n`-th Stirling number `S_2(n,k)` of the second - kind (the number of ways to partition a set of `n` elements into `k` - pairwise disjoint nonempty subsets). (The `n`-th Bell number is the - sum of the `S_2(n,k)`'s, `k=0,...,n`.) + kind. + + This is the number of ways to partition a set of `n` elements into `k` + pairwise disjoint nonempty subsets. The `n`-th Bell number is the + sum of the `S_2(n,k)`'s, `k=0,...,n`. INPUT: @@ -970,7 +978,8 @@ def stirling_number2(n, k, algorithm=None): ... ValueError: unknown algorithm: CloudReading """ - n = ZZ(n); k = ZZ(k) + n = ZZ(n) + k = ZZ(k) if algorithm is None: return _stirling_number2(n, k) elif algorithm == 'gap': @@ -1075,7 +1084,7 @@ def _repr_(self): def __eq__(self, other): """ - Test equality of self and other. + Test equality of ``self`` and ``other``. EXAMPLES:: @@ -1105,7 +1114,7 @@ def __eq__(self, other): sage: sorted(L) Traceback (most recent call last): ... - NotImplementedError: comparison not implemented for + TypeError: '<' not supported between instances of 'Bar' and 'Bar' """ if isinstance(other, CombinatorialObject): return self._list == other._list @@ -1230,7 +1239,7 @@ def __add__(self, other): def __hash__(self): """ - Computes the hash of self by computing the hash of the string + Compute the hash of ``self`` by computing the hash of the string representation of self._list. The hash is cached and stored in self._hash. @@ -1357,8 +1366,8 @@ def index(self, key): return self._list.index(key) -@add_metaclass(InheritComparisonClasscallMetaclass) -class CombinatorialElement(CombinatorialObject, Element): +class CombinatorialElement(CombinatorialObject, Element, + metaclass=InheritComparisonClasscallMetaclass): """ ``CombinatorialElement`` is both a :class:`CombinatorialObject` and an :class:`Element`. So it represents a list which is an @@ -1445,8 +1454,7 @@ def __init__(self, parent, *args, **kwds): super(CombinatorialObject, self).__init__(parent) -@add_metaclass(ClasscallMetaclass) -class CombinatorialClass(Parent): +class CombinatorialClass(Parent, metaclass=ClasscallMetaclass): """ This class is deprecated, and will disappear as soon as all derived classes in Sage's library will have been fixed. Please derive @@ -1480,7 +1488,7 @@ def __init__(self, category = None): def is_finite(self): """ - Returns whether self is finite or not. + Return whether ``self`` is finite or not. EXAMPLES:: @@ -1493,7 +1501,7 @@ def is_finite(self): def __getitem__(self, i): """ - Returns the combinatorial object of rank i. + Return the combinatorial object of rank i. EXAMPLES:: @@ -1514,7 +1522,7 @@ def __getitem__(self, i): def __str__(self): """ - Returns a string representation of self. + Return a string representation of self. EXAMPLES:: @@ -1537,7 +1545,7 @@ def _repr_(self): def __contains__(self, x): """ - Tests whether or not the combinatorial class contains the object x. + Test whether or not the combinatorial class contains the object x. This raises a NotImplementedError as a default since _all_ subclasses of CombinatorialClass should override this. @@ -1576,7 +1584,7 @@ def __eq__(self, other): def __ne__(self, other): """ - Test unequality of self and other. + Test unequality of ``self`` and ``other``. EXAMPLES:: @@ -1629,7 +1637,7 @@ def __cardinality_from_iterator(self): def __call__(self, x): """ - Returns x as an element of the combinatorial class's object class. + Return x as an element of the combinatorial class's object class. EXAMPLES:: @@ -1978,8 +1986,8 @@ def __previous_from_iterator(self, obj): def filter(self, f, name=None): """ - Returns the combinatorial subclass of f which consists of the - elements x of self such that f(x) is True. + Return the combinatorial subclass of f which consists of the + elements x of ``self`` such that f(x) is ``True``. EXAMPLES:: @@ -1992,8 +2000,8 @@ def filter(self, f, name=None): def union(self, right_cc, name=None): """ - Returns the combinatorial class representing the union of self and - right_cc. + Return the combinatorial class representing the union of ``self`` and + ``right_cc``. EXAMPLES:: @@ -2008,7 +2016,7 @@ def union(self, right_cc, name=None): def map(self, f, name=None): r""" - Returns the image `\{f(x) | x \in \text{self}\}` of this combinatorial + Return the image `\{f(x) | x \in \text{self}\}` of this combinatorial class by `f`, as a combinatorial class. `f` is supposed to be injective. @@ -2024,7 +2032,8 @@ class by `f`, as a combinatorial class. sage: [ r for r in R] [[], [2], [1], [1, 2], [2, 1], [2, 1, 2]] - If the function is not injective, then there may be repeated elements: + If the function is not injective, then there may be repeated elements:: + sage: P = Partitions(4) sage: P.list() [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]] @@ -2039,6 +2048,7 @@ class by `f`, as a combinatorial class. """ return MapCombinatorialClass(self, f, name) + class FilteredCombinatorialClass(CombinatorialClass): def __init__(self, combinatorial_class, f, name=None): """ @@ -2117,6 +2127,7 @@ def __iter__(self): if self.f(x): yield x + class UnionCombinatorialClass(CombinatorialClass): def __init__(self, left_cc, right_cc, name=None): """ @@ -2353,7 +2364,7 @@ def __repr__(self): def cardinality(self): """ - Returns the cardinality of this combinatorial class + Return the cardinality of this combinatorial class EXAMPLES:: @@ -2366,7 +2377,7 @@ def cardinality(self): def __iter__(self): """ - Returns an iterator over the elements of this combinatorial class + Return an iterator over the elements of this combinatorial class EXAMPLES:: @@ -2379,7 +2390,7 @@ def __iter__(self): def an_element(self): """ - Returns an element of this combinatorial class + Return an element of this combinatorial class EXAMPLES:: @@ -2403,7 +2414,7 @@ class InfiniteAbstractCombinatorialClass(CombinatorialClass): """ def cardinality(self): """ - Counts the elements of the combinatorial class. + Count the elements of the combinatorial class. EXAMPLES:: @@ -2415,7 +2426,7 @@ def cardinality(self): def list(self): """ - Returns an error since self is an infinite combinatorial class. + Return an error since ``self`` is an infinite combinatorial class. EXAMPLES:: @@ -2429,7 +2440,7 @@ def list(self): def __iter__(self): """ - Returns an iterator for the infinite combinatorial class self if + Return an iterator for the infinite combinatorial class ``self`` if possible or raise a NotImplementedError. EXAMPLES:: @@ -2844,7 +2855,7 @@ def bell_polynomial(n, k): for p in Partitions(n, length=k): factorial_product = 1 power_factorial_product = 1 - for part, count in iteritems(p.to_exp_dict()): + for part, count in p.to_exp_dict().items(): factorial_product *= factorial(count) power_factorial_product *= factorial(part)**count coefficient = factorial(n) // (factorial_product * power_factorial_product) diff --git a/src/sage/combinat/combinat_cython.pyx b/src/sage/combinat/combinat_cython.pyx index 664d64b9929..494e516ca27 100644 --- a/src/sage/combinat/combinat_cython.pyx +++ b/src/sage/combinat/combinat_cython.pyx @@ -7,6 +7,7 @@ Currently implemented: - iterators for set partitions - iterator for Lyndon words - iterator for perfect matchings +- conjugate of partitions AUTHORS: @@ -758,3 +759,26 @@ def set_partition_composition(tuple sp1, tuple sp2): return (tuple(diagram), num_loops) + +def conjugate(p): + """ + Return the conjugate partition associated to the partition ``p`` + as a list. + + EXAMPLES:: + + sage: from sage.combinat.combinat_cython import conjugate + sage: conjugate([2,2]) + [2, 2] + sage: conjugate([6,3,1]) + [3, 2, 2, 1, 1, 1] + """ + cdef Py_ssize_t j, l + cdef list conj + l = len(p) + if l == 0: + return [] + conj = [l] * p[-1] + for j in range(l - 1, 0, -1): + conj.extend([j] * (p[j - 1] - p[j])) + return conj diff --git a/src/sage/combinat/combination.py b/src/sage/combinat/combination.py index 132866ea55c..3722147f14a 100644 --- a/src/sage/combinat/combination.py +++ b/src/sage/combinat/combination.py @@ -7,6 +7,8 @@ - Vincent Delecroix (2011): cleaning, bug corrections, doctests +- Antoine Genitrini (2020) : new implementation of the lexicographic unranking of combinations + """ #***************************************************************************** # Copyright (C) 2007 Mike Hansen , @@ -23,7 +25,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import -from six.moves import range from sage.interfaces.all import gap from sage.rings.all import ZZ, Integer @@ -533,32 +534,14 @@ def rank(comb, n, check=True): return binomial(n,k)-t-1 -def _comb_largest(a,b,x): - r""" - Returns the largest `w < a` such that `binomial(w,b) <= x`. - - EXAMPLES:: - - sage: from sage.combinat.combination import _comb_largest - sage: _comb_largest(6,3,10) - 5 - sage: _comb_largest(6,3,5) - 4 - """ - w = a - 1 - - while binomial(w,b) > x: - w -= 1 - - return w def from_rank(r, n, k): r""" - Returns the combination of rank ``r`` in the subsets of + Return the combination of rank ``r`` in the subsets of ``range(n)`` of size ``k`` when listed in lexicographic order. - The algorithm used is based on combinadics and James McCaffrey's - MSDN article. See: :wikipedia:`Combinadic` + The algorithm used is based on factoradics and presented in [DGH2020]. + It is there compared to the other from the literature. EXAMPLES:: @@ -579,27 +562,94 @@ def from_rank(r, n, k): (1, 2) sage: combination.from_rank(0,3,3) (0, 1, 2) + + TESTS:: + + sage: from sage.combinat.combination import from_rank + sage: def _comb_largest(a,b,x): + ....: w = a - 1 + ....: while binomial(w,b) > x: + ....: w -= 1 + ....: return w + sage: def from_rank_comb_largest(r, n, k): + ....: a = n + ....: b = k + ....: x = binomial(n, k) - 1 - r # x is the 'dual' of m + ....: comb = [None] * k + ....: for i in range(k): + ....: comb[i] = _comb_largest(a, b, x) + ....: x = x - binomial(comb[i], b) + ....: a = comb[i] + ....: b = b - 1 + ....: for i in range(k): + ....: comb[i] = (n - 1) - comb[i] + ....: return tuple(comb) + sage: all(from_rank(r, n, k) == from_rank_comb_largest(r, n, k) + ....: for n in range(10) for k in range(n+1) for r in range(binomial(n,k))) + True """ if k < 0: raise ValueError("k must be > 0") if k > n: raise ValueError("k must be <= n") - - a = n - b = k - x = binomial(n, k) - 1 - r # x is the 'dual' of m - comb = [None] * k - - for i in range(k): - comb[i] = _comb_largest(a, b, x) - x = x - binomial(comb[i], b) - a = comb[i] - b = b - 1 - - for i in range(k): - comb[i] = (n - 1) - comb[i] - - return tuple(comb) + if n == 0 or k == 0: + return () + if n < 0: + raise ValueError("n must be >= 0") + B = binomial(n, k) + if r < 0 or r >= B: + raise ValueError("r must satisfy 0 <= r < binomial(n, k)") + if k == 1: + return (r,) + + n0 = n + D = [0] * k + inverse = False + if k < n0 / 2: + inverse = True + k = n - k + r = B - 1 - r + + B = (B * k) // n0 + m = 0 + i = 0 + j = 0 + m2 = 0 + d = 0 + while d < k - 1: + if B > r: + if i < k - 2: + if n0 - 1 - m == 0: + B = 1 + else: + B = (B * (k-1-i)) // (n0 - 1 - m) + d += 1 + if inverse: + for e in range(m2, m+i): + D[j] = e + j += 1 + m2 = m + i + 1 + else: + D[i] = m + i + i += 1 + n0 -= 1 + else: + r -= B + if n0 - 1 - m == 0: + B = 1 + else: + B = (B * (n0-m-k+i)) // (n0 - 1 - m) + m += 1 + if inverse: + for e in range(m2, n0+r+i-B): + D[j] = e + j += 1 + for e in range(n0+r+i+1-B, n): + D[j] = e + j += 1 + else: + D[k-1] = n0 + r + k - 1 - B + return tuple(D) ########################################################## # Deprecations @@ -624,3 +674,4 @@ def __setstate__(self, state): from sage.misc.persist import register_unpickle_override register_unpickle_override("sage.combinat.choose_nk", "ChooseNK", ChooseNK) + diff --git a/src/sage/combinat/combinatorial_algebra.py b/src/sage/combinat/combinatorial_algebra.py index 948cfe9d907..c9e4d0675d1 100644 --- a/src/sage/combinat/combinatorial_algebra.py +++ b/src/sage/combinat/combinatorial_algebra.py @@ -60,13 +60,11 @@ #***************************************************************************** from sage.combinat.free_module import CombinatorialFreeModule -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.misc.cachefunc import cached_method from sage.categories.all import AlgebrasWithBasis from sage.structure.element import Element -import six - # TODO: migrate this completely to the combinatorial free module + categories framework @@ -299,8 +297,8 @@ def product(self, left, right): #Do the case where the user specifies how to multiply basis elements if hasattr(self, '_multiply_basis'): - 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(): res = self._multiply_basis(left_m, right_m) coeffprod = left_c * right_c #Handle the case where the user returns a dictionary @@ -313,7 +311,7 @@ def product(self, left, right): else: z_elt[res] = z_elt.get(res, ABRzero) + coeffprod continue - for m, c in six.iteritems(res): + for m, c in res.items(): z_elt[m] = z_elt.get(m, ABRzero) + coeffprod * c #We assume that the user handles the multiplication correctly on @@ -332,7 +330,7 @@ def product(self, left, right): BR = self.base_ring() zero = BR(0) del_list = [] - for m, c in six.iteritems(z_elt): + for m, c in z_elt.items(): if c == zero: del_list.append(m) for m in del_list: diff --git a/src/sage/combinat/combinatorial_map.py b/src/sage/combinat/combinatorial_map.py index 21f2904d30d..945ec225d4e 100644 --- a/src/sage/combinat/combinatorial_map.py +++ b/src/sage/combinat/combinatorial_map.py @@ -191,7 +191,6 @@ def combinatorial_map_wrapper(f=None, order=None, name=None): sage: def major_index(p): ....: return p.major_index() - ....: sage: major_index sage: combinatorial_map(major_index) @@ -219,7 +218,7 @@ class CombinatorialMap(object): """ def __init__(self, f, order=None, name=None): """ - Constructor for combinatorial maps + Constructor for combinatorial maps. EXAMPLES:: @@ -227,7 +226,6 @@ def __init__(self, f, order=None, name=None): sage: def f(x): ....: "doc of f" ....: return x - ....: sage: x = combinatorial_map(f); x Combinatorial map: f sage: x.__doc__ diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index 33bee8a475c..dea047aa63d 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -40,7 +40,6 @@ from sage.categories.cartesian_product import cartesian_product from .integer_lists import IntegerListsLex -from six.moves import builtins from sage.rings.integer import Integer from sage.combinat.combinatorial_map import combinatorial_map @@ -1668,7 +1667,7 @@ def __contains__(self, x): """ if isinstance(x, Composition): return True - elif isinstance(x, builtins.list): + elif isinstance(x, list): for i in x: if (not isinstance(i, (int, Integer))) and i not in ZZ: return False diff --git a/src/sage/combinat/composition_signed.py b/src/sage/combinat/composition_signed.py index a1c85faec26..4d4fbc201f6 100644 --- a/src/sage/combinat/composition_signed.py +++ b/src/sage/combinat/composition_signed.py @@ -16,7 +16,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import -from six.moves import builtins import itertools @@ -91,7 +90,7 @@ def __contains__(self, x): sage: [-2, 1, -3] in SignedCompositions(6) True """ - if isinstance(x, builtins.list): + if isinstance(x, list): for z in x: if (not isinstance(z, (int, Integer))) and z not in ZZ: return False diff --git a/src/sage/combinat/composition_tableau.py b/src/sage/combinat/composition_tableau.py index f283845a02d..7c563bfffd1 100644 --- a/src/sage/combinat/composition_tableau.py +++ b/src/sage/combinat/composition_tableau.py @@ -5,8 +5,6 @@ - Chris Berg, Jeff Ferreira (2012-9): Initial version """ -from six.moves import range -from six import add_metaclass from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.sets.non_negative_integers import NonNegativeIntegers @@ -23,8 +21,7 @@ import copy -@add_metaclass(ClasscallMetaclass) -class CompositionTableau(CombinatorialElement): +class CompositionTableau(CombinatorialElement, metaclass=ClasscallMetaclass): r""" A composition tableau. diff --git a/src/sage/combinat/constellation.py b/src/sage/combinat/constellation.py index 5a67d7ebe80..50c40540dc6 100644 --- a/src/sage/combinat/constellation.py +++ b/src/sage/combinat/constellation.py @@ -49,9 +49,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from six.moves import range -from six import integer_types - from sage.structure.element import parent from sage.structure.parent import Parent from sage.structure.element import Element @@ -1509,7 +1506,7 @@ def perms_sym_init(g, sym=None): if sym is None: domain = set().union(*[perm_sym_domain(gg) for gg in g]) - if all(isinstance(s, (Integer,) + integer_types) and s > 0 + if all(isinstance(s, (int, Integer)) and s > 0 for s in domain): domain = max(domain) else: diff --git a/src/sage/combinat/core.py b/src/sage/combinat/core.py index a4e8370451a..3e699ee4295 100644 --- a/src/sage/combinat/core.py +++ b/src/sage/combinat/core.py @@ -32,7 +32,6 @@ from sage.combinat.partition import Partitions, Partition from sage.combinat.combinat import CombinatorialElement from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from sage.functions.other import floor from sage.combinat.combinatorial_map import combinatorial_map @@ -53,7 +52,7 @@ class Core(CombinatorialElement): @staticmethod def __classcall_private__(cls, part, k): r""" - Implements the shortcut ``Core(part, k)`` to ``Cores(k,l)(part)`` + Implement the shortcut ``Core(part, k)`` to ``Cores(k,l)(part)`` where `l` is the length of the core. TESTS:: @@ -74,7 +73,7 @@ def __classcall_private__(cls, part, k): return part part = Partition(part) if not part.is_core(k): - raise ValueError("%s is not a %s-core"%(part, k)) + raise ValueError("%s is not a %s-core" % (part, k)) l = sum(part.k_boundary(k).row_lengths()) return Cores(k, l)(part) @@ -100,11 +99,13 @@ def __init__(self, parent, core): k = parent.k part = Partition(core) if not part.is_core(k): - raise ValueError("%s is not a %s-core"%(part, k)) + raise ValueError("%s is not a %s-core" % (part, k)) CombinatorialElement.__init__(self, parent, core) def __eq__(self, other): """ + Test for equality. + EXAMPLES:: sage: c = Core([4,2,1,1],5) @@ -118,14 +119,33 @@ def __eq__(self, other): False """ if isinstance(other, Core): - return self._list == other._list and self.parent().k == other.parent().k - else: - return False + return (self._list == other._list and + self.parent().k == other.parent().k) + return False + + def __ne__(self, other): + """ + Test for un-equality. + + EXAMPLES:: + + sage: c = Core([4,2,1,1],5) + sage: d = Core([4,2,1,1],5) + sage: e = Core([4,2,1,1],6) + sage: c != [4,2,1,1] + True + sage: c != d + False + sage: c != e + True + """ + return not (self == other) def __hash__(self): """ - Computes the hash of ``self`` by computing the hash of the + Compute the hash of ``self`` by computing the hash of the underlying list and of the additional parameter. + The hash is cached and stored in ``self._hash``. EXAMPLES:: @@ -170,7 +190,7 @@ def _latex_(self): def k(self): r""" - Returns `k` of the `k`-core ``self``. + Return `k` of the `k`-core ``self``. EXAMPLES:: @@ -183,7 +203,7 @@ def k(self): @combinatorial_map(name="to partition") def to_partition(self): r""" - Turns the core ``self`` into the partition identical to ``self``. + Turn the core ``self`` into the partition identical to ``self``. EXAMPLES:: @@ -198,7 +218,7 @@ def to_bounded_partition(self): r""" Bijection between `k`-cores and `(k-1)`-bounded partitions. - Maps the `k`-core ``self`` to the corresponding `(k-1)`-bounded partition. + This maps the `k`-core ``self`` to the corresponding `(k-1)`-bounded partition. This bijection is achieved by deleting all cells in ``self`` of hook length greater than `k`. @@ -213,7 +233,7 @@ def to_bounded_partition(self): def size(self): r""" - Returns the size of ``self`` as a partition. + Return the size of ``self`` as a partition. EXAMPLES:: @@ -226,7 +246,7 @@ def size(self): def length(self): r""" - Returns the length of ``self``. + Return the length of ``self``. The length of a `k`-core is the size of the corresponding `(k-1)`-bounded partition which agrees with the length of the corresponding Grassmannian element, @@ -270,11 +290,12 @@ def to_grassmannian(self): [0 1 0] [0 0 1] """ - return self.to_bounded_partition().from_kbounded_to_grassmannian(self.k()-1) + bp = self.to_bounded_partition() + return bp.from_kbounded_to_grassmannian(self.k() - 1) def affine_symmetric_group_simple_action(self, i): r""" - Returns the action of the simple transposition `s_i` of the affine symmetric group on ``self``. + Return the action of the simple transposition `s_i` of the affine symmetric group on ``self``. This gives the action of the affine symmetric group of type `A_k^{(1)}` on the `k`-core ``self``. If ``self`` has outside (resp. inside) corners of content `i` modulo `k`, then @@ -304,11 +325,13 @@ def affine_symmetric_group_simple_action(self, i): """ mu = self.to_partition() corners = mu.outside_corners() - corners = [ p for p in corners if mu.content(p[0],p[1])%self.k()==i ] - if corners == []: + corners = [p for p in corners + if mu.content(p[0], p[1]) % self.k() == i] + if not corners: corners = mu.corners() - corners = [ p for p in corners if mu.content(p[0],p[1])%self.k()==i ] - if corners == []: + corners = [p for p in corners + if mu.content(p[0], p[1]) % self.k() == i] + if not corners: return self for p in corners: mu = mu.remove_cell(p[0]) @@ -317,7 +340,7 @@ def affine_symmetric_group_simple_action(self, i): mu = mu.add_cell(p[0]) return Core(mu, self.k()) - def affine_symmetric_group_action(self, w, transposition = False): + def affine_symmetric_group_action(self, w, transposition=False): r""" Return the (left) action of the affine symmetric group on ``self``. @@ -384,13 +407,15 @@ def _transposition_to_reduced_word(self, t): [1, 2, 0, 1, 2, 0, 2, 1, 0, 2, 1] """ k = self.k() - if (t[0]-t[1])%k == 0: + if (t[0] - t[1]) % k == 0: raise ValueError("t_0 and t_1 cannot be equal mod k") if t[0] > t[1]: - return self._transposition_to_reduced_word([t[1],t[0]]) + return self._transposition_to_reduced_word([t[1], t[0]]) else: - return [i%k for i in range(t[0],t[1]-floor((t[1]-t[0])/k))] + [(t[1]-floor((t[1]-t[0])/k)-2-i)%(k) for i in - range(t[1]-floor((t[1]-t[0])/k)-t[0]-1)] + resu = [i % k for i in range(t[0], t[1] - (t[1] - t[0]) // k)] + resu += [(t[1] - (t[1] - t[0]) // k - 2 - i) % k + for i in range(t[1] - (t[1] - t[0]) // k - t[0] - 1)] + return resu def weak_le(self, other): r""" @@ -402,7 +427,7 @@ def weak_le(self, other): OUTPUT: a boolean - Returns whether ``self`` <= ``other`` in weak order. + This returns whether ``self`` <= ``other`` in weak order. EXAMPLES:: @@ -434,7 +459,7 @@ def weak_le(self, other): def weak_covers(self): r""" - Returns a list of all elements that cover ``self`` in weak order. + Return a list of all elements that cover ``self`` in weak order. EXAMPLES:: @@ -448,8 +473,8 @@ def weak_covers(self): """ w = self.to_grassmannian() S = w.upper_covers(side='left') - S = [x for x in S if x.is_affine_grassmannian()] - return [ x.affine_grassmannian_to_core() for x in set(S) ] + S = (x for x in S if x.is_affine_grassmannian()) + return [x.affine_grassmannian_to_core() for x in set(S)] def strong_le(self, other): r""" @@ -461,7 +486,7 @@ def strong_le(self, other): OUTPUT: a boolean - Returns whether ``self`` <= ``other`` in Bruhat (or strong) order. + This returns whether ``self`` <= ``other`` in Bruhat (or strong) order. EXAMPLES:: @@ -479,7 +504,7 @@ def strong_le(self, other): ValueError: The two cores do not have the same k """ if type(self) is type(other): - if self.k()!=other.k(): + if self.k() != other.k(): raise ValueError("The two cores do not have the same k") else: other = Core(other, self.k()) @@ -495,8 +520,8 @@ def contains(self, other): OUTPUT: a boolean - Returns ``True`` if the Ferrers diagram of ``self`` contains the - Ferrers diagram of other. + This returns ``True`` if the Ferrers diagram of ``self`` contains the + Ferrers diagram of ``other``. EXAMPLES:: @@ -513,7 +538,7 @@ def contains(self, other): def strong_covers(self): r""" - Returns a list of all elements that cover ``self`` in strong order. + Return a list of all elements that cover ``self`` in strong order. EXAMPLES:: @@ -524,12 +549,12 @@ def strong_covers(self): sage: c.strong_covers() [[5, 3, 1], [4, 2, 1, 1]] """ - S = Cores(self.k(), length=self.length()+1) - return [ ga for ga in S if ga.contains(self) ] + S = Cores(self.k(), length=self.length() + 1) + return [ga for ga in S if ga.contains(self)] def strong_down_list(self): r""" - Returns a list of all elements that are covered by ``self`` in strong order. + Return a list of all elements that are covered by ``self`` in strong order. EXAMPLES:: @@ -540,11 +565,13 @@ def strong_down_list(self): sage: c.strong_down_list() [[4, 2], [3, 1, 1]] """ - if self==[]: + if not self: return [] - return [ga for ga in Cores(self.k(), length=self.length()-1) if self.contains(ga)] + return [ga for ga in Cores(self.k(), length=self.length() - 1) + if self.contains(ga)] + -def Cores(k, length = None, **kwargs): +def Cores(k, length=None, **kwargs): r""" A `k`-core is a partition from which no rim hook of size `k` can be removed. Alternatively, a `k`-core is an integer partition such that the Ferrers @@ -586,12 +613,13 @@ def Cores(k, length = None, **kwargs): [4, 1, 1] """ if length is None and 'size' in kwargs: - return Cores_size(k,kwargs['size']) + return Cores_size(k, kwargs['size']) elif length is not None: - return Cores_length(k,length) + return Cores_length(k, length) else: raise ValueError("You need to either specify the length or size of the cores considered!") + class Cores_length(UniqueRepresentation, Parent): r""" The class of `k`-cores of length `n`. @@ -607,7 +635,7 @@ def __init__(self, k, n): """ self.k = k self.n = n - Parent.__init__(self, category = FiniteEnumeratedSets()) + Parent.__init__(self, category=FiniteEnumeratedSets()) def _repr_(self): """ @@ -616,11 +644,11 @@ def _repr_(self): sage: repr(Cores(4, 3)) #indirect doctest '4-Cores of length 3' """ - return "%s-Cores of length %s"%(self.k,self.n) + return "%s-Cores of length %s" % (self.k, self.n) def list(self): r""" - Returns the list of all `k`-cores of length `n`. + Return the list of all `k`-cores of length `n`. EXAMPLES:: @@ -628,7 +656,8 @@ def list(self): sage: C.list() [[4, 2], [3, 1, 1], [2, 2, 1, 1]] """ - return [la.to_core(self.k-1) for la in Partitions(self.n, max_part=self.k-1)] + return [la.to_core(self.k - 1) + for la in Partitions(self.n, max_part=self.k - 1)] def from_partition(self, part): r""" @@ -671,7 +700,7 @@ def __init__(self, k, n): """ self.k = k self.n = n - Parent.__init__(self, category = FiniteEnumeratedSets()) + Parent.__init__(self, category=FiniteEnumeratedSets()) def _repr_(self): """ @@ -680,11 +709,11 @@ def _repr_(self): sage: repr(Cores(4, size = 3)) #indirect doctest '4-Cores of size 3' """ - return "%s-Cores of size %s"%(self.k,self.n) + return "%s-Cores of size %s" % (self.k, self.n) def list(self): r""" - Returns the list of all `k`-cores of size `n`. + Return the list of all `k`-cores of size `n`. EXAMPLES:: @@ -692,11 +721,12 @@ def list(self): sage: C.list() [[3, 1], [2, 1, 1]] """ - return [ Core(x, self.k) for x in Partitions(self.n) if x.is_core(self.k) ] + return [Core(x, self.k) for x in Partitions(self.n) + if x.is_core(self.k)] def from_partition(self, part): r""" - Converts the partition ``part`` into a core (as the identity map). + Convert the partition ``part`` into a core (as the identity map). This is the inverse method to :meth:`to_partition`. diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index 77b4a6f7113..f112e45491f 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -512,7 +512,7 @@ def is_admissible(self): sage: C = crystals.AlcovePaths(['A',2],[1,1]); C Highest weight crystal of alcove paths of type ['A', 2] and weight Lambda[1] + Lambda[2] - sage: roots = sorted(list(C._R._root_lattice.positive_roots())); roots + sage: roots = sorted(C._R._root_lattice.positive_roots()); roots [alpha[1], alpha[1] + alpha[2], alpha[2]] sage: r1 = C._R(roots[0],0); r1 (alpha[1], 0) diff --git a/src/sage/combinat/crystals/fast_crystals.py b/src/sage/combinat/crystals/fast_crystals.py index e98989df6ba..2f964bb60de 100644 --- a/src/sage/combinat/crystals/fast_crystals.py +++ b/src/sage/combinat/crystals/fast_crystals.py @@ -1,7 +1,7 @@ r""" Fast Rank Two Crystals """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Anne Schilling # Nicolas Thiery # Ben Brubaker @@ -17,8 +17,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#**************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent @@ -91,7 +91,7 @@ class FastCrystal(UniqueRepresentation, Parent): @staticmethod def __classcall__(cls, cartan_type, shape, format = "string"): """ - Normalizes the input arguments to ensure unique representation + Normalize the input arguments to ensure unique representation EXAMPLES:: @@ -245,12 +245,13 @@ def __call__(self, value): sage: C(x) is x True """ - if parent(value) is self: return value + if parent(value) is self: + return value return self.element_class(self, value, self.format) def list(self): """ - Returns a list of the elements of self. + Return a list of the elements of self. EXAMPLES:: @@ -269,7 +270,7 @@ def list(self): def digraph(self): """ - Returns the digraph associated to self. + Return the digraph associated to self. EXAMPLES:: @@ -281,7 +282,7 @@ def digraph(self): def cmp_elements(self, x,y): r""" - Returns True if and only if there is a path from x to y in the + Return True if and only if there is a path from x to y in the crystal graph. Because the crystal graph is classical, it is a directed acyclic @@ -326,7 +327,7 @@ def __init__(self, parent, value, format): def weight(self): """ - Returns the weight of self. + Return the weight of self. EXAMPLES:: @@ -411,7 +412,7 @@ def _richcmp_(self, other, op): def e(self, i): """ - Returns the action of `e_i` on self. + Return the action of `e_i` on self. EXAMPLES:: @@ -430,7 +431,7 @@ def e(self, i): def f(self, i): """ - Returns the action of `f_i` on self. + Return the action of `f_i` on self. EXAMPLES:: @@ -446,6 +447,3 @@ def f(self, i): else: r = self.parent()._rootoperators[self.value][3] return self.parent()(r) if r is not None else None - - -#FastCrystal.Element = FastCrystalElement diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py index afcc1fc669b..28b76b6f9c2 100644 --- a/src/sage/combinat/crystals/kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py @@ -21,7 +21,6 @@ # library is heavily inspired from MuPAD-Combinat. # *************************************************************************** from __future__ import division, print_function -from six.moves import range from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 3e74b89dbd2..73c810bd594 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -99,8 +99,6 @@ from sage.rings.integer_ring import ZZ from sage.matrix.matrix_space import MatrixSpace -import six - class NakajimaMonomial(Element): r""" @@ -177,7 +175,7 @@ def _repr_Y(self): if not self._Y: return "1" - L = sorted(six.iteritems(self._Y), key=lambda x: (x[0][0], x[0][1])) + L = sorted(self._Y.items(), key=lambda x: (x[0][0], x[0][1])) exp = lambda e: "^{}".format(e) if e != 1 else "" return ' '.join("Y({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) for mon in L) @@ -201,7 +199,7 @@ def _repr_A(self): if not Y and not self._A: return "1" - L = sorted(six.iteritems(Y), key=lambda x: (x[0][0], x[0][1])) + L = sorted(Y.items(), key=lambda x: (x[0][0], x[0][1])) exp = lambda e: "^{}".format(e) if e != 1 else "" ret = ' '.join("Y({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) for mon in L) @@ -209,7 +207,7 @@ def _repr_A(self): return ret if Y: ret += ' ' - L = sorted(six.iteritems(self._A), key=lambda x: (x[0][0], x[0][1])) + L = sorted(self._A.items(), key=lambda x: (x[0][0], x[0][1])) return ret + ' '.join("A({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) for mon in L) @@ -223,7 +221,7 @@ def __hash__(self): sage: hash(m1) != hash(m2) True """ - return hash(frozenset(tuple(six.iteritems(self._Y)))) + return hash(frozenset(tuple(self._Y.items()))) def __eq__(self, other): r""" @@ -300,7 +298,7 @@ def _latex_Y(self): if not self._Y: return "\\boldsymbol{1}" - L = sorted(six.iteritems(self._Y), key=lambda x:(x[0][0],x[0][1])) + L = sorted(self._Y.items(), key=lambda x:(x[0][0],x[0][1])) return_str = '' for x in L: if x[1] != 1: @@ -328,14 +326,14 @@ def _latex_A(self): if not Y and not self._A: return "\\boldsymbol{1}" - L = sorted(six.iteritems(Y), key=lambda x:(x[0][0],x[0][1])) + L = sorted(Y.items(), key=lambda x:(x[0][0],x[0][1])) return_str = '' for x in L: if x[1] != 1: return_str += "Y_{%s,%s}"%(x[0][0],x[0][1]) + "^{%s} "%x[1] else: return_str += "Y_{%s,%s} "%(x[0][0],x[0][1]) - L = sorted(six.iteritems(self._A), key=lambda x:(x[0][0],x[0][1])) + L = sorted(self._A.items(), key=lambda x:(x[0][0],x[0][1])) for x in L: if x[1] != 1: return_str += "A_{%s,%s}"%(x[0][0],x[0][1]) + "^{%s} "%x[1] @@ -362,7 +360,7 @@ def _classical_weight(self): """ P = self.parent().weight_lattice_realization() La = P.fundamental_weights() - return P(sum(v*La[k[0]] for k,v in six.iteritems(self._Y))) + return P(sum(v*La[k[0]] for k,v in self._Y.items())) def weight_in_root_lattice(self): r""" @@ -388,7 +386,7 @@ def weight_in_root_lattice(self): """ Q = RootSystem(self.parent().cartan_type()).root_lattice() al = Q.simple_roots() - return Q.sum(e*al[k[0]] for k,e in six.iteritems(self._A)) + return Q.sum(e*al[k[0]] for k,e in self._A.items()) def weight(self): r""" @@ -466,7 +464,7 @@ def phi(self, i): continue else: d[(i,a)] = 0 - S = sorted((x for x in six.iteritems(d) if x[0][0] == i), key=lambda x: x[0][1]) + S = sorted((x for x in d.items() if x[0][0] == i), key=lambda x: x[0][1]) return max(sum(S[k][1] for k in range(s)) for s in range(1,len(S)+1)) def _ke(self, i): @@ -498,7 +496,7 @@ def _ke(self, i): d[(i,a)] = 0 total = ZZ.zero() L = [] - S = sorted((x for x in six.iteritems(d) if x[0][0] == i), key=lambda x: x[0][1]) + S = sorted((x for x in d.items() if x[0][0] == i), key=lambda x: x[0][1]) for var,exp in S: total += exp if total == phi: @@ -531,7 +529,7 @@ def _kf(self, i): continue else: d[(i,a)] = 0 - S = sorted((x for x in six.iteritems(d) if x[0][0] == i), key=lambda x: x[0][1]) + S = sorted((x for x in d.items() if x[0][0] == i), key=lambda x: x[0][1]) sum = 0 phi = self.phi(i) for var,exp in S: @@ -601,7 +599,7 @@ def e(self, i): if cm[j_index,i-shift] != 0: Aik[(j, ke+c)] = cm[j_index,i-shift] # Multiply by Aik - for key,value in six.iteritems(Aik): + for key,value in Aik.items(): if key in newdict: if newdict[key] == -value: # The result would be a 0 exponent del newdict[key] @@ -650,7 +648,7 @@ def f(self, i): if cm[j_index,i-shift] != 0: Aik[(j, kf+c)] = -cm[j_index,i-shift] # Multiply by Aik - for key,value in six.iteritems(Aik): + for key,value in Aik.items(): if key in newdict: if newdict[key] == -value: # The result would be a 0 exponent del newdict[key] @@ -902,7 +900,7 @@ def _element_constructor_(self, Y=None, A=None): if ct.is_finite(): shift = 1 Y = {} - for k,v in six.iteritems(A): + for k,v in A.items(): Y[k] = Y.get(k, 0) + v Y[(k[0],k[1]+1)] = Y.get((k[0],k[1]+1), 0) + v for j_index,j in enumerate(I): diff --git a/src/sage/combinat/crystals/pbw_crystal.py b/src/sage/combinat/crystals/pbw_crystal.py index a1ac25af0f7..531ebe1a9c3 100644 --- a/src/sage/combinat/crystals/pbw_crystal.py +++ b/src/sage/combinat/crystals/pbw_crystal.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -`\mathcal{B}(\infty)` Crystal Of PBW Monomials. +`\mathcal{B}(\infty)` Crystal Of PBW Monomials AUTHORS: @@ -12,15 +12,15 @@ :ref:`sage.combinat.crystals.pbw_datum`. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2015 Dinakar Muthiah # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.structure.element import Element diff --git a/src/sage/combinat/cyclic_sieving_phenomenon.py b/src/sage/combinat/cyclic_sieving_phenomenon.py index 5a826929614..e4e64868730 100644 --- a/src/sage/combinat/cyclic_sieving_phenomenon.py +++ b/src/sage/combinat/cyclic_sieving_phenomenon.py @@ -24,7 +24,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # **************************************************************************** -from six.moves import range from sage.rings.all import ZZ from sage.arith.all import lcm from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing diff --git a/src/sage/combinat/derangements.py b/src/sage/combinat/derangements.py index 42fe5133611..cf792e091a2 100644 --- a/src/sage/combinat/derangements.py +++ b/src/sage/combinat/derangements.py @@ -22,7 +22,6 @@ # # http://www.gnu.org/licenses/ # **************************************************************************** -from six.moves import range from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation diff --git a/src/sage/combinat/designs/all.py b/src/sage/combinat/designs/all.py index 7fb0763a1af..7ec907e7100 100644 --- a/src/sage/combinat/designs/all.py +++ b/src/sage/combinat/designs/all.py @@ -1,30 +1,8 @@ """ Combinatorial design features that are imported by default in the interpreter namespace - -Test for deprecations of imports into global namespace:: - - sage: designs_from_XML - doctest:warning...: - DeprecationWarning: - Importing designs_from_XML from here is deprecated. If you need to use it, please import it directly from sage.combinat.designs.ext_rep - See https://trac.sagemath.org/27066 for details. - ... - - sage: designs_from_XML_url - doctest:warning...: - DeprecationWarning: - Importing designs_from_XML_url from here is deprecated. If you need to use it, please import it directly from sage.combinat.designs.ext_rep - See https://trac.sagemath.org/27066 for details. - ... """ -from __future__ import absolute_import - from sage.misc.lazy_import import lazy_import -lazy_import("sage.combinat.designs.ext_rep", ['designs_from_XML', - 'designs_from_XML_url'], - deprecation=27066) - lazy_import('sage.combinat.designs.block_design', 'BlockDesign') lazy_import('sage.combinat.designs.incidence_structures', 'IncidenceStructure') @@ -36,5 +14,3 @@ ['CoveringDesign', 'schonheim', 'trivial_covering_design']) from . import design_catalog as designs - -del absolute_import diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 00ae9149676..64b76a6df80 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -4,7 +4,7 @@ This module gathers everything related to Balanced Incomplete Block Designs. One can build a BIBD (or check that it can be built) with :func:`balanced_incomplete_block_design`:: - sage: BIBD = designs.balanced_incomplete_block_design(7,3) + sage: BIBD = designs.balanced_incomplete_block_design(7,3,1) In particular, Sage can build a `(v,k,1)`-BIBD when one exists for all `k\leq 5`. The following functions are available: @@ -15,7 +15,7 @@ :widths: 30, 70 :delim: | - :func:`balanced_incomplete_block_design` | Return a BIBD of parameters `v,k`. + :func:`balanced_incomplete_block_design` | Return a BIBD of parameters `v,k,\lambda`. :func:`BIBD_from_TD` | Return a BIBD through TD-based constructions. :func:`BIBD_from_difference_family` | Return the BIBD associated to the difference family ``D`` on the group ``G``. :func:`BIBD_from_PBD` | Return a `(v,k,1)`-BIBD from a `(r,K)`-PBD where `r=(v-1)/(k-1)`. @@ -53,34 +53,76 @@ from __future__ import division, print_function from __future__ import absolute_import -from six.moves import range - from sage.categories.sets_cat import EmptySetError from sage.misc.unknown import Unknown from .design_catalog import transversal_design -from .block_design import BlockDesign from sage.arith.all import binomial, is_prime_power from .group_divisible_designs import GroupDivisibleDesign from .designs_pyx import is_pairwise_balanced_design -def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False): +def biplane(n, existence=False): r""" - Return a BIBD of parameters `v,k`. + Return a biplane of order `n`. + + A biplane of order `n` is a symmetric `(1+\frac {(n+1)(n+2)} {2}, n+2, 2)`-BIBD. + A symmetric (or square) `(v,k,\lambda)`-BIBD is a `(v,k,\lambda)`-BIBD with `v` blocks. + + INPUT: + + - ``n`` -- (integer) order of the biplane + + - ``existence`` (boolean) -- instead of building the design, return: + + - ``True`` -- meaning that Sage knows how to build the design + + - ``Unknown`` -- meaning that Sage does not know how to build the + design, but that the design may exist (see :mod:`sage.misc.unknown`). + + - ``False`` -- meaning that the design does not exist. + + .. SEEALSO:: + + * :func:`balanced_incomplete_block_design` + + EXAMPLES:: + + sage: designs.biplane(4) + (16,6,2)-Balanced Incomplete Block Design + sage: designs.biplane(7, existence=True) + True + sage: designs.biplane(11) + (79,13,2)-Balanced Incomplete Block Design + + TESTS:: - A Balanced Incomplete Block Design of parameters `v,k` is a collection + sage: designs.biplane(9) + (56,11,2)-Balanced Incomplete Block Design + + Check all knwon biplanes:: + + sage: [n for n in [0,1,2,3,4,7,9,11] if designs.biplane(n, existence=True) is True] + [0, 1, 2, 3, 4, 7, 9, 11] + """ + k = n+2 + v = (k*(k-1))//2 + 1 + return balanced_incomplete_block_design(v, k, lambd=2, existence=existence) + + +def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=False): + r""" + Return a BIBD of parameters `v,k, \lambda`. + + A Balanced Incomplete Block Design of parameters `v,k,\lambda` is a collection `\mathcal C` of `k`-subsets of `V=\{0,\dots,v-1\}` such that for any two - distinct elements `x,y\in V` there is a unique element `S\in \mathcal C` + distinct elements `x,y\in V` there are `\lambda` elements `S\in \mathcal C` such that `x,y\in S`. - More general definitions sometimes involve a `\lambda` parameter, and we - assume here that `\lambda=1`. - For more information on BIBD, see the :wikipedia:`corresponding Wikipedia entry `. INPUT: - - ``v,k`` (integers) + - ``v,k,lambd`` (integers) - ``existence`` (boolean) -- instead of building the design, return: @@ -109,16 +151,14 @@ def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False): EXAMPLES:: - sage: designs.balanced_incomplete_block_design(7, 3).blocks() + sage: designs.balanced_incomplete_block_design(7, 3, 1).blocks() [[0, 1, 3], [0, 2, 4], [0, 5, 6], [1, 2, 6], [1, 4, 5], [2, 3, 5], [3, 4, 6]] - sage: B = designs.balanced_incomplete_block_design(66, 6, use_LJCR=True) # optional - internet + sage: B = designs.balanced_incomplete_block_design(66, 6, 1, use_LJCR=True) # optional - internet sage: B # optional - internet - Incidence structure with 66 points and 143 blocks + (66,6,1)-Balanced Incomplete Block Design sage: B.blocks() # optional - internet [[0, 1, 2, 3, 4, 65], [0, 5, 22, 32, 38, 58], [0, 6, 21, 30, 43, 48], ... - sage: designs.balanced_incomplete_block_design(66, 6, use_LJCR=True) # optional - internet - Incidence structure with 66 points and 143 blocks - sage: designs.balanced_incomplete_block_design(216, 6) + sage: designs.balanced_incomplete_block_design(216, 6, 1) Traceback (most recent call last): ... NotImplementedError: I don't know how to build a (216,6,1)-BIBD! @@ -167,29 +207,49 @@ def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False): sage: designs.balanced_incomplete_block_design(1171,10) (1171,10,1)-Balanced Incomplete Block Design - And we know some inexistence results:: + And we know some nonexistence results:: sage: designs.balanced_incomplete_block_design(21,6,existence=True) False - """ - lmbd = 1 + Some BIBDs with `\lambda \neq 1`:: + + sage: designs.balanced_incomplete_block_design(176, 50, 14, existence=True) + True + sage: designs.balanced_incomplete_block_design(64,28,12) + (64,28,12)-Balanced Incomplete Block Design + sage: designs.balanced_incomplete_block_design(37,9,8) + (37,9,8)-Balanced Incomplete Block Design + sage: designs.balanced_incomplete_block_design(15,7,3) + (15,7,3)-Balanced Incomplete Block Design + + Some BIBDs from the recursive construction :: + + sage: designs.balanced_incomplete_block_design(76,16,4) + (76,16,4)-Balanced Incomplete Block Design + sage: designs.balanced_incomplete_block_design(10,4,2) + (10,4,2)-Balanced Incomplete Block Design + sage: designs.balanced_incomplete_block_design(50,25,24) + (50,25,24)-Balanced Incomplete Block Design + sage: designs.balanced_incomplete_block_design(29,15,15) + (29,15,15)-Balanced Incomplete Block Design + """ # Trivial BIBD if v == 1: if existence: return True - return BalancedIncompleteBlockDesign(v, [], check=False) + return BIBD(v, [], check=False) if k == v: if existence: return True - return BalancedIncompleteBlockDesign(v, [list(range(v))], check=False, copy=False) + return BIBD(v, [list(range(v)) for _ in range(lambd)],lambd=lambd, check=False, copy=False) # Non-existence of BIBD if (v < k or k < 2 or - (v-1) % (k-1) != 0 or - (v*(v-1)) % (k*(k-1)) != 0 or + (lambd*(v-1)) % (k-1) != 0 or + (lambd*v*(v-1)) % (k*(k-1)) != 0 or # From the Handbook of combinatorial designs: # # With lambda>1 other exceptions are @@ -197,76 +257,182 @@ def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False): (k==6 and v in [36,46]) or (k==7 and v == 43) or # Fisher's inequality - (v*(v-1))/(k*(k-1)) < v): + (lambd*v*(v-1))/(k*(k-1)) < v): + if existence: + return False + raise EmptySetError("There exists no ({},{},{})-BIBD".format(v, k, lambd)) + + # Non-esistence by BRC Theoerem + if BruckRyserChowla_check(v, k, lambd) is False: if existence: return False - raise EmptySetError("There exists no ({},{},{})-BIBD".format(v,k,lmbd)) + raise EmptySetError("There exists no ({},{},{})-BIBD by Bruck-Ryser-Chowla Theorem".format(v,k,lambd)) if k == 2: if existence: return True - from itertools import combinations - return BalancedIncompleteBlockDesign(v, combinations(list(range(v)),2), check=False, copy=True) - if k == 3: + return BIBD(v, [[x, y] for _ in range(lambd) for x in range(v) for y in range(x+1, v) if x != y], lambd=lambd, check=False, copy=True) + if k == 3 and lambd == 1: if existence: return v%6 == 1 or v%6 == 3 return steiner_triple_system(v) - if k == 4: + if k == 4 and lambd == 1: if existence: return v%12 == 1 or v%12 == 4 - return BalancedIncompleteBlockDesign(v, v_4_1_BIBD(v), copy=False) - if k == 5: + return BIBD(v, v_4_1_BIBD(v), copy=False) + if k == 5 and lambd == 1: if existence: return v%20 == 1 or v%20 == 5 - return BalancedIncompleteBlockDesign(v, v_5_1_BIBD(v), copy=False) + return BIBD(v, v_5_1_BIBD(v), copy=False) from .difference_family import difference_family from .database import BIBD_constructions - if (v,k,1) in BIBD_constructions: + if (v, k, lambd) in BIBD_constructions: if existence: return True - return BlockDesign(v,BIBD_constructions[(v,k,1)](), copy=False) - if BIBD_from_arc_in_desarguesian_projective_plane(v,k,existence=True): + return BIBD(v,BIBD_constructions[(v, k, lambd)](), lambd=lambd, copy=False) + if lambd == 1 and BIBD_from_arc_in_desarguesian_projective_plane(v, k, existence=True): if existence: return True - B = BIBD_from_arc_in_desarguesian_projective_plane(v,k) - return BalancedIncompleteBlockDesign(v, B, copy=False) - if BIBD_from_TD(v,k,existence=True): + B = BIBD_from_arc_in_desarguesian_projective_plane(v, k) + return BIBD(v, B, copy=False) + if lambd == 1 and BIBD_from_TD(v, k, existence=True) is True: if existence: return True - return BalancedIncompleteBlockDesign(v, BIBD_from_TD(v,k), copy=False) - if v == (k-1)**2+k and is_prime_power(k-1): + return BIBD(v, BIBD_from_TD(v, k), copy=False) + if lambd == 1 and v == (k-1)**2+k and is_prime_power(k-1): if existence: return True from .block_design import projective_plane - return BalancedIncompleteBlockDesign(v, projective_plane(k-1),copy=False) - if difference_family(v,k,existence=True): + return BIBD(v, projective_plane(k-1),copy=False) + if difference_family(v, k, l=lambd, existence=True) is True: if existence: return True - G,D = difference_family(v,k) - return BalancedIncompleteBlockDesign(v, BIBD_from_difference_family(G,D,check=False), copy=False) - if use_LJCR: + G, D = difference_family(v, k, l=lambd) + return BIBD(v, BIBD_from_difference_family(G, D, check=False), lambd=lambd, copy=False) + if lambd == 1 and use_LJCR: from .covering_design import best_known_covering_design_www - B = best_known_covering_design_www(v,k,2) - - # Is it a BIBD or just a good covering ? - expected_n_of_blocks = binomial(v,2)//binomial(k,2) - if B.low_bd() > expected_n_of_blocks: - if existence: - return False - raise EmptySetError("There exists no ({},{},{})-BIBD".format(v,k,lmbd)) - B = B.incidence_structure() - if B.num_blocks() == expected_n_of_blocks: - if existence: - return True - else: - return B + values_in_db = False + try: + B = best_known_covering_design_www(v, k, 2) + values_in_db = True + except ValueError: + # the parameters are not in the LJCR database + pass + + if values_in_db: + # Is it a BIBD or just a good covering? + expected_n_of_blocks = binomial(v, 2) // binomial(k, 2) + if B.low_bd() > expected_n_of_blocks: + if existence: + return False + raise EmptySetError(f"there exists no ({v},{k},{lambd})-BIBD") + B = B.incidence_structure() + if B.num_blocks() == expected_n_of_blocks: + if existence: + return True + else: + return BIBD(B.ground_set(), B.blocks(), k=k, lambd=1, copy=False) + + + if ( (k+lambd)*(k+lambd-1) == lambd*(v+k+lambd-1) and + balanced_incomplete_block_design(v+k+lambd, k+lambd, lambd, existence=True) is True): + # By removing a block and all points of that block from the + # symmetric (v+k+lambd, k+lambd, lambd) BIBD + # we get a (v, k, lambd) BIBD + if existence: + return True + + D = balanced_incomplete_block_design(v+k+lambd, k+lambd, lambd) + Br = D.blocks()[0] # block to remove + blocks = D.blocks()[1:] + + blocks = [set(B).difference(Br) for B in blocks] + points = set(D.ground_set()).difference(Br) + + return BalancedIncompleteBlockDesign(points, blocks, k=k, lambd=lambd, copy=False) if existence: return Unknown else: - raise NotImplementedError("I don't know how to build a ({},{},1)-BIBD!".format(v,k)) + raise NotImplementedError("I don't know how to build a ({},{},{})-BIBD!".format(v, k, lambd)) + +def BruckRyserChowla_check(v, k, lambd): + r""" + Check whether the parameters passed satisfy the Bruck-Ryser-Chowla theorem. + + For more information on the theorem, see the + :wikipedia:`corresponding Wikipedia entry `. + + INPUT: + + - ``v, k, lambd`` -- integers; parameters to check + + OUTPUT: + + - ``True`` -- the parameters satisfy the theorem + + - ``False`` -- the theorem fails for the given parameters + + - ``Unknown`` -- the preconditions of the theorem are not met + + EXAMPLES: + + sage: from sage.combinat.designs.bibd import BruckRyserChowla_check + sage: BruckRyserChowla_check(22,7,2) + False + + Nonexistence of projective planes of order 6 and 14 + + sage: from sage.combinat.designs.bibd import BruckRyserChowla_check + sage: BruckRyserChowla_check(43,7,1) + False + sage: BruckRyserChowla_check(211,15,1) + False + + Existence of symmetric BIBDs with parameters `(79,13,2)` and `(56,11,2)` + + sage: from sage.combinat.designs.bibd import BruckRyserChowla_check + sage: BruckRyserChowla_check(79,13,2) + True + sage: BruckRyserChowla_check(56,11,2) + True + + TESTS: + + Test some non-symmetric parameters:: + + sage: from sage.combinat.designs.bibd import BruckRyserChowla_check + sage: BruckRyserChowla_check(89,11,3) + Unknown + sage: BruckRyserChowla_check(25,23,2) + Unknown + + Clearly wrong parameters satisfying the theorem:: + + sage: from sage.combinat.designs.bibd import BruckRyserChowla_check + sage: BruckRyserChowla_check(13,25,50) + True + + """ + from sage.arith.misc import is_square + from sage.schemes.plane_conics.constructor import Conic + from sage.rings.rational_field import QQ + + # design is not symmetric + if k*(k-1) != lambd*(v-1): + return Unknown + + if v%2 == 0: + return is_square(k-lambd) + + g = 1 if v%4 == 1 else -1 + C = Conic(QQ, [1, lambd - k, -g * lambd]) + + (flag, sol) = C.has_rational_point(point=True) + + return flag def steiner_triple_system(n): r""" @@ -351,7 +517,7 @@ def steiner_triple_system(n): # apply T and remove duplicates sts = set(frozenset(T(xx) for xx in x) for x in sts) - return BalancedIncompleteBlockDesign(n, sts, name=name,check=False) + return BIBD(n, sts, name=name,check=False) def BIBD_from_TD(v,k,existence=False): @@ -434,8 +600,8 @@ def BIBD_from_TD(v,k,existence=False): """ # First construction if (v%k == 0 and - balanced_incomplete_block_design(v//k,k,existence=True) and - transversal_design(k,v//k,existence=True)): + balanced_incomplete_block_design(v//k, k, existence=True) is True and + transversal_design(k, v//k, existence=True) is True): if existence: return True @@ -450,8 +616,8 @@ def BIBD_from_TD(v,k,existence=False): # Second construction elif ((v-1)%k == 0 and - balanced_incomplete_block_design((v-1)//k+1,k,existence=True) and - transversal_design(k,(v-1)//k,existence=True)): + balanced_incomplete_block_design((v-1)//k+1,k,existence=True) is True and + transversal_design(k,(v-1)//k,existence=True)) is True: if existence: return True @@ -467,9 +633,8 @@ def BIBD_from_TD(v,k,existence=False): # Third construction elif ((v-k)%k == 0 and - balanced_incomplete_block_design((v-k)//k+k,k,existence=True) and - transversal_design(k,(v-k)//k,existence=True)): - + balanced_incomplete_block_design((v-k)//k+k,k,existence=True) is True + and transversal_design(k,(v-k)//k,existence=True) is True): if existence: return True @@ -566,7 +731,8 @@ def BIBD_from_difference_family(G, D, lambd=None, check=True): GG = Gset.copy() while GG: g = GG.pop() - if S: GG.difference_update(mul(s,g) for s in S) + if S: + GG.difference_update(mul(s,g) for s in S) bibd.append([p_to_i[mul(i,g)] for i in b]) if check: @@ -1030,20 +1196,28 @@ def _get_r_s_t_u(v): s = r//150 x = r%150 - if x == 0: t,u = 30*s-5, 25 - elif x == 1: t,u = 30*s-5, 26 - elif x <= 21: t,u = 30*s+1, x-5 - elif x == 25: t,u = 30*s+5, 0 - elif x == 26: t,u = 30*s+5, 1 - elif x == 30: t,u = 30*s+5, 5 - elif x <= 51: t,u = 30*s+5, x-25 - elif x <= 66: t,u = 30*s+11, x-55 - elif x <= 96: t,u = 30*s+11, x-55 - elif x <= 121: t,u = 30*s+11, x-55 - elif x <= 146: t,u = 30*s+25, x-125 + if x == 0: + t,u = 30*s-5, 25 + elif x == 1: + t,u = 30*s-5, 26 + elif x <= 21: + t,u = 30*s+1, x-5 + elif x == 25: + t,u = 30*s+5, 0 + elif x == 26: + t,u = 30*s+5, 1 + elif x == 30: + t,u = 30*s+5, 5 + elif x <= 51: + t,u = 30*s+5, x-25 + elif x <= 121: + t,u = 30*s+11, x-55 + elif x <= 146: + t,u = 30*s+25, x-125 return r,s,t,u + def PBD_from_TD(k,t,u): r""" Return a `(kt,\{k,t\})`-PBD if `u=0` and a `(kt+u,\{k,k+1,t,u\})`-PBD otherwise. @@ -1114,9 +1288,10 @@ def BIBD_5q_5_for_q_prime_power(q): return B + def BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=False): r""" - Returns a `(n,k,1)`-BIBD from a maximal arc in a projective plane. + Return a `(n,k,1)`-BIBD from a maximal arc in a projective plane. This function implements a construction from Denniston [Denniston69]_, who describes a maximal :meth:`arc @@ -1465,3 +1640,5 @@ def arc(self, s=2, solver=None, verbose=0): p.add_constraint(p.sum(b[k] for k in i) <= s) p.solve(log=verbose) return [self._points[i] for (i,j) in p.get_values(b).items() if j == 1] + +BIBD = BalancedIncompleteBlockDesign diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index 047d6977db7..dad5da3fb36 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -67,9 +67,6 @@ from sage.matrix.matrix_space import MatrixSpace -import six - - BlockDesign = IncidenceStructure ### utility functions ------------------------------------------------------- @@ -275,7 +272,7 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None, point_coordinates=True, ch blocks.append(b) B = BlockDesign(len(points), blocks, name="ProjectiveGeometryDesign", check=check) if point_coordinates: - B.relabel({i:p[0] for p,i in six.iteritems(points)}) + B.relabel({i:p[0] for p,i in points.items()}) elif algorithm == "gap": # Requires GAP's Design from sage.interfaces.gap import gap @@ -748,6 +745,7 @@ def projective_plane(n, check=True, existence=False): Unknown """ from sage.rings.sum_of_squares import is_sum_of_two_squares_pyx + from sage.combinat.designs.bibd import BruckRyserChowla_check if n <= 1: if existence: @@ -761,7 +759,7 @@ def projective_plane(n, check=True, existence=False): "projective planes of order 10\" (1989), Canad. J. Math.") raise EmptySetError("No projective plane of order 10 exists by %s"%ref) - if (n%4) in [1,2] and not is_sum_of_two_squares_pyx(n): + if BruckRyserChowla_check(n*n+n+1, n+1, 1) is False: if existence: return False raise EmptySetError("By the Bruck-Ryser theorem, no projective" @@ -874,7 +872,7 @@ def AffineGeometryDesign(n, d, F, point_coordinates=True, check=True): B = BlockDesign(len(points), blocks, name="AffineGeometryDesign", check=check) if point_coordinates: - rd = {i:p[0][1:] for p,i in six.iteritems(points)} + rd = {i:p[0][1:] for p,i in points.items()} for v in rd.values(): v.set_immutable() B.relabel(rd) diff --git a/src/sage/combinat/designs/covering_design.py b/src/sage/combinat/designs/covering_design.py index f204dddd09e..41d1fe32754 100644 --- a/src/sage/combinat/designs/covering_design.py +++ b/src/sage/combinat/designs/covering_design.py @@ -46,7 +46,7 @@ # **************************************************************************** from __future__ import print_function -from six.moves.urllib.request import urlopen +from urllib.request import urlopen from sage.misc.sage_eval import sage_eval from sage.structure.sage_object import SageObject diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 91e2f54fc70..4f1343a57c6 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Database of small combinatorial designs @@ -43,17 +44,22 @@ Chapman & Hall/CRC 2012 +.. [Aschbacher71] \M. Aschbacher, + On collineation groups of symmetric block designs. + J. Combinatorial Theory Ser. A 11 (1971), pp. 272–281. + +.. [Hall71] \M. Hall, Jr., + Combinatorial designs and groups. + Actes du Congrès International des Mathématiciens (Nice, 1970), + v.3, pp. 217–222. Gauthier-Villars, Paris, 1971. + Functions --------- """ from __future__ import print_function, absolute_import -from six import iteritems -from six.moves import range, zip from sage.combinat.designs.orthogonal_arrays import (OA_from_quasi_difference_matrix, - OA_from_Vmt, QDM_from_Vmt, - OA_from_wider_OA, OA_from_PBD, OA_n_times_2_pow_c_from_matrix, orthogonal_array) @@ -773,7 +779,6 @@ def OA_9_120(): sage: designs.orthogonal_arrays.is_available(9,120) True """ - from .incidence_structures import IncidenceStructure RBIBD_120 = RBIBD_120_8_1() equiv = [RBIBD_120[i*15:(i+1)*15] for i in range(17)] @@ -1685,7 +1690,7 @@ def OA_10_469(): blocks[len(B)].append(B) # Product of each symmetric design with the OA - for b_size,symmetric_design in iteritems(blocks): + for b_size,symmetric_design in blocks.items(): matrix = _reorder_matrix(symmetric_design) OA.extend([[B[xx] for xx in R] for R in incomplete_orthogonal_array(9,b_size,[1]*b_size) @@ -2695,7 +2700,7 @@ def QDM_57_9_1_1_8(): (12,413) : ((0,1,436,546,977,467,242,3695,682,483,3026,461,1334), _ref_Abel_v_12_t), } # Translate all V(m,t) into (mt+1,m+2;1,0;t)-QDM constructors -for (m,t),(vec,source) in iteritems(Vmt_vectors): +for (m,t),(vec,source) in Vmt_vectors.items(): n,k,lmbda,mu,u = (m*t+1,m+2,1,0,t) if not (n+u,lmbda) in QDM: QDM[n+u,lmbda] = {} @@ -3153,7 +3158,6 @@ def DM_12_6_1(): :doi:`10.1016/0012-365X(75)90040-0`, Discrete Mathematics, Volume 11, Issue 3, 1975, Pages 255-369. """ - from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as AdditiveCyclic G = AdditiveCyclic(2).cartesian_product(AdditiveCyclic(6)) M = [[(0,0),(0,0),(0,0),(0,0),(0,0),(0,0)], @@ -3691,7 +3695,6 @@ def DM_52_6_1(): sage: _ = designs.difference_matrix(52,6) """ - from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as AdditiveCyclic from sage.rings.finite_rings.finite_field_constructor import FiniteField F4 = FiniteField(4,'z') G13 = FiniteField(13) @@ -3723,7 +3726,6 @@ def DM_52_6_1(): Mb=[[(0,0)]*6] from itertools import product - p = lambda x,y : G(tuple([x*yy for yy in G(y)])) def t1(i,R): if i > 1: @@ -4567,17 +4569,140 @@ def BIBD_201_6_1(): bibd = RecursivelyEnumeratedSet([frozenset(e) for e in bibd], successors=gens) return IncidenceStructure(bibd)._blocks +def BIBD_79_13_2(): + r""" + Return a symmetric `(79,13,2)`-BIBD. + + The construction implemented is the one described in [Aschbacher71]_. + A typo in that paper was corrected in [Hall71]_. + + .. NOTE:: + + A symmetric `(v,k,\lambda)` BIBD is a `(v,k,\lambda)` BIBD with `v` blocks. + + EXAMPLES: + + sage: from sage.combinat.designs.database import BIBD_79_13_2 + sage: D = IncidenceStructure(BIBD_79_13_2()) + sage: D.is_t_design(t=2, v=79, k=13, l=2) + True + """ + from sage.libs.gap.libgap import libgap + + g11 = libgap.Z(11) # generator for GF(11) + one = g11**0 + zero = 0*g11 + + X = libgap([[one, one], [zero, one]]) + Y = libgap([[5*one, zero], [zero, 9*one]]) + Z = libgap([[-one, zero], [zero, one]]) + + G = libgap.Group(X, Y, Z) + H1 = libgap.Group(X, Y) + H23 = libgap.Group(Y, Z) + H4 = libgap.Group(Z) + + P1Action = G.FactorCosetAction(H1) + P23Action = G.FactorCosetAction(H23) + P4Action = G.FactorCosetAction(H4) + + libgap.set_global("p1Act", P1Action) + libgap.set_global("p23Act", P23Action) + libgap.set_global("p4Act", P4Action) + + action = libgap.function_factory("""function(pair, g) + local i, C, homs; + i := pair[1]; + C := pair[2]; + homs := [p1Act, p23Act, p23Act, p4Act]; + return [i, C^(ImageElm(homs[i],g))]; + end;""") + + p1 = (1,1) + p2 = (2,1) + p3 = (3,1) + p4 = (4,1) + + B1 = list(libgap.Orbit(H4, p1, action)) + list(libgap.Orbit(G, p2, action)) + B2 = list(libgap.Orbit(H4, p1, action)) + list(libgap.Orbit(G, p3, action)) + B3 = list(libgap([p1, p2, p3])) + list(libgap.Orbit(libgap.Group(Y), action(p4, X), action)) + list(libgap.Orbit(libgap.Group(Y), action(p4, X**4), action)) + B4 = [action(p2, X**2), action(p2, X**-2), action(p3, X**5), action(p3, X**-5), p4, + action(p4, X * Y**2), action(p4, X**-1 * Y**2), action(p4, X*Y), action(p4, X**-1 * Y), + action(p4, X**5 * Y), action(p4, X**-5 * Y), action(p4, X**5 * Y**4), action(p4, X**-5 * Y**4)] + + points = [] + for i in range(1,5): + points += list(libgap.Orbit(G, (i,1), action)) + + permAction = libgap.Action(G, points, action) + + baseBlocks = [libgap.Set(list(map(lambda p: libgap.Position(points, p), B))) for B in [B1, B2, B3, B4]] + + B3Orbit = libgap.Orbit(permAction, baseBlocks[2], libgap.OnSets) + B4Orbit = libgap.Orbit(permAction, baseBlocks[3], libgap.OnSets) + blocks = baseBlocks[0:2] + list(B3Orbit) + list(B4Orbit) + + # clear gap variables + libgap.unset_global("p1Act") + libgap.unset_global("p23Act") + libgap.unset_global("p4Act") + return [[int(t)-1 for t in y] for y in blocks] + +def BIBD_56_11_2(): + r""" + Return a symmetric `(56,11,2)`-BIBD. + + The construction implemented is given in [Hall71]_. + + .. NOTE:: + + A symmetric `(v,k,\lambda)` BIBD is a `(v,k,\lambda)` BIBD with `v` blocks. + + EXAMPLES: + + sage: from sage.combinat.designs.database import BIBD_56_11_2 + sage: D = IncidenceStructure(BIBD_56_11_2()) + sage: D.is_t_design(t=2, v=56, k=11, l=2) + True + """ + from sage.libs.gap.libgap import libgap + from .incidence_structures import IncidenceStructure + + a = list(range(2,57)) + [50] + a[6] = 1 + a[13] = 8 + a[20] = 15 + a[27] = 22 + a[34] = 29 + a[41] = 36 + a[48] = 43 + + b = [1,8,27,36,20,14,42,41,29,52,24,30,55,22,26,21,10,40,23,53, + 56,6,49,46,50,32,28,3,34,48,4,15,13,9,18,31,51,39,43,35, + 2,54,38,25,45,11,37,12,19,44,47,17,5,7,33,16] + + a = libgap.PermList(a) + b = libgap.PermList(b) + G = libgap.Group(a,b) + + B = libgap.Set([1,12,19,23,30,37,45,47,48,49,51]) + + D = IncidenceStructure(libgap.Orbit(G, B, libgap.OnSets)) + return D._blocks + # Index of the BIBD constructions # # Associates to triple (v,k,lambda) a function that return a # (n,k,lambda)-BIBD family. # # This dictionary is used by designs.BalancedIncompleteBlockDesign - +# Note that the values are a list of blocks and not a design object BIBD_constructions = { ( 45,9,8): BIBD_45_9_8, + (56,11,2): BIBD_56_11_2, ( 66,6,1): BIBD_66_6_1, ( 76,6,1): BIBD_76_6_1, + (79,13,2): BIBD_79_13_2, ( 96,6,1): BIBD_96_6_1, (120,8,1): RBIBD_120_8_1, (106,6,1): BIBD_106_6_1, @@ -4586,7 +4711,7 @@ def BIBD_201_6_1(): (136,6,1): BIBD_136_6_1, (141,6,1): BIBD_141_6_1, (171,6,1): BIBD_171_6_1, - (176,50,14): HigmanSimsDesign, + (176,50,14): lambda : HigmanSimsDesign().blocks(), (196,6,1): BIBD_196_6_1, (201,6,1): BIBD_201_6_1, } diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index 9ff77953bac..fafb01b3d07 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -58,6 +58,7 @@ :meth:`~sage.combinat.designs.bibd.steiner_triple_system` :meth:`~sage.combinat.designs.steiner_quadruple_systems.steiner_quadruple_system` :meth:`~sage.combinat.designs.block_design.projective_plane` + :meth:`~sage.combinat.designs.biplane` And the :meth:`designs.best_known_covering_design_from_LJCR ` function @@ -105,7 +106,7 @@ lazy_import('sage.combinat.designs.difference_matrices', 'difference_matrix') lazy_import('sage.combinat.designs.bibd', - ('balanced_incomplete_block_design', 'steiner_triple_system')) + ('balanced_incomplete_block_design', 'steiner_triple_system', 'biplane')) lazy_import('sage.combinat.designs.resolvable_bibd', ('resolvable_balanced_incomplete_block_design', 'kirkman_triple_system')) diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 2facd8b8177..7549516400d 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -39,22 +39,17 @@ Functions --------- """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2014 Vincent Delecroix <20100.delecroix@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** # python3 from __future__ import division, print_function, absolute_import -from builtins import zip -import six -from six import itervalues -from six.moves import range - from sage.misc.cachefunc import cached_function from sage.categories.sets_cat import EmptySetError @@ -63,6 +58,7 @@ from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ + def group_law(G): r""" Return a triple ``(identity, operation, inverse)`` that define the @@ -219,8 +215,6 @@ def is_difference_family(G, D, v=None, k=None, l=None, verbose=False): sage: _ False """ - import operator - identity, mul, inv = group_law(G) Glist = list(G) @@ -289,9 +283,9 @@ def is_difference_family(G, D, v=None, k=None, l=None, verbose=False): where[gg].add(i) tmp_counter[gg] += 1 - if sum(itervalues(tmp_counter)) != k * (k - 1): + if sum(tmp_counter.values()) != k * (k - 1): if verbose: - print("repeated element in the {}-th block {}".format(i,d)) + print("repeated element in the {}-th block {}".format(i, d)) return False # Normalized number of occurrences added to counter @@ -426,7 +420,7 @@ def df_q_6_1(K, existence=False, check=True): sage: from sage.combinat.designs.difference_family import is_difference_family, df_q_6_1 sage: prime_powers = [v for v in range(31,500,30) if is_prime_power(v)] - sage: parameters = [v for v in prime_powers if df_q_6_1(GF(v,'a'), existence=True)] + sage: parameters = [v for v in prime_powers if df_q_6_1(GF(v,'a'), existence=True) is True] sage: parameters [31, 151, 181, 211, 241, 271, 331, 361, 421] sage: for v in parameters: @@ -507,7 +501,7 @@ def radical_difference_set(K, k, l=1, existence=False, check=True): sage: for k in range(2,50): ....: for l in reversed(divisors(k*(k-1))): ....: v = k*(k-1)//l + 1 - ....: if is_prime_power(v) and radical_difference_set(GF(v,'a'),k,l,existence=True): + ....: if is_prime_power(v) and radical_difference_set(GF(v,'a'),k,l,existence=True) is True: ....: _ = radical_difference_set(GF(v,'a'),k,l) ....: print("{:3} {:3} {:3}".format(v,k,l)) 3 2 1 @@ -878,7 +872,7 @@ def radical_difference_family(K, k, l=1, existence=False, check=True): ....: for q in range(k*(k-1)+1, 2000, k*(k-1)): ....: if is_prime_power(q): ....: K = GF(q,'a') - ....: if radical_difference_family(K, k, existence=True): + ....: if radical_difference_family(K, k, existence=True) is True: ....: list_q.append(q) ....: _ = radical_difference_family(K,k) ....: print(" ".join(str(p) for p in list_q)) @@ -896,7 +890,6 @@ def radical_difference_family(K, k, l=1, existence=False, check=True): """ v = K.cardinality() x = K.multiplicative_generator() - one = K.one() e = k*(k-1) if (l*(v-1)) % e: raise ValueError("k (k-1) = {} should be a multiple of l (v-1) ={}".format( @@ -1209,11 +1202,11 @@ def hadamard_difference_set_product(G1, D1, G2, D2): sage: G11,D11 = hadamard_difference_set_product(G1,D1,G1,D1) sage: assert is_difference_family(G11, D11, 256, 120, 56) - sage: assert designs.difference_family(256, 120, 56, existence=True) + sage: assert designs.difference_family(256, 120, 56, existence=True) is True sage: G12,D12 = hadamard_difference_set_product(G1,D1,G2,D2) sage: assert is_difference_family(G12, D12, 576, 276, 132) - sage: assert designs.difference_family(576, 276, 132, existence=True) + sage: assert designs.difference_family(576, 276, 132, existence=True) is True """ from sage.categories.cartesian_product import cartesian_product @@ -1379,7 +1372,8 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch ....: constructions = [] ....: for k in range(2,10): ....: for l in range(1,10): - ....: if designs.difference_family(v,k,l,existence=True): + ....: ret = designs.difference_family(v,k,l,existence=True) + ....: if ret is True: ....: constructions.append((k,l)) ....: _ = designs.difference_family(v,k,l) ....: if constructions: @@ -1490,7 +1484,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch sage: for N in range(2,21): ....: v = 4*N^2; k = 2*N^2-N; l = N^2-N ....: status = designs.difference_family(v,k,l,existence=True) - ....: print("{:2} {}".format(N,designs.difference_family(v,k,l,explain_construction=True) if status else status)) + ....: print("{:2} {}".format(N,designs.difference_family(v,k,l,explain_construction=True) if status is True else status)) 2 McFarland 1973 construction 3 Turyn 1965 construction 4 McFarland 1973 construction @@ -1549,13 +1543,18 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch k = ZZ(k) l = ZZ(l) + if v < 0 or k < 0 or l < 0: + if existence: + return False + raise EmptySetError("No difference family eixsts with negative parameters") + if (v,k,l) in DF: if existence: return True elif explain_construction: return "The database contains a ({},{},{})-difference family".format(v,k,l) - vv, blocks = next(six.iteritems(DF[v,k,l])) + vv, blocks = next(iter(DF[v,k,l].items())) # Build the group from sage.rings.finite_rings.integer_mod_ring import Zmod @@ -1597,12 +1596,25 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch "sage-devel@googlegroups.com".format(v,k,l)) return G,df + if k in [0,1]: + # Then \Delta D_i is empty + # So if G\{0} is empty is good, otherwise not + if v == 1: + if existence: + return True + from sage.rings.finite_rings.integer_mod_ring import Zmod + l = [0] if k ==1 else [] + return Zmod(1),[l] + + if existence: + return False + raise EmptySetError("No difference family exists with k=1 and v!=1") + e = k*(k-1) if (l*(v-1)) % e: if existence: return Unknown raise NotImplementedError("No construction available for ({},{},{})-difference family".format(v,k,l)) - t = l*(v-1) // e # number of blocks # trivial construction if k == (v-1) and l == (v-2): @@ -1666,7 +1678,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch else: raise EmptySetError("by McFarland 1989 such difference family does not exist") - elif len(factorization) == 1 and radical_difference_family(K, k, l, existence=True): + elif len(factorization) == 1 and radical_difference_family(K, k, l, existence=True) is True: if existence: return True elif explain_construction: @@ -1675,7 +1687,10 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch D = radical_difference_family(K,k,l) G = K - elif len(factorization) == 1 and l == 1 and k == 6 and df_q_6_1(K, existence=True): + elif (len(factorization) == 1 + and l == 1 + and k == 6 + and df_q_6_1(K, existence=True) is True): if existence: return True elif explain_construction: diff --git a/src/sage/combinat/designs/difference_matrices.py b/src/sage/combinat/designs/difference_matrices.py index 5eddf33ee5f..6e2e78eb284 100644 --- a/src/sage/combinat/designs/difference_matrices.py +++ b/src/sage/combinat/designs/difference_matrices.py @@ -66,8 +66,8 @@ def find_product_decomposition(g, k, lmbda=1): g2 = g//g1 if g1 > g2: break - if (difference_matrix(g1,k,lmbda1,existence=True) and - difference_matrix(g2,k,lmbda2,existence=True)): + if (difference_matrix(g1,k,lmbda1,existence=True) is True and + difference_matrix(g2,k,lmbda2,existence=True) is True): return (g1,lmbda1),(g2,lmbda2) return False @@ -245,7 +245,7 @@ def difference_matrix(g,k,lmbda=1,existence=False,check=True): # (find the max k such that there exists a DM) elif k is None: i = 2 - while difference_matrix(g=g,k=i,lmbda=lmbda,existence=True): + while difference_matrix(g=g,k=i,lmbda=lmbda,existence=True) is True: i += 1 return i-1 diff --git a/src/sage/combinat/designs/ext_rep.py b/src/sage/combinat/designs/ext_rep.py index f9662b13cac..57f3b7eb636 100644 --- a/src/sage/combinat/designs/ext_rep.py +++ b/src/sage/combinat/designs/ext_rep.py @@ -39,12 +39,11 @@ import os.path import gzip import bz2 + +from urllib.request import urlopen + from sage.misc.all import tmp_filename -# import compatible with py2 and py3 -import six -from six.moves.urllib.request import urlopen -from six import string_types, PY2 XML_NAMESPACE = 'http://designtheory.org/xml-namespace' DTRS_PROTOCOL = '2.0' @@ -662,7 +661,7 @@ def __init__(self, node): """ - if isinstance(node, string_types): + if isinstance(node, str): node = (node, {}, []) name, attributes, children = node self.xt_node = node @@ -870,7 +869,7 @@ def _start_element(self, name, attrs): elif name == 'designs': pass # self.outf.write(' <%s>\n' % name) if self.in_item: - for k, v in six.iteritems(attrs): + for k, v in attrs.items(): attrs[k] = _encode_attribute(v) new_node = (name, attrs, []) self.node_stack.append(new_node) @@ -990,10 +989,8 @@ def parse(self, xml_source): p.StartElementHandler = self._start_element p.EndElementHandler = self._end_element p.CharacterDataHandler = self._char_data - if PY2: - p.returns_unicode = 0 - if isinstance(xml_source, string_types+(bytes,)): + if isinstance(xml_source, (str, bytes)): p.Parse(xml_source) else: p.ParseFile(xml_source) diff --git a/src/sage/combinat/designs/group_divisible_designs.py b/src/sage/combinat/designs/group_divisible_designs.py index c68ae3c398f..ff7bf291603 100644 --- a/src/sage/combinat/designs/group_divisible_designs.py +++ b/src/sage/combinat/designs/group_divisible_designs.py @@ -31,7 +31,6 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from sage.arith.all import is_prime_power from sage.misc.unknown import Unknown diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 3e03a076730..12eb87c6c31 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -40,10 +40,6 @@ # ************************************************************************** from __future__ import print_function -import six -from six import itervalues -from six.moves import range - from sage.rings.integer import Integer from sage.misc.latex import latex from sage.sets.set import Set @@ -482,8 +478,8 @@ def is_isomorphic(self, other, certificate=False): if A == B: if certificate: - B_canon_rev = {y:x for x,y in six.iteritems(B_canon)} - return {x:B_canon_rev[xint] for x,xint in six.iteritems(A_canon)} + B_canon_rev = {y:x for x,y in B_canon.items()} + return {x:B_canon_rev[xint] for x,xint in A_canon.items()} else: return True else: @@ -890,7 +886,7 @@ def degrees(self, size=None): for s in combinations(b,size): d[s]+=1 if self._point_to_index: - return {tuple([self._points[x] for x in s]):v for s,v in six.iteritems(d)} + return {tuple([self._points[x] for x in s]):v for s,v in d.items()} else: return d @@ -1189,6 +1185,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. @@ -1405,7 +1435,7 @@ def packing(self, solver=None, verbose=0): p.solve(log=verbose) return [[self._points[x] for x in self._blocks[i]] - for i, v in six.iteritems(p.get_values(b)) if v] + for i, v in p.get_values(b).items() if v] def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): r""" @@ -1895,7 +1925,7 @@ def is_resolvable(self, certificate=False, solver=None, verbose=0, check=True): False """ if self._classes is None: - degrees = set(itervalues(self.degrees())) + degrees = set(self.degrees().values()) if len(degrees) != 1: self._classes = False else: @@ -1928,7 +1958,7 @@ def is_resolvable(self, certificate=False, solver=None, verbose=0, check=True): else: # each class is stored as the list of indices of its blocks self._classes = [[] for _ in range(n_classes)] - for (t, i), v in six.iteritems(p.get_values(b)): + for (t, i), v in p.get_values(b).items(): if v: self._classes[t].append(self._blocks[i]) @@ -2040,7 +2070,7 @@ def coloring(self, k=None, solver=None, verbose=0): col = [[] for i in range(k)] - for (x,i),v in six.iteritems(p.get_values(b)): + for (x,i),v in p.get_values(b).items(): if v: col[i].append(self._points[x]) diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 0926824cbf1..aced411b060 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -123,8 +123,6 @@ --------- """ from __future__ import print_function, absolute_import -from six import iteritems -from six.moves import zip from sage.rings.integer import Integer from sage.categories.sets_cat import EmptySetError @@ -449,7 +447,7 @@ def latin_square_product(M, N, *others): for jj in range(n)} L = lambda i_j: i_j[0] * n + i_j[1] - D = {(L(c[0]), L(c[1])): L(v) for c, v in iteritems(D)} + D = {(L(c[0]), L(c[1])): L(v) for c, v in D.items()} P = Matrix(D) if others: diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index ffb78a5e6f9..7e47fd5402a 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -45,7 +45,7 @@ REFERENCES: -- [CD1996]_ +-- [CD1996]_ Functions --------- @@ -53,10 +53,6 @@ """ from __future__ import print_function, absolute_import -from builtins import zip -from six import itervalues, iteritems -from six.moves import range - from sage.categories.sets_cat import EmptySetError from sage.misc.unknown import Unknown from .designs_pyx import is_orthogonal_array @@ -917,7 +913,7 @@ def orthogonal_array(k,n,t=2,resolvable=False, check=True,existence=False,explai return [[i,j,(i+j)%n] for i in range(n) for j in range(n)] # projective spaces are equivalent to OA(n+1,n,2) - elif (projective_plane(n, existence=True) or + elif (projective_plane(n, existence=True) is True or (k == n+1 and projective_plane(n, existence=True) is False)): _OA_cache_set(n+1,n,projective_plane(n, existence=True)) if k == n+1: @@ -983,7 +979,7 @@ def orthogonal_array(k,n,t=2,resolvable=False, check=True,existence=False,explai break # From Difference Matrices - elif may_be_available and difference_matrix(n,k-1,existence=True): + elif may_be_available and difference_matrix(n,k-1,existence=True) is True: _OA_cache_set(k,n,True) if existence: return True @@ -1050,7 +1046,7 @@ def largest_available_k(n,t=2): from sage.rings.infinity import Infinity return Infinity elif t == 2: - if projective_plane(n,existence=True): + if projective_plane(n,existence=True) is True: return n+1 else: k=1 @@ -1337,15 +1333,15 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): "intersect in a projective plane.").format(number_of_holes)) # Holes of size 1 from OA(k+1,n) - elif max_hole==1 and orthogonal_array(k+1,n,existence=True): + elif max_hole==1 and orthogonal_array(k+1,n,existence=True) is True: if existence: return True OA = orthogonal_array(k+1,n) independent_set = [B[:-1] for B in OA if B[-1] == 0][:number_of_holes] OA = [B[:-1] for B in OA] - elif max_hole==1 and orthogonal_array(k,n,existence=True): - OA = orthogonal_array(k, n) + elif max_hole==1 and orthogonal_array(k,n,existence=True) is True: + OA = orthogonal_array(k,n) try: independent_set = OA_find_disjoint_blocks(OA,k,n,number_of_holes) except ValueError: @@ -1356,14 +1352,14 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): return True independent_set = OA_find_disjoint_blocks(OA,k,n,number_of_holes) - elif max_hole==1 and not orthogonal_array(k,n,existence=True): + elif max_hole==1 and not orthogonal_array(k,n,existence=True) is True: return orthogonal_array(k,n,existence=existence) # From a quasi-difference matrix elif (number_of_holes == 1 and any(uu == sum_of_holes and mu <= 1 and lmbda == 1 and k <= kk + 1 - for (nn,lmbda,mu,uu),(kk,_) in iteritems(QDM.get((n,1),{})))): - for (nn,lmbda,mu,uu),(kk,f) in iteritems(QDM[n,1]): + for (nn,lmbda,mu,uu),(kk,_) in QDM.get((n,1),{}).items())): + for (nn,lmbda,mu,uu),(kk,f) in QDM[n,1].items(): if uu == sum_of_holes and mu <= 1 and lmbda == 1 and k <= kk + 1: break G,M = f() @@ -1683,7 +1679,7 @@ def OA_n_times_2_pow_c_from_matrix(k,c,G,A,Y,check=True): Hij = set([(Y[i] - Y[j]) * v for v in H]) for s in range(2 * G_card): g_to_col_indices[B[i][s] - B[j][s]].append(s) - for s1, s2 in itervalues(g_to_col_indices): + for s1, s2 in g_to_col_indices.values(): v1 = A[i][s1][1] - A[j][s1][1] v2 = A[i][s2][1] - A[j][s2][1] @@ -1781,8 +1777,8 @@ def OA_from_quasi_difference_matrix(M,G,add_col=True,fill_hole=True): # A cache for addition in G G_sum = [[0] * Gn for _ in range(Gn)] - for x, i in iteritems(G_to_int): - for xx, ii in iteritems(G_to_int): + for x, i in G_to_int.items(): + for xx, ii in G_to_int.items(): G_sum[i][ii] = G_to_int[x + xx] # Convert M to integers diff --git a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py index b1a6aa41310..f1e19383806 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py @@ -31,8 +31,6 @@ --------- """ from __future__ import print_function, absolute_import -from six.moves import range -from builtins import zip from .orthogonal_arrays import orthogonal_array, wilson_construction, is_orthogonal_array diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py index 1f6b07de1f2..e942ec51631 100644 --- a/src/sage/combinat/designs/resolvable_bibd.py +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -48,7 +48,6 @@ --------- """ from __future__ import print_function, absolute_import, division -from six.moves import range from sage.arith.all import is_prime_power from sage.combinat.designs.bibd import BalancedIncompleteBlockDesign @@ -92,7 +91,7 @@ def resolvable_balanced_incomplete_block_design(v,k,existence=False): sage: for v in range(40): ....: for k in range(v): - ....: if designs.resolvable_balanced_incomplete_block_design(v,k,existence=True): + ....: if designs.resolvable_balanced_incomplete_block_design(v,k,existence=True) is True: ....: _ = designs.resolvable_balanced_incomplete_block_design(v,k) """ # Trivial cases @@ -448,7 +447,7 @@ def PBD_4_7(v,check=True, existence=False): sage: for i in range(1,300,3): ....: if i not in [10,19,31]: - ....: assert PBD_4_7(i,existence=True) + ....: assert PBD_4_7(i,existence=True) is True ....: _ = PBD_4_7(i,check=True) """ if v%3 != 1 or v in [10,19,31]: @@ -623,7 +622,7 @@ def PBD_4_7(v,check=True, existence=False): return PBD_4_7_from_Y(GDD,check=check) - elif v % 6 == 1 and GDD_4_2((v - 1) // 6, existence=True): + elif v % 6 == 1 and GDD_4_2((v - 1) // 6, existence=True) is True: # VII.5.17 from [BJL99] gdd = GDD_4_2((v - 1) // 6) return PBD_4_7_from_Y(gdd, check=check) @@ -634,9 +633,9 @@ def PBD_4_7(v,check=True, existence=False): PBD = PBD_4_7_from_Y(PBD,check=False) return PBD_4_7_from_Y(PBD,check=check) - elif balanced_incomplete_block_design(v,4,existence=True): + elif balanced_incomplete_block_design(v,4,existence=True) is True: return balanced_incomplete_block_design(v,4) - elif balanced_incomplete_block_design(v,7,existence=True): + elif balanced_incomplete_block_design(v,7,existence=True) is True: return balanced_incomplete_block_design(v,7) else: from sage.combinat.designs.orthogonal_arrays import orthogonal_array @@ -651,9 +650,9 @@ def PBD_4_7(v,check=True, existence=False): vv = (v - 1) // 3 for g in range((vv + 5 - 1) // 5, vv // 4 + 1): u = vv-4*g - if (orthogonal_array(5,g,existence=True) and - PBD_4_7(3*g+1,existence=True) and - PBD_4_7(3*u+1,existence=True)): + if (orthogonal_array(5,g,existence=True) is True and + PBD_4_7(3*g+1,existence=True) is True and + PBD_4_7(3*u+1,existence=True) is True): from .orthogonal_arrays import transversal_design domain = set(range(vv)) GDD = transversal_design(5,g) @@ -723,7 +722,7 @@ def PBD_4_7_from_Y(gdd,check=True): "but there are other: {}".format(txt)) for gs in group_sizes: - if not PBD_4_7(3*gs+1,existence=True): + if not PBD_4_7(3*gs+1,existence=True) is True: raise RuntimeError("A group has size {} but I do not know how to " "build a ({},[4,7])-PBD".format(gs,3*gs+1)) diff --git a/src/sage/combinat/designs/steiner_quadruple_systems.py b/src/sage/combinat/designs/steiner_quadruple_systems.py index 4a7092da2d6..a08266faca7 100644 --- a/src/sage/combinat/designs/steiner_quadruple_systems.py +++ b/src/sage/combinat/designs/steiner_quadruple_systems.py @@ -60,7 +60,6 @@ --------- """ from __future__ import print_function -from six.moves import range from sage.misc.cachefunc import cached_function from sage.combinat.designs.incidence_structures import IncidenceStructure diff --git a/src/sage/combinat/designs/twographs.py b/src/sage/combinat/designs/twographs.py index 2de7c09fb80..ecf0460fc89 100644 --- a/src/sage/combinat/designs/twographs.py +++ b/src/sage/combinat/designs/twographs.py @@ -247,7 +247,6 @@ def has_triple(x_y_z): return bool(v_to_blocks[x] & v_to_blocks[y] & v_to_blocks[z]) # Check that every quadruple contains an even number of triples - from six.moves.builtins import sum for quad in combinations(range(T.num_points()),4): if sum(map(has_triple,combinations(quad,3))) % 2 == 1: return False diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 5037c6062fa..0d6f1a4b17f 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Diagram and Partition Algebras @@ -23,7 +24,6 @@ # *************************************************************************** # python3 from __future__ import division -from six.moves import range from sage.categories.associative_algebras import AssociativeAlgebras from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -44,7 +44,7 @@ from sage.misc.flatten import flatten from sage.misc.misc_c import prod from sage.rings.all import ZZ, QQ -from sage.functions.other import ceil +from sage.functions.other import floor, ceil import itertools @@ -246,7 +246,7 @@ class AbstractPartitionDiagram(AbstractSetPartition): ... ValueError: {{1, 2}, {3, 4}} does not represent two rows of vertices of order 2 """ - def __init__(self, parent, d): + def __init__(self, parent, d, check=True): r""" Initialize ``self``. @@ -257,7 +257,7 @@ def __init__(self, parent, d): sage: pd1 = da.AbstractPartitionDiagram(pd, ((-2,-1),(1,2)) ) """ self._base_diagram = tuple(sorted(tuple(sorted(i)) for i in d)) - super(AbstractPartitionDiagram, self).__init__(parent, self._base_diagram) + super(AbstractPartitionDiagram, self).__init__(parent, self._base_diagram, check=check) def check(self): r""" @@ -402,7 +402,7 @@ def set_partition(self): """ return SetPartitions()(self) - def compose(self, other): + def compose(self, other, check=True): r""" Compose ``self`` with ``other``. @@ -427,7 +427,7 @@ def compose(self, other): ({{-2, -1}, {1, 2}}, 1) """ (composite_diagram, loops_removed) = set_partition_composition(self._base_diagram, other._base_diagram) - return (self.__class__(self.parent(), composite_diagram), loops_removed) + return (self.__class__(self.parent(), composite_diagram, check=check), loops_removed) def propagating_number(self): r""" @@ -506,6 +506,19 @@ def is_planar(self): """ return is_planar(self) + def dual(self): + """ + Return the dual diagram of ``self`` by flipping it top-to-bottom. + + EXAMPLES:: + + sage: from sage.combinat.diagram_algebras import PartitionDiagram + sage: D = PartitionDiagram([[1,-1],[2,-2,-3],[3]]) + sage: D.dual() + {{-3}, {-2, 2, 3}, {-1, 1}} + """ + return self.parent([[-i for i in part] for part in self]) + class IdealDiagram(AbstractPartitionDiagram): r""" The element class for a ideal diagram. @@ -1216,7 +1229,7 @@ def __iter__(self): # treat it like an attribute, so we call the underlying # __func__. for i in self._diagram_func.__func__(self.order): - yield self.element_class(self, i) + yield self.element_class(self, i, check=False) def __contains__(self, obj): r""" @@ -1998,8 +2011,8 @@ def _latex_term(self, diagram): \node[vertex] (G--1) at (0.0, -1) [shape = circle, draw] {}; \node[vertex] (G-1) at (0.0, 1) [shape = circle, draw] {}; \node[vertex] (G-2) at (1.5, 1) [shape = circle, draw] {}; - \draw (G--2) .. controls +(-0.5, 0.5) and +(0.5, 0.5) .. (G--1); - \draw (G-1) .. controls +(0.5, -0.5) and +(-0.5, -0.5) .. (G-2); + \draw[] (G--2) .. controls +(-0.5, 0.5) and +(0.5, 0.5) .. (G--1); + \draw[] (G-1) .. controls +(0.5, -0.5) and +(-0.5, -0.5) .. (G-2); \end{tikzpicture} sage: latex(P.orbit_basis()([[1,2],[-2,-1]])) # indirect doctest @@ -2009,73 +2022,11 @@ def _latex_term(self, diagram): \node[vertex] (G--1) at (0.0, -1) [shape = circle, draw, fill] {}; \node[vertex] (G-1) at (0.0, 1) [shape = circle, draw, fill] {}; \node[vertex] (G-2) at (1.5, 1) [shape = circle, draw, fill] {}; - \draw (G--2) .. controls +(-0.5, 0.5) and +(0.5, 0.5) .. (G--1); - \draw (G-1) .. controls +(0.5, -0.5) and +(-0.5, -0.5) .. (G-2); + \draw[] (G--2) .. controls +(-0.5, 0.5) and +(0.5, 0.5) .. (G--1); + \draw[] (G-1) .. controls +(0.5, -0.5) and +(-0.5, -0.5) .. (G-2); \end{tikzpicture} - """ - # these allow the view command to work (maybe move them - # somewhere more appropriate?) - from sage.misc.latex import latex - latex.add_to_mathjax_avoid_list('tikzpicture') - latex.add_package_to_preamble_if_available('tikz') - if hasattr(self, '_fill'): - filled_str = ", fill" - else: - filled_str = "" - - def sgn(x): - # Define the sign function - if x > 0: - return 1 - if x < 0: - return -1 - return 0 - l1 = [] # list of blocks - l2 = [] # list of nodes - for i in list(diagram): - l1.append(list(i)) - for j in list(i): - l2.append(j) - output = "\\begin{tikzpicture}[scale = 0.5,thick, baseline={(0,-1ex/2)}] \n\\tikzstyle{vertex} = [shape = circle, minimum size = 7pt, inner sep = 1pt] \n" #setup beginning of picture - for i in l2: #add nodes - output = output + "\\node[vertex] (G-{}) at ({}, {}) [shape = circle, draw{}] {{}}; \n".format(i, (abs(i)-1)*1.5, sgn(i), filled_str) - for i in l1: #add edges - if len(i) > 1: - l4 = list(i) - posList = [] - negList = [] - for j in l4: # sort list so rows are grouped together - if j > 0: - posList.append(j) - elif j < 0: - negList.append(j) - posList.sort() - negList.sort() - l4 = posList + negList - l5 = l4[:] #deep copy - for j in range(len(l5)): - l5[j-1] = l4[j] #create a permuted list - if len(l4) == 2: - l4.pop() - l5.pop() #pops to prevent duplicating edges - for j in zip(l4, l5): - xdiff = abs(j[1])-abs(j[0]) - y1 = sgn(j[0]) - y2 = sgn(j[1]) - if y2-y1 == 0 and abs(xdiff) < 5: #if nodes are close to each other on same row - diffCo = (0.5+0.1*(abs(xdiff)-1)) #gets bigger as nodes are farther apart; max value of 1; min value of 0.5. - outVec = (sgn(xdiff)*diffCo, -1*diffCo*y1) - inVec = (-1*diffCo*sgn(xdiff), -1*diffCo*y2) - elif y2-y1 != 0 and abs(xdiff) == 1: #if nodes are close enough curviness looks bad. - outVec = (sgn(xdiff)*0.75, -1*y1) - inVec = (-1*sgn(xdiff)*0.75, -1*y2) - else: - outVec = (sgn(xdiff)*1, -1*y1) - inVec = (-1*sgn(xdiff), -1*y2) - output = output + "\\draw (G-{}) .. controls +{} and +{} .. (G-{}); \n".format(j[0], outVec, inVec, j[1]) - output = output + "\\end{tikzpicture} \n" #end picture - return output + return diagram_latex(diagram, fill=hasattr(self, '_fill')) # The following subclass provides a few additional methods for # (sub)partition algebra elements. @@ -2165,7 +2116,7 @@ def product_on_basis(self, d1, d2): d1 = self._indices(d1) if not self._indices.is_parent_of(d2): d2 = self._indices(d2) - (composite_diagram, loops_removed) = d1.compose(d2) + (composite_diagram, loops_removed) = d1.compose(d2, check=False) return self.term(composite_diagram, self._q**loops_removed) class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): @@ -2209,13 +2160,30 @@ class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): in which the product of two set partitions is simply given by their composition. - The Iwahori--Hecke algebra of type `A` (with a single parameter) is - naturally a subalgebra of the partition algebra. - The partition algebra is regarded as an example of a "diagram algebra" due to the fact that its natural basis is given by certain graphs often called diagrams. + There are a number of predefined elements for the partition algebra. + We define the cup/cap pair by :meth:`a()`. The simple transpositions + are denoted :meth:`s()`. Finally, we define elements :meth:`e()`, + where if `i = (2r+1)/2`, then ``e(i)`` contains the blocks `\{r+1\}` + and `\{-r-1\}` and if `i \in \ZZ`, then `e_i` contains the block + `\{-i, -i-1, i, i+1\}`, with all other blocks being `\{-j, j\}`. + So we have:: + + sage: P = PartitionAlgebra(4, 0) + sage: P.a(2) + P{{-4, 4}, {-3, -2}, {-1, 1}, {2, 3}} + sage: P.e(3/2) + P{{-4, 4}, {-3, 3}, {-2}, {-1, 1}, {2}} + sage: P.e(2) + P{{-4, 4}, {-3, -2, 2, 3}, {-1, 1}} + sage: P.e(5/2) + P{{-4, 4}, {-3}, {-2, 2}, {-1, 1}, {3}} + sage: P.s(2) + P{{-4, 4}, {-3, 2}, {-2, 3}, {-1, 1}} + An excellent reference for partition algebras and their various subalgebras (Brauer algebra, Temperley--Lieb algebra, etc) is the paper [HR2005]_. @@ -2451,11 +2419,6 @@ class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): P{{-3, 2}, {-2, 1}, {-1, 3}} sage: A([2,3,1]) == A(S([2,3,1])) True - - REFERENCES: - - .. [HR2005] Tom Halverson and Arun Ram, *Partition algebras*. European - Journal of Combinatorics **26** (2005), 869--921. """ @staticmethod def __classcall_private__(cls, k, q, base_ring=None, prefix="P"): @@ -2682,6 +2645,373 @@ def _orbit_to_diagram_on_basis(self, d): * self([sum((list(d[i-1]) for i in p),[]) for p in sp]) for sp in SPd) + @cached_method + def a(self, i): + r""" + Return the element `a_i` in ``self``. + + The element `a_i` is the cap and cup at `(i, i+1)`, so it contains + the blocks `\{i, i+1\}`, `\{-i, -i-1\}`. Other blocks are of the + form `\{-j, j\}`. + + INPUT: + + - ``i`` -- an integer between 1 and `k-1` + + EXAMPLES:: + + sage: R. = QQ[] + sage: P3 = PartitionAlgebra(3, n) + sage: P3.a(1) + P{{-3, 3}, {-2, -1}, {1, 2}} + sage: P3.a(2) + P{{-3, -2}, {-1, 1}, {2, 3}} + + sage: P3 = PartitionAlgebra(5/2, n) + sage: P3.a(1) + P{{-3, 3}, {-2, -1}, {1, 2}} + sage: P3.a(2) + Traceback (most recent call last): + ... + ValueError: i must be an integer between 1 and 1 + """ + if i <= 0 or i >= floor(self._k): + raise ValueError("i must be an integer between 1 and {}".format(floor(self._k)-1)) + B = self.basis() + SP = B.keys() + D = [[-j, j] for j in range(1, ceil(self._k)+1)] + D[i-1] = [i,i+1] + D[i] = [-i,-(i+1)] + return B[SP(D)] + + generator_a = a + + @cached_method + def e(self, i): + r""" + Return the element `e_i` in ``self``. + + If `i = (2r+1)/2`, then `e_i` contains the blocks `\{r+1\}` and + `\{-r-1\}`. If `i \in \ZZ`, then `e_i` contains the block + `\{-i, -i-1, i, i+1\}`. Other blocks are of the form `\{-j, j\}`. + + INPUT: + + - ``i`` -- a half integer between 1/2 and `k-1/2` + + EXAMPLES:: + + sage: R. = QQ[] + sage: P3 = PartitionAlgebra(3, n) + sage: P3.e(1) + P{{-3, 3}, {-2, -1, 1, 2}} + sage: P3.e(2) + P{{-3, -2, 2, 3}, {-1, 1}} + sage: P3.e(1/2) + P{{-3, 3}, {-2, 2}, {-1}, {1}} + sage: P3.e(5/2) + P{{-3}, {-2, 2}, {-1, 1}, {3}} + sage: P3.e(0) + Traceback (most recent call last): + ... + ValueError: i must be an (half) integer between 1/2 and 5/2 + sage: P3.e(3) + Traceback (most recent call last): + ... + ValueError: i must be an (half) integer between 1/2 and 5/2 + + sage: P2h = PartitionAlgebra(5/2,n) + sage: [P2h.e(k/2) for k in range(1,5)] + [P{{-3, 3}, {-2, 2}, {-1}, {1}}, + P{{-3, 3}, {-2, -1, 1, 2}}, + P{{-3, 3}, {-2}, {-1, 1}, {2}}, + P{{-3, -2, 2, 3}, {-1, 1}}] + """ + if i <= 0 or i >= self._k: + raise ValueError("i must be an (half) integer between 1/2 and {}".format((2*self._k-1)/2)) + B = self.basis() + SP = B.keys() + if i in ZZ: + i -= 1 + D = [[-j, j] for j in range(1, ceil(self._k)+1)] + D[i] += D.pop(i+1) + return B[SP(D)] + else: + i = ceil(i) + D = [[-j, j] for j in range(1, ceil(self._k)+1)] + D[i-1] = [-i] + D.append([i]) + return B[SP(D)] + + generator_e = e + + @cached_method + def s(self, i): + r""" + Return the ``i``-th simple transposition `s_i` in ``self``. + + Borrowing the notation from the symmetric group, the `i`-th + simple transposition `s_i` has blocks of the form `\{-i, i+1\}`, + `\{-i-1, i\}`. Other blocks are of the form `\{-j, j\}`. + + INPUT: + + - ``i`` -- an integer between 1 and `k-1` + + EXAMPLES:: + + sage: R. = QQ[] + sage: P3 = PartitionAlgebra(3, n) + sage: P3.s(1) + P{{-3, 3}, {-2, 1}, {-1, 2}} + sage: P3.s(2) + P{{-3, 2}, {-2, 3}, {-1, 1}} + + sage: R. = ZZ[] + sage: P2h = PartitionAlgebra(5/2,n) + sage: P2h.s(1) + P{{-3, 3}, {-2, 1}, {-1, 2}} + """ + if not i in ZZ or i <= 0 or i >= self._k: + raise ValueError("i must be an integer between 1 and {}".format(self._k-1)) + B = self.basis() + SP = B.keys() + D = [[-j, j] for j in range(1, ceil(self._k)+1)] + D[i-1] = [-(i+1), i] + D[i] = [-i, i+1] + return B[SP(D)] + + generator_s = s + + @cached_method + def sigma(self, i): + r""" + Return the element `\sigma_i` from [Eny2012]_ of ``self``. + + INPUT: + + - ``i`` -- a half integer between 1/2 and `k-1/2` + + .. NOTE:: + + In [Cre2020]_ and [Eny2013]_, these are the elements `\sigma_{2i}`. + + EXAMPLES:: + + sage: R. = QQ[] + sage: P3 = PartitionAlgebra(3, n) + sage: P3.sigma(1) + P{{-3, 3}, {-2, 2}, {-1, 1}} + sage: P3.sigma(3/2) + P{{-3, 3}, {-2, 1}, {-1, 2}} + sage: P3.sigma(2) + -P{{-3, -1, 1, 3}, {-2, 2}} + P{{-3, -1, 3}, {-2, 1, 2}} + + P{{-3, 1, 3}, {-2, -1, 2}} - P{{-3, 3}, {-2, -1, 1, 2}} + + P{{-3, 3}, {-2, 2}, {-1, 1}} + sage: P3.sigma(5/2) + -P{{-3, -1, 1, 2}, {-2, 3}} + P{{-3, -1, 2}, {-2, 1, 3}} + + P{{-3, 1, 2}, {-2, -1, 3}} - P{{-3, 2}, {-2, -1, 1, 3}} + + P{{-3, 2}, {-2, 3}, {-1, 1}} + + We test the relations in Lemma 2.2.3(1) in [Cre2020]_ (v1):: + + sage: k = 4 + sage: R. = QQ[] + sage: P = PartitionAlgebra(k, x) + sage: all(P.sigma(i/2).dual() == P.sigma(i/2) + ....: for i in range(1,2*k)) + True + sage: all(P.sigma(i)*P.sigma(i+1/2) == P.sigma(i+1/2)*P.sigma(i) == P.s(i) + ....: for i in range(1,floor(k))) + True + sage: all(P.sigma(i)*P.e(i) == P.e(i)*P.sigma(i) == P.e(i) + ....: for i in range(1,floor(k))) + True + sage: all(P.sigma(i+1/2)*P.e(i) == P.e(i)*P.sigma(i+1/2) == P.e(i) + ....: for i in range(1,floor(k))) + True + + sage: k = 9/2 + sage: R. = QQ[] + sage: P = PartitionAlgebra(k, x) + sage: all(P.sigma(i/2).dual() == P.sigma(i/2) + ....: for i in range(1,2*k-1)) + True + sage: all(P.sigma(i)*P.sigma(i+1/2) == P.sigma(i+1/2)*P.sigma(i) == P.s(i) + ....: for i in range(1,k-1/2)) + True + sage: all(P.sigma(i)*P.e(i) == P.e(i)*P.sigma(i) == P.e(i) + ....: for i in range(1,floor(k))) + True + sage: all(P.sigma(i+1/2)*P.e(i) == P.e(i)*P.sigma(i+1/2) == P.e(i) + ....: for i in range(1,floor(k))) + True + """ + if i <= 0 or i >= self._k: + raise ValueError("i must be an (half) integer between 1 and {}".format((2*self._k-1)/2)) + + half = QQ.one() / 2 + if i in ZZ: + if i == 1: + return self.one() + si = self.s(i) + sim = self.s(i-1) + x = self.e(i-1) * self.jucys_murphy_element(i-1) * si * self.e(i-1) + return (sim * si * self.sigma(i-1) * si * sim + + x * si + si * x + - self.e(i-1) * self.jucys_murphy_element(i-1) * sim + * self.e(i) * self.e(i-half) * self.e(i-1) + - si * self.e(i-1) * self.e(i-half) * self.e(i) * sim + * self.jucys_murphy_element(i-1) * self.e(i-1) * si) + else: + j = ceil(i) - 1 + if j == 0: + return self.zero() + if j == 1: + return self.s(1) + si = self.s(j) + sim = self.s(j-1) + x = self.e(j-1) * self.jucys_murphy_element(j-1) * si * self.e(j-1) + return (sim * si * self.sigma(i-1) * si * sim + + si * x * si + x + - si * self.e(j-1) * self.jucys_murphy_element(j-1) * sim + * self.e(j) * self.e(i-1) * self.e(j-1) + - self.e(j-1) * self.e(i-1) * self.e(j) * sim + * self.jucys_murphy_element(j-1) * self.e(j-1) * si) + + @cached_method + def jucys_murphy_element(self, i): + r""" + Return the ``i``-th Jucys-Murphy element `L_i` from [Eny2012]_. + + INPUT: + + - ``i`` -- a half integer between 1/2 and `k` + + ALGORITHM: + + We use the recursive definition for `L_{2i}` given in [Cre2020]_. + See also [Eny2012]_ and [Eny2013]_. + + .. NOTE:: + + `L_{1/2}` and `L_1` differs from [HR2005]_. + + EXAMPLES:: + + sage: R. = QQ[] + sage: P3 = PartitionAlgebra(3, n) + sage: P3.jucys_murphy_element(1/2) + 0 + sage: P3.jucys_murphy_element(1) + P{{-3, 3}, {-2, 2}, {-1}, {1}} + sage: P3.jucys_murphy_element(2) + P{{-3, 3}, {-2}, {-1, 1}, {2}} - P{{-3, 3}, {-2}, {-1, 1, 2}} + + P{{-3, 3}, {-2, -1}, {1, 2}} - P{{-3, 3}, {-2, -1, 1}, {2}} + + P{{-3, 3}, {-2, 1}, {-1, 2}} + sage: P3.jucys_murphy_element(3/2) + n*P{{-3, 3}, {-2, -1, 1, 2}} - P{{-3, 3}, {-2, -1, 2}, {1}} + - P{{-3, 3}, {-2, 1, 2}, {-1}} + P{{-3, 3}, {-2, 2}, {-1, 1}} + sage: P3.L(3/2) * P3.L(2) == P3.L(2) * P3.L(3/2) + True + + We test the relations in Lemma 2.2.3(2) in [Cre2020]_ (v1):: + + sage: k = 4 + sage: R. = QQ[] + sage: P = PartitionAlgebra(k, n) + sage: L = [P.L(i/2) for i in range(1,2*k+1)] + sage: all(x.dual() == x for x in L) + True + sage: all(x * y == y * x for x in L for y in L) # long time + True + sage: Lsum = sum(L) + sage: gens = [P.s(i) for i in range(1,k)] + sage: gens += [P.e(i/2) for i in range(1,2*k)] + sage: all(x * Lsum == Lsum * x for x in gens) + True + + Also the relations in Lemma 2.2.3(3) in [Cre2020]_ (v1):: + + sage: all(P.e((2*i+1)/2) * P.sigma(2*i/2) * P.e((2*i+1)/2) + ....: == (n - P.L((2*i-1)/2)) * P.e((2*i+1)/2) for i in range(1,k)) + True + sage: all(P.e(i/2) * (P.L(i/2) + P.L((i+1)/2)) + ....: == (P.L(i/2) + P.L((i+1)/2)) * P.e(i/2) + ....: == n * P.e(i/2) for i in range(1,2*k)) + True + sage: all(P.sigma(2*i/2) * P.e((2*i-1)/2) * P.e(2*i/2) + ....: == P.L(2*i/2) * P.e(2*i/2) for i in range(1,k)) + True + sage: all(P.e(2*i/2) * P.e((2*i-1)/2) * P.sigma(2*i/2) + ....: == P.e(2*i/2) * P.L(2*i/2) for i in range(1,k)) + True + sage: all(P.sigma((2*i+1)/2) * P.e((2*i+1)/2) * P.e(2*i/2) + ....: == P.L(2*i/2) * P.e(2*i/2) for i in range(1,k)) + True + sage: all(P.e(2*i/2) * P.e((2*i+1)/2) * P.sigma((2*i+1)/2) + ....: == P.e(2*i/2) * P.L(2*i/2) for i in range(1,k)) + True + + The same tests for a half integer partition algebra:: + + sage: k = 9/2 + sage: R. = QQ[] + sage: P = PartitionAlgebra(k, n) + sage: L = [P.L(i/2) for i in range(1,2*k+1)] + sage: all(x.dual() == x for x in L) + True + sage: all(x * y == y * x for x in L for y in L) # long time + True + sage: Lsum = sum(L) + sage: gens = [P.s(i) for i in range(1,k-1/2)] + sage: gens += [P.e(i/2) for i in range(1,2*k)] + sage: all(x * Lsum == Lsum * x for x in gens) + True + sage: all(P.e((2*i+1)/2) * P.sigma(2*i/2) * P.e((2*i+1)/2) + ....: == (n - P.L((2*i-1)/2)) * P.e((2*i+1)/2) for i in range(1,floor(k))) + True + sage: all(P.e(i/2) * (P.L(i/2) + P.L((i+1)/2)) + ....: == (P.L(i/2) + P.L((i+1)/2)) * P.e(i/2) + ....: == n * P.e(i/2) for i in range(1,2*k)) + True + sage: all(P.sigma(2*i/2) * P.e((2*i-1)/2) * P.e(2*i/2) + ....: == P.L(2*i/2) * P.e(2*i/2) for i in range(1,ceil(k))) + True + sage: all(P.e(2*i/2) * P.e((2*i-1)/2) * P.sigma(2*i/2) + ....: == P.e(2*i/2) * P.L(2*i/2) for i in range(1,ceil(k))) + True + sage: all(P.sigma((2*i+1)/2) * P.e((2*i+1)/2) * P.e(2*i/2) + ....: == P.L(2*i/2) * P.e(2*i/2) for i in range(1,floor(k))) + True + sage: all(P.e(2*i/2) * P.e((2*i+1)/2) * P.sigma((2*i+1)/2) + ....: == P.e(2*i/2) * P.L(2*i/2) for i in range(1,floor(k))) + True + """ + if i <= 0 or i > self._k: + raise ValueError("i must be an (half) integer between 1/2 and {}".format(self._k)) + + half = QQ.one() / 2 + if i in ZZ: + if i == 1: + return self.e(half) + i -= 1 + L = self.jucys_murphy_element + return ((self.s(i) * L(i)) * (self.s(i) - self.e(i)) + - (self.e(i) * L(i)) * (self.s(i) - self.e(i+half)*self.e(i)) + + self.sigma(i+half)) + else: + j = ceil(i) - 1 + if j == 0: + return self.zero() + L = self.jucys_murphy_element + return (self.s(j) * L(i-1) * self.s(j) + - self.e(j)*L(j) + + (self._q*self.one() - L(i-1) - L(j))*self.e(j) + + self.sigma(j)) + + L = jucys_murphy_element + class Element(DiagramBasis.Element): def to_orbit_basis(self): """ @@ -2704,6 +3034,26 @@ def to_orbit_basis(self): OP = self.parent().orbit_basis() return OP(self) + def dual(self): + r""" + Return the dual of ``self``. + + The dual of an element in the partition algebra is formed + by taking the dual of each diagram in the support. + + EXAMPLES:: + + sage: R. = QQ[] + sage: P = PartitionAlgebra(2, x, R) + sage: elt = P.an_element(); elt + 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} + sage: elt.dual() + 3*P{{-2, -1, 1}, {2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, -1, 2}, {1}} + """ + P = self.parent() + return P._from_dict({D.dual(): c for D, c in self._monomial_coefficients.items()}, + remove_zeros=False) + class OrbitBasis(DiagramAlgebra): r""" The orbit basis of the partition algebra. @@ -3088,7 +3438,7 @@ def matchings(A, B): for sigma in Permutations(Y): yield [x.union(y) for x, y in zip(X, sigma)] + restA + restB - D, removed = d1.compose(d2) + D, removed = d1.compose(d2, check=False) only_top = set([frozenset(part) for part in d1 if all(i > 0 for i in part)]) only_bottom = set([frozenset(part) for part in d2 @@ -3544,6 +3894,41 @@ def _element_constructor_(self, set_partition): set_partition = to_Brauer_partition(set_partition, k=self.order()) return SubPartitionAlgebra._element_constructor_(self, set_partition) + def _ascii_art_term(self, diagram): + r""" + Return an ascii art representation of ``diagram``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: TL = TemperleyLiebAlgebra(4, q, R) + sage: x = TL.an_element() + sage: ascii_art(x) # indirect doctest + o o o o o o o o + o o o o | `-` | | `-` | + 2* `-` `-` + 2* `-----` + 3* `---. | + .-. .-. .-. .-. .-. | | + o o o o o o o o o o o o + """ + return TL_diagram_ascii_art(diagram, use_unicode=False) + + def _unicode_art_term(self, diagram): + r""" + Return a unicode art representation of ``diagram``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: TL = TemperleyLiebAlgebra(4, q, R) + sage: x = TL.an_element() + sage: unicode_art(x) # indirect doctest + ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ + ⚬ ⚬ ⚬ ⚬ │ ╰─╯ │ │ ╰─╯ │ + 2* ╰─╯ ╰─╯ + 2* ╰─────╯ + 3* ╰───╮ │ + ╭─╮ ╭─╮ ╭─╮ ╭─╮ ╭─╮ │ │ + ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ + """ + return TL_diagram_ascii_art(diagram, use_unicode=True) class PlanarAlgebra(SubPartitionAlgebra, UnitDiagramMixin): r""" @@ -3767,6 +4152,290 @@ def __pow__(self, n): raise ValueError("can only take positive integer powers") return generic_power(self, n) +def TL_diagram_ascii_art(diagram, use_unicode=False, blobs=[]): + r""" + Return ascii art for a Temperley-Lieb diagram ``diagram``. + + INPUT: + + - ``diagram`` -- a list of pairs of matchings of the set + `\{-1, \ldots, -n, 1, \ldots, n\}` + - ``use_unicode`` -- (default: ``False``): whether or not + to use unicode art instead of ascii art + - ``blobs`` -- (optional) a list of matchings with blobs on them + + EXAMPLES:: + + sage: from sage.combinat.diagram_algebras import TL_diagram_ascii_art + sage: TL = [(-15,-12), (-14,-13), (-11,15), (-10,14), (-9,-6), + ....: (-8,-7), (-5,-4), (-3,1), (-2,-1), (2,3), (4,5), + ....: (6,11), (7, 8), (9,10), (12,13)] + sage: TL_diagram_ascii_art(TL, use_unicode=False) + o o o o o o o o o o o o o o o + | `-` `-` | `-` `-` | `-` | | + | `---------` | | + | .-------` | + `---. | .-------` + | .-----. | | .-----. + .-. | .-. | .-. | | | | .-. | + o o o o o o o o o o o o o o o + sage: TL_diagram_ascii_art(TL, use_unicode=True) + ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ + │ ╰─╯ ╰─╯ │ ╰─╯ ╰─╯ │ ╰─╯ │ │ + │ ╰─────────╯ │ │ + │ ╭───────╯ │ + ╰───╮ │ ╭───────╯ + │ ╭─────╮ │ │ ╭─────╮ + ╭─╮ │ ╭─╮ │ ╭─╮ │ │ │ │ ╭─╮ │ + ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ + + sage: TL = [(-20,-9), (-19,-10), (-18,-11), (-17,-16), (-15,-12), (2,3), + ....: (-14,-13), (-8,16), (-7,7), (-6,6), (-5,1), (-4,-3), (-2,-1), + ....: (4,5), (8,15), (9,10), (11,14), (12,13), (17,20), (18,19)] + sage: TL_diagram_ascii_art(TL, use_unicode=False, blobs=[(-2,-1), (-5,1)]) + o o o o o o o o o o o o o o o o o o o o + | `-` `-` | | | `-` | `-` | | | | `-` | + | | | | `-----` | | `-----` + | | | `-------------` | + `---0---. | | .---------------` + | | | | .---------------------. + | | | | | .-----------------. | + | | | | | | .-------------. | | + | | | | | | | .-----. | | | + .0. .-. | | | | | | | | .-. | .-. | | | + o o o o o o o o o o o o o o o o o o o o + sage: TL_diagram_ascii_art(TL, use_unicode=True, blobs=[(-2,-1), (-5,1)]) + ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ + │ ╰─╯ ╰─╯ │ │ │ ╰─╯ │ ╰─╯ │ │ │ │ ╰─╯ │ + │ │ │ │ ╰─────╯ │ │ ╰─────╯ + │ │ │ ╰─────────────╯ │ + ╰───●───╮ │ │ ╭───────────────╯ + │ │ │ │ ╭─────────────────────╮ + │ │ │ │ │ ╭─────────────────╮ │ + │ │ │ │ │ │ ╭─────────────╮ │ │ + │ │ │ │ │ │ │ ╭─────╮ │ │ │ + ╭●╮ ╭─╮ │ │ │ │ │ │ │ │ ╭─╮ │ ╭─╮ │ │ │ + ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ ⚬ + """ + def insert_pairing(cur, intervals): + """ + Helper function to insert a possibly nested interval + and push the others up, assuming inserting points from + right-to-left. + """ + for level in intervals: + for j, I in enumerate(level): + # Singleton intervals are vertical lines, + # so we don't need to worry about them + if len(I) > 1 and I[0] < cur[0]: + cur, level[j] = level[j], cur + level.append([cur[0]]) + level.append([cur[1]]) + break + else: + level.append(cur) + return # We have stopped + else: + intervals.append([cur]) + # Build a set of intervals that defines where to draw the diagram + intervals = [[]] + propogating = [] + vertical = [] + top_intervals = [[]] + num_left = 0 + num_right = 0 + def key_func(P): + if P[1] < 0: # cap + return (0, P[0], P[1]) + elif P[0] > 0: # cup + return (3, -P[1], -P[0]) + else: + bot, top = -P[0], P[1] + if top < bot: # left moving + return (1, top, bot) + elif top > bot: # right moving + return (2, -bot, -top) + else: # vertical line + return (1, top, bot) + diagram = sorted(diagram, key=key_func) + # Since diagram is sorted in lex order, we will first do the matchings + # from right-to-left on the bottom, then the propogating lines, and + # then the matchings on the top from right-to-left. + # Note that we need the top to go from right-to-left so the + # insert_pairing() function's assumptions are satisfied. + for P in diagram: + if P[1] < 0: # Bottom matching + insert_pairing([-P[1], -P[0], False, False], intervals) + elif P[0] > 0: # Top matching + insert_pairing([P[0], P[1], True, True], top_intervals) + else: # Propogating line + if -P[0] == P[1]: + vertical.append(P[1]) + else: + if -P[0] < P[1]: + num_right += 1 + else: + num_left += 1 + propogating.append(P) + + # Now piece together the intervals together + total_prop = max(num_left, num_right) + prop_intervals = [[] for _ in range(total_prop)] + count_left = 0 + # Recall that the left-moving propogating lines come before the right-moving + for i, P in enumerate(propogating): + bot, top = P + bot = -bot # This makes it equal to its x-coordinate + for level in intervals: + level.append([bot]) + for level in top_intervals: + level.append([top]) + left_moving = count_left < num_left + if not left_moving: + i -= num_left + else: + count_left += 1 + for j in range(i): + prop_intervals[j].append([bot]) + for j in range(i+1,total_prop): + prop_intervals[j].append([top]) + if not left_moving: + top, bot = bot, top + prop_intervals[i].append([top, bot, left_moving, not left_moving]) + intervals += prop_intervals + intervals += reversed(top_intervals) + for level in intervals: + level.extend([i] for i in vertical) + + n = max(max(P) for P in diagram) + + # Finally, convert to a picture + if use_unicode: + from sage.typeset.unicode_art import UnicodeArt + d = ["╭", "╮", "╰", "╯", "─", "│"] + #db = ["┏", "┓", "┗", "┛", "━", "┃"] + blob = '●' + ret = [" ⚬" * n] + char_art = UnicodeArt + else: + from sage.typeset.ascii_art import AsciiArt + d = [".", ".", "`", "`", "-", "|"] + #db = [".", ".", "`", "`", "=", "|"] + blob = '0' + ret = [" o" * n] + char_art = AsciiArt + def signed(val, pos): + return val if pos else -val + for level in reversed(intervals): + cur = "" + for I in sorted(level): + cur += ' '*(2*I[0]-1 - len(cur)) + if len(I) == 1: + cur += d[5] + ' ' + else: + cur += d[2] if I[2] else d[0] + if tuple(sorted([signed(I[0], I[2]), signed(I[1], I[3])])) in blobs: + cur += d[4] * (I[1]-I[0]-1) + cur += blob + cur += d[4] * (I[1]-I[0]-1) + else: + cur += d[4] * (2*(I[1]-I[0])-1) + cur += d[3] if I[3] else d[1] + ret.append(cur) + # Note that the top row and bottom row will be the same + ret.append(ret[0]) + return char_art(ret, baseline=len(ret)//2) + +def diagram_latex(diagram, fill=False, edge_options=None, edge_additions=None): + r""" + Return latex code for the diagram ``diagram`` using tikz. + + EXAMPLES:: + + sage: from sage.combinat.diagram_algebras import PartitionDiagrams, diagram_latex + sage: P = PartitionDiagrams(2) + sage: D = P([[1,2],[-2,-1]]) + sage: print(diagram_latex(D)) # indirect doctest + \begin{tikzpicture}[scale = 0.5,thick, baseline={(0,-1ex/2)}] + \tikzstyle{vertex} = [shape = circle, minimum size = 7pt, inner sep = 1pt] + \node[vertex] (G--2) at (1.5, -1) [shape = circle, draw] {}; + \node[vertex] (G--1) at (0.0, -1) [shape = circle, draw] {}; + \node[vertex] (G-1) at (0.0, 1) [shape = circle, draw] {}; + \node[vertex] (G-2) at (1.5, 1) [shape = circle, draw] {}; + \draw[] (G--2) .. controls +(-0.5, 0.5) and +(0.5, 0.5) .. (G--1); + \draw[] (G-1) .. controls +(0.5, -0.5) and +(-0.5, -0.5) .. (G-2); + \end{tikzpicture} + """ + # these allow the view command to work (maybe move them + # somewhere more appropriate?) + from sage.misc.latex import latex + latex.add_to_mathjax_avoid_list('tikzpicture') + latex.add_package_to_preamble_if_available('tikz') + + if fill: + filled_str = ", fill" + else: + filled_str = "" + + if edge_options is None: + edge_options = lambda P: '' + if edge_additions is None: + edge_additions = lambda P: '' + + def sgn(x): + # Define the sign function + if x > 0: + return 1 + if x < 0: + return -1 + return 0 + l1 = [] # list of blocks + l2 = [] # list of nodes + for i in list(diagram): + l1.append(list(i)) + for j in list(i): + l2.append(j) + output = "\\begin{tikzpicture}[scale = 0.5,thick, baseline={(0,-1ex/2)}] \n\\tikzstyle{vertex} = [shape = circle, minimum size = 7pt, inner sep = 1pt] \n" #setup beginning of picture + for i in l2: #add nodes + output = output + "\\node[vertex] (G-{}) at ({}, {}) [shape = circle, draw{}] {{}}; \n".format(i, (abs(i)-1)*1.5, sgn(i), filled_str) + for i in l1: #add edges + if len(i) > 1: + l4 = list(i) + posList = [] + negList = [] + for j in l4: # sort list so rows are grouped together + if j > 0: + posList.append(j) + elif j < 0: + negList.append(j) + posList.sort() + negList.sort() + l4 = posList + negList + l5 = l4[:] #deep copy + for j in range(len(l5)): + l5[j-1] = l4[j] #create a permuted list + if len(l4) == 2: + l4.pop() + l5.pop() #pops to prevent duplicating edges + for j in zip(l4, l5): + xdiff = abs(j[1])-abs(j[0]) + y1 = sgn(j[0]) + y2 = sgn(j[1]) + if y2-y1 == 0 and abs(xdiff) < 5: #if nodes are close to each other on same row + diffCo = (0.5+0.1*(abs(xdiff)-1)) #gets bigger as nodes are farther apart; max value of 1; min value of 0.5. + outVec = (sgn(xdiff)*diffCo, -1*diffCo*y1) + inVec = (-1*diffCo*sgn(xdiff), -1*diffCo*y2) + elif y2-y1 != 0 and abs(xdiff) == 1: #if nodes are close enough curviness looks bad. + outVec = (sgn(xdiff)*0.75, -1*y1) + inVec = (-1*sgn(xdiff)*0.75, -1*y2) + else: + outVec = (sgn(xdiff)*1, -1*y1) + inVec = (-1*sgn(xdiff), -1*y2) + output = output + "\\draw[{}] (G-{}) .. controls +{} and +{} .. {}(G-{}); \n".format( + edge_options(j), j[0], outVec, inVec, edge_additions(j), j[1]) + output = output + "\\end{tikzpicture}" #end picture + return output + ######################################################################### # START BORROWED CODE ######################################################################### diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index 33ecc328190..438b68ef939 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -82,7 +82,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import -from six.moves import range from .combinat import CombinatorialElement, catalan_number from sage.combinat.combinatorial_map import combinatorial_map @@ -1581,7 +1580,7 @@ def to_standard_tableau(self): else: close_positions.append(i + 1) from sage.combinat.tableau import StandardTableau - return StandardTableau([x for x in [open_positions, close_positions] if x != []]) + return StandardTableau([x for x in [open_positions, close_positions] if x]) def to_tamari_sorting_tuple(self): """ diff --git a/src/sage/combinat/e_one_star.py b/src/sage/combinat/e_one_star.py index a32cc13ddac..04fbe15d7b3 100644 --- a/src/sage/combinat/e_one_star.py +++ b/src/sage/combinat/e_one_star.py @@ -180,7 +180,7 @@ sage: E E_1^*(1->12, 10->1,11, 11->1,12, 12->1, 2->13, 3->14, 4->15, 5->16, 6->17, 7->18, 8->19, 9->1,10) sage: P = Patch([Face((0,0,0,0,0,0,0,0,0,0,0,0),t) for t in [1,2,3]]) - sage: for x in sorted(list(E(P)), key=lambda x : (x.vector(),x.type())): print(x) + sage: for x in sorted(E(P), key=lambda x : (x.vector(),x.type())): print(x) [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 1]* [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 2]* [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 12]* @@ -208,7 +208,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from sage.misc.functional import det from sage.structure.sage_object import SageObject diff --git a/src/sage/combinat/fast_vector_partitions.pyx b/src/sage/combinat/fast_vector_partitions.pyx new file mode 100644 index 00000000000..ca855ac2362 --- /dev/null +++ b/src/sage/combinat/fast_vector_partitions.pyx @@ -0,0 +1,346 @@ +# -*- coding: utf-8 -*- +r""" +Brent Yorgey's fast algorithm for integer vector (multiset) partitions. + +ALGORITHM: + +Brent Yorgey, Generating Multiset Partitions, The Monad Reader, Issue 8, +September 2007, p. 5. + +https://wiki.haskell.org/The_Monad.Reader/Previous_issues + +AUTHORS: + +- D\. K\. Sunko (2020-02-19): initial version +- F\. Chapoton (2020-02-22): conversion to iterators and shorter doctests and + doc tweaks +- T\. Scrimshaw (2020-03-06): Cython optimizations and doc tweaks +""" +################################################################################ +# Copyright (C) 2020 Denis Sunko # +# Copyright (C) 2020 Frédéric Chapoton # +# Copyright (C) 2020 Travis Scrimshaw # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 2 of the License, or # +# (at your option) any later version. The text of the license is found at # +# https://www.gnu.org/licenses/ # +################################################################################ +# +# To understand the code below, consult the ALGORITHM. + +cdef list vector_halve(list v): + r""" + Return the vector halfway (lexicographically) between ``v`` and zero. + + Internal part of :func:`fast_vector_partitions`. + + INPUT: + + - ``v`` -- list of non-negative integers, understood as a vector + + OUTPUT: + + A list, understood as the integer vector halfway down the list of + lexicographically ordered vectors between between ``v`` and zero. + + EXAMPLES:: + + sage: from sage.combinat.fast_vector_partitions import vector_halve # not tested + sage: vector_halve([1, 2, 3, 4, 5, 6, 7, 8, 9]) # not tested + [0, 2, 3, 4, 5, 6, 7, 8, 9] + sage: vector_halve([2, 4, 6, 8, 5, 6, 7, 8, 9]) # not tested + [1, 2, 3, 4, 2, 6, 7, 8, 9] + + .. NOTE:: + + For vectors, ``v = a + b`` implies ``v = b + a``, which means that a + downward search for such splittings, starting with ``v = v + 0``, need + only look as far as some "v / 2", given precise meaning here. + + A similar logic is to stop the search for divisors of ``N`` at + ``sqrt(N)``, halving the exponents in the prime decomposition. + However, here "v / 2" does not mean halving each coordinate. + """ + cdef list result = list(v) # make a copy + cdef Py_ssize_t i, vv + for i in range(len(v)): + vv = v[i] + result[i] = vv // 2 + if vv % 2: + # the less significant part is just copied + return result + return result + + +def recursive_within_from_to(list m, list s, list e, bint useS, bint useE): + r""" + Iterate over a lexicographically ordered list of lists ``v`` satisfying + ``e <= v <= s`` and ``v <|= m`` as vectors. + + Internal part of :func:`fast_vector_partitions`. + + INPUT: + + - ``m`` -- list of non-negative integers, understood as a vector + - ``s`` -- list of non-negative integers, understood as a vector + - ``e`` -- list of non-negative integers, understood as a vector + - ``useS`` -- boolean + - ``useE`` -- boolean + + EXAMPLES:: + + sage: from sage.combinat.fast_vector_partitions import recursive_within_from_to + sage: list(recursive_within_from_to([1, 2, 3],[1, 2, 2],[1, 1, 1],True,True)) + [[1, 2, 2], [1, 2, 1], [1, 2, 0], [1, 1, 3], [1, 1, 2], [1, 1, 1]] + + .. NOTE:: + + The flags ``useS`` and ``useE`` are used to implement the condition + efficiently. Because testing it loops over the vector, re-testing + at each step as the vector is parsed is inefficient: all but the last + comparison have been done cumulatively already. This code tests + only for the last one, using the flags to accumulate information + from previous calls. + + .. WARNING:: + + Expects to be called with ``s <|= m``. + + Expects to be called first with ``useS == useE == True``. + """ + cdef Py_ssize_t start, end, x + cdef bint useSS, useEE + + if useS: + start = s[0] + else: + start = m[0] + + if useE: + end = e[0] + else: + end = 0 + + if len(m) == 1: + # We use this style of Cython code for now in order to get this to convert + # to an optimized pure C for loop. See Cython github issue #532. + #for x in range(start, end - 1, -1): + for x from start >= x >= end by 1: + yield [x] # we know the answer for singletons + else: + # We use this style of Cython code for now in order to get this to convert + # to an optimized pure C for loop. See Cython github issue #532. + #for x in range(start, end - 1, -1): + for x from start >= x >= end by 1: + useSS = useS and x == s[0] + useEE = useE and x == e[0] + for o in recursive_within_from_to(m[1:], s[1:], e[1:], useSS, useEE): + yield [x] + o + +def within_from_to(list m, list s, list e): + r""" + Iterate over a lexicographically ordered list of lists ``v`` satisfying + ``e <= v <= s`` and ``v <|= m`` as vectors. + + Internal part of :func:`fast_vector_partitions`. + + INPUT: + + - ``m`` -- list of non-negative integers, understood as a vector + - ``s`` -- list of non-negative integers, understood as a vector + - ``e`` -- list of non-negative integers, understood as a vector + + EXAMPLES:: + + sage: from sage.combinat.fast_vector_partitions import within_from_to + sage: list(within_from_to([1, 2, 3], [1, 2, 2], [1, 1, 1])) + [[1, 2, 2], [1, 2, 1], [1, 2, 0], [1, 1, 3], [1, 1, 2], [1, 1, 1]] + + .. NOTE:: + + The input ``s`` will be "clipped" internally if it does not satisfy + the condition ``s <|= m``. + + To understand the input check, some line art is helpful. Assume + that ``(a,b)`` are the two least significant coordinates of some + vector. Say:: + + e = (2,3), s = (7,6), m = (9,8). + + In the figure, these values are denoted by ``E``, ``S``, and ``M``, + while the letter ``X`` stands for all other allowed values of + ``v = (a,b)``:: + + b ^ + | + 8 --------X---X---X---X---X-----------M + | | + 7 - X X X X X | + | | + 6 - X X X X X S | + | | + 5 - X X X X X X | + | | + 4 - X X X X X X | + | | + 3 - E X X X X X | + | | + 2 - X X X X X | + | | + 1 - X X X X X | + | | + 0 ----|---|---X---X---X---X---X---|---|---> + 0 1 2 3 4 5 6 7 8 9 a + + If ``S`` moves horizontally, the full-height columns fill the box in + until ``S`` reaches ``M``, at which point it remains the limit in the + b-direction as it moves out of the box, while M takes over as the + limit in the a-direction, so the ``M``-column remains filled only up to + ``S``, no matter how much ``S`` moves further to the right. + + If ``S`` moves vertically, its column will be filled to the top of the + box, but it remains the relevant limit in the a-direction, while ``M`` + takes over in the b-direction as ``S`` goes out of the box upwards. + + Both behaviors are captured by using the smaller coordinate of ``S`` + and ``M``, whenever ``S`` is outside the box defined by M. The input + will be "clipped" accordingly in that case. + + .. WARNING:: + + The "clipping" behavior is transparent to the user, but may be + puzzling when comparing outputs with the function + :func:`recursive_within_from_to` which has no input protection. + """ + cdef list ss = s + # if s is not in the box defined by m, we must clip: + cdef Py_ssize_t i, j + for i in range(len(m)): # should have the same length as s + if s[i] > m[i]: + ss = list(ss) # make a copy + ss[i] = m[i] + for j in range(i+1, len(m)): + if ss[j] > m[j]: + ss[j] = m[j] + break + if e > ss: + return + yield from recursive_within_from_to(m, ss, e, True, True) + +cdef inline list vector_sub(list a, list b): + """ + Return ``a - b`` considered as vectors. + + This assumes ``len(b) >= len(a)``. + """ + cdef Py_ssize_t i + cdef list ret = [] + for i in range(len(a)): + ret.append(( a[i]) - ( b[i])) + return ret + +def recursive_vector_partitions(list v, list vL): + r""" + Iterate over a lexicographically ordered list of lists, each list + representing a vector partition of ``v``, such that no part of any + partition is lexicographically smaller than ``vL``. + + Internal part of :func:`fast_vector_partitions`. + + INPUT: + + - ``v`` -- list of non-negative integers, understood as a vector + - ``vL`` -- list of non-negative integers, understood as a vector + + EXAMPLES:: + + sage: from sage.combinat.fast_vector_partitions import recursive_vector_partitions + sage: list(recursive_vector_partitions([2, 2, 2],[1, 1, 1])) + [[[2, 2, 2]], [[1, 1, 1], [1, 1, 1]]] + sage: list(recursive_vector_partitions([2, 2, 2],[1, 1, 0])) + [[[2, 2, 2]], [[1, 1, 1], [1, 1, 1]], [[1, 1, 0], [1, 1, 2]]] + sage: list(recursive_vector_partitions([2, 2, 2],[1, 0, 1])) + [[[2, 2, 2]], + [[1, 1, 1], [1, 1, 1]], + [[1, 1, 0], [1, 1, 2]], + [[1, 0, 2], [1, 2, 0]], + [[1, 0, 1], [1, 2, 1]]] + """ + cdef list v_minus_vv, pp, vv + yield [v] + for vv in within_from_to(v, vector_halve(v), vL): + v_minus_vv = vector_sub(v, vv) + for pp in recursive_vector_partitions(v_minus_vv, vv): + yield [vv] + pp + + +def fast_vector_partitions(v, min_vals=None): + r""" + Brent Yorgey's fast algorithm for integer vector (multiset) partitions. + + INPUT: + + - ``v`` -- list of non-negative integers, understood as the vector + to be partitioned + + - ``min_vals`` -- optional list of non-negative integers, of same + length as ``v`` + + OUTPUT: + + A list of lists, each representing a vector partition of ``v``. + + If ``min_vals`` is given, only partitions with parts ``p >= min_vals`` in + the lexicographic ordering will appear. + + If ``min_vals`` is given and ``len(min_vals) != len(v)``, an error + is raised. + + EXAMPLES: + + The older the computer, the more impressive the comparison:: + + sage: from sage.combinat.fast_vector_partitions import fast_vector_partitions + sage: fastvparts = list(fast_vector_partitions([3, 3, 3])) + sage: vparts = list(VectorPartitions([3, 3, 3])) + sage: vparts == fastvparts[::-1] + True + sage: len(fastvparts) + 686 + sage: list(fast_vector_partitions([1, 2, 3], min_vals=[0, 1, 1])) + [[[1, 2, 3]], + [[0, 2, 3], [1, 0, 0]], + [[0, 2, 2], [1, 0, 1]], + [[0, 2, 1], [1, 0, 2]], + [[0, 2, 0], [1, 0, 3]], + [[0, 1, 3], [1, 1, 0]], + [[0, 1, 2], [1, 1, 1]], + [[0, 1, 1], [1, 1, 2]], + [[0, 1, 1], [0, 1, 2], [1, 0, 0]], + [[0, 1, 1], [0, 1, 1], [1, 0, 1]]] + sage: L1 = list(fast_vector_partitions([5, 7, 6], min_vals=[1, 3, 2])) + sage: L1 == list(VectorPartitions([5, 7, 6], min=[1, 3, 2]))[::-1] + True + + .. NOTE:: + + The partitions are returned as an iterator. + + In this documentation, ``a <|= b`` means ``a[i] <= b[i]`` for all ``i`` + (notation following B. Yorgey's paper). It is the monomial partial + ordering in Dickson's lemma: ``a <|= b`` iff ``x^a`` divides ``x^b`` as + monomials. + + .. WARNING:: + + The ordering of the partitions is reversed with respect to the output of + Sage class :class:`~sage.combinat.vector_partition.VectorPartitions`. + """ + if min_vals is None: + min_vals = (len(v) - 1) * [0] + [1] # lexicographically smallest vector > 0 + if len(v) != len(min_vals): + raise ValueError("the length of v and min_vals must be equal") + return recursive_vector_partitions(list(v), list(min_vals)) + diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 796571fec00..8c03a0a693c 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -931,10 +931,6 @@ # **************************************************************************** from __future__ import print_function -import six -from six.moves import range, zip_longest, zip -from six import itervalues - from IPython.lib.pretty import pretty import collections import itertools @@ -946,9 +942,10 @@ from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_function from sage.misc.latex import latex -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.misc.sageinspect import sage_getargspec from sage.rings.qqbar import QQbar +from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR from sage.structure.sage_object import SageObject @@ -1065,13 +1062,13 @@ def equal(iterator): return True -def startswith(list, prefix): +def startswith(list_, prefix): """ Determine whether list starts with the given prefix. INPUT: - - ``list`` -- list + - ``list_`` -- list - ``prefix`` -- list representing the prefix OUTPUT: @@ -1090,10 +1087,11 @@ def startswith(list, prefix): sage: startswith([1, 3, 2], [1, 2]) False """ + if len(prefix) > len(list_): + return False + return list_[:len(prefix)] == prefix - return list[:len(prefix)] == prefix - -#***************************************************************************** +# **************************************************************************** FSMEmptyWordSymbol = '-' @@ -1107,7 +1105,7 @@ def startswith(list, prefix): def FSMLetterSymbol(letter): """ - Returns a string associated to the input letter. + Return a string associated to the input letter. INPUT: @@ -1133,7 +1131,7 @@ def FSMLetterSymbol(letter): def FSMWordSymbol(word): """ - Returns a string of ``word``. It may returns the symbol of the + Return a string of ``word``. It may returns the symbol of the empty word ``FSMEmptyWordSymbol``. INPUT: @@ -1152,15 +1150,12 @@ def FSMWordSymbol(word): """ if not isinstance(word, list): return FSMLetterSymbol(word) - if len(word) == 0: + if not word: return FSMEmptyWordSymbol - s = '' - for letter in word: - s += (',' if len(s) > 0 else '') + FSMLetterSymbol(letter) - return s + return ','.join(FSMLetterSymbol(letter) for letter in word) -#***************************************************************************** +# **************************************************************************** def is_FSMState(S): @@ -1223,7 +1218,7 @@ class FSMState(SageObject): OUTPUT: - Returns a state of a finite state machine. + A state of a finite state machine. EXAMPLES:: @@ -1432,8 +1427,7 @@ def __init__(self, label, word_out=None, def __lt__(self, other): """ - Returns True if label of ``self`` is less than label of - ``other``. + Return ``True`` if label of ``self`` is less than label of ``other``. INPUT: @@ -1441,7 +1435,7 @@ def __lt__(self, other): OUTPUT: - True or False. + ``True`` or ``False`` EXAMPLES:: @@ -1451,7 +1445,6 @@ def __lt__(self, other): """ return self.label() < other.label() - @property def final_word_out(self): """ @@ -1644,10 +1637,9 @@ def is_final(self, is_final): "can have a final output word. " % (self.label(),)) - def label(self): """ - Returns the label of the state. + Return the label of the state. INPUT: @@ -1666,10 +1658,9 @@ def label(self): """ return self._label_ - def __copy__(self): """ - Returns a (shallow) copy of the state. + Return a (shallow) copy of the state. INPUT: @@ -1713,13 +1704,11 @@ def __copy__(self): new.hook = self.hook return new - copy = __copy__ - def __deepcopy__(self, memo): """ - Returns a deep copy of the state. + Return a deep copy of the state. INPUT: @@ -1749,10 +1738,9 @@ def __deepcopy__(self, memo): new.initial_probability = deepcopy(self.initial_probability, memo) return new - def deepcopy(self, memo=None): """ - Returns a deep copy of the state. + Return a deep copy of the state. INPUT: @@ -1797,7 +1785,7 @@ def deepcopy(self, memo=None): def relabeled(self, label, memo=None): """ - Returns a deep copy of the state with a new label. + Return a deep copy of the state with a new label. INPUT: @@ -1871,11 +1859,7 @@ def __getstate__(self): def __hash__(self): """ - Returns a hash value for the object. - - INPUT: - - Nothing. + Return a hash value for the object. OUTPUT: @@ -1911,20 +1895,20 @@ def _repr_(self): """ return pretty(self.label()) - def __eq__(left, right): + def __eq__(self, other): """ - Returns True if two states are the same, i.e., if they have + Return ``True`` if two states are the same, i.e., if they have the same labels. INPUT: - - ``left`` -- a state. + - ``self`` -- a state. - - ``right`` -- a state. + - ``other`` -- a state. OUTPUT: - True or False. + ``True`` or ``False``. Note that the hooks and whether the states are initial or final are not checked. To fully compare two states (including @@ -1944,24 +1928,23 @@ def __eq__(left, right): sage: A == B True """ - if not is_FSMState(right): + if not is_FSMState(other): return False - return left.label() == right.label() - + return self.label() == other.label() - def __ne__(left, right): + def __ne__(self, other): """ Tests for inequality, complement of __eq__. INPUT: - - ``left`` -- a state. + - ``self`` -- a state. - - ``right`` -- a state. + - ``other`` -- a state. OUTPUT: - True or False. + ``True`` or ``False`` EXAMPLES:: @@ -1971,19 +1954,18 @@ def __ne__(left, right): sage: A != B False """ - return (not (left == right)) - + return not (self == other) - def fully_equal(left, right, compare_color=True): + def fully_equal(self, other, compare_color=True): """ - Checks whether two states are fully equal, i.e., including all + Check whether two states are fully equal, i.e., including all attributes except ``hook``. INPUT: - - ``left`` -- a state. + - ``self`` -- a state. - - ``right`` -- a state. + - ``other`` -- a state. - ``compare_color`` -- If ``True`` (default) colors are compared as well, otherwise not. @@ -2009,27 +1991,18 @@ def fully_equal(left, right, compare_color=True): sage: A.fully_equal(B, compare_color=False) True """ - color = not compare_color or left.color == right.color - return (left == right and - left.is_initial == right.is_initial and - left.is_final == right.is_final and - left.final_word_out == right.final_word_out and - left.word_out == right.word_out and + color = not compare_color or self.color == other.color + return (self == other and + self.is_initial == other.is_initial and + self.is_final == other.is_final and + self.final_word_out == other.final_word_out and + self.word_out == other.word_out and color and - left.initial_probability == right.initial_probability) - + self.initial_probability == other.initial_probability) def __bool__(self): """ - Returns True. - - INPUT: - - Nothing. - - OUTPUT: - - True or False. + Return ``True``. TESTS:: @@ -2039,13 +2012,11 @@ def __bool__(self): """ return True # A state cannot be zero (see __init__) - __nonzero__ = __bool__ - def _epsilon_successors_(self, fsm=None): """ - Returns the dictionary with states reachable from ``self`` + Return the dictionary with states reachable from ``self`` without reading anything from an input tape as keys. The values are lists of outputs. @@ -2106,15 +2077,14 @@ def _epsilon_successors_(self, fsm=None): _epsilon_successors_dict_[self].remove([]) # delete starting state if not _epsilon_successors_dict_[self]: del _epsilon_successors_dict_[self] - for s, outputs in six.iteritems(_epsilon_successors_dict_): + for s, outputs in _epsilon_successors_dict_.items(): _epsilon_successors_dict_[s] = [t for t, _ in itertools.groupby(sorted(outputs))] return _epsilon_successors_dict_ - def _in_epsilon_cycle_(self, fsm=None): """ - Returns whether ``self`` is in an epsilon-cycle or not. + Return whether ``self`` is in an epsilon-cycle or not. INPUT: @@ -2141,10 +2111,9 @@ def _in_epsilon_cycle_(self, fsm=None): """ return self in self._epsilon_successors_(fsm) - def _epsilon_cycle_output_empty_(self, fsm=None): """ - Returns whether all epsilon-cycles in which ``self`` is + Return whether all epsilon-cycles in which ``self`` is contained have an empty output (i.e., do not write any output word). @@ -2155,7 +2124,7 @@ def _epsilon_cycle_output_empty_(self, fsm=None): OUTPUT: - ``True`` or ``False``. + ``True`` or ``False`` A ``ValueError`` is raised when ``self`` is not in an epsilon cycle. @@ -2204,7 +2173,7 @@ def _epsilon_cycle_output_empty_(self, fsm=None): raise ValueError("State %s is not in an epsilon cycle." % (self,)) -#***************************************************************************** +# **************************************************************************** def is_FSMTransition(T): @@ -2312,16 +2281,16 @@ def __init__(self, from_state, to_state, def __lt__(self, other): """ - Returns True if ``self`` is less than ``other`` with respect to the + Return True if ``self`` is less than ``other`` with respect to the key ``(self.from_state, self.word_in, self.to_state, self.word_out)``. INPUT: - - `other` -- a transition. + - ``other`` -- a transition. OUTPUT: - True or False. + ``True`` or ``False`` EXAMPLES:: @@ -2332,14 +2301,9 @@ def __lt__(self, other): return (self.from_state, self.word_in, self.to_state, self.word_out) < \ (other.from_state, other.word_in, other.to_state, other.word_out) - def __copy__(self): """ - Returns a (shallow) copy of the transition. - - INPUT: - - Nothing. + Return a (shallow) copy of the transition. OUTPUT: @@ -2358,13 +2322,11 @@ def __copy__(self): new.hook = self.hook return new - copy = __copy__ - def __deepcopy__(self, memo): """ - Returns a deep copy of the transition. + Return a deep copy of the transition. INPUT: @@ -2389,10 +2351,9 @@ def __deepcopy__(self, memo): new.hook = deepcopy(self.hook, memo) return new - def deepcopy(self, memo=None): """ - Returns a deep copy of the transition. + Return a deep copy of the transition. INPUT: @@ -2438,11 +2399,9 @@ def _repr_(self): repr(self.to_state), self._in_out_label_()) - def _in_out_label_(self): """ - Returns the input and output of a transition as - "word_in|word_out". + Return the input and output of a transition as "word_in|word_out". INPUT: @@ -2461,10 +2420,9 @@ def _in_out_label_(self): return "%s|%s" % (FSMWordSymbol(self.word_in), FSMWordSymbol(self.word_out)) - - def __eq__(left, right): + def __eq__(self, other): """ - Returns True if the two transitions are the same, i.e., if the + Return ``True`` if the two transitions are the same, i.e., if the both go from the same states to the same states and read and write the same words. @@ -2472,13 +2430,13 @@ def __eq__(left, right): INPUT: - - ``left`` -- a transition. + - ``self`` -- a transition. - - ``right`` -- a transition. + - ``other`` -- a transition. OUTPUT: - True or False. + ``True`` or ``False`` EXAMPLES:: @@ -2489,28 +2447,26 @@ def __eq__(left, right): sage: t1 == t2 True """ - if not is_FSMTransition(right): - raise TypeError('Only instances of FSMTransition ' \ - 'can be compared.') - return left.from_state == right.from_state \ - and left.to_state == right.to_state \ - and left.word_in == right.word_in \ - and left.word_out == right.word_out - + if not is_FSMTransition(other): + return False + return self.from_state == other.from_state \ + and self.to_state == other.to_state \ + and self.word_in == other.word_in \ + and self.word_out == other.word_out - def __ne__(left, right): + def __ne__(self, other): """ + Test for inequality, complement of __eq__. INPUT: - - ``left`` -- a transition. + - ``self`` -- a transition. - - ``right`` -- a transition. + - ``other`` -- a transition. OUTPUT: - True or False. - Tests for inequality, complement of __eq__. + ``True`` or ``False`` EXAMPLES:: @@ -2521,20 +2477,11 @@ def __ne__(left, right): sage: t1 != t2 False """ - return (not (left == right)) - + return not (self == other) def __bool__(self): """ - Returns True. - - INPUT: - - Nothing. - - OUTPUT: - - True or False. + Return ``True``. EXAMPLES:: @@ -2547,7 +2494,7 @@ def __bool__(self): __nonzero__ = __bool__ -#***************************************************************************** +# **************************************************************************** def is_FiniteStateMachine(FSM): @@ -3127,9 +3074,9 @@ class FiniteStateMachine(SageObject): :attr:`input_alphabet`. """ - #************************************************************************* + # ************************************************************************ # init - #************************************************************************* + # ************************************************************************ def __init__(self, @@ -3148,9 +3095,10 @@ def __init__(self, sage: FiniteStateMachine() Empty finite state machine """ - self._states_ = [] # List of states in the finite state - # machine. Each state stores a list of - # outgoing transitions. + self._states_ = [] + # List of states in the finite state + # machine. Each state stores a list of + # outgoing transitions. if store_states_dict: self._states_dict_ = {} @@ -3192,16 +3140,16 @@ def __init__(self, if initial_states is not None: if not hasattr(initial_states, '__iter__'): - raise TypeError('Initial states must be iterable ' \ - '(e.g. a list of states).') + raise TypeError('Initial states must be iterable ' + '(e.g. a list of states).') for s in initial_states: state = self.add_state(s) state.is_initial = True if final_states is not None: if not hasattr(final_states, '__iter__'): - raise TypeError('Final states must be iterable ' \ - '(e.g. a list of states).') + raise TypeError('Final states must be iterable ' + '(e.g. a list of states).') for s in final_states: state = self.add_state(s) state.is_final = True @@ -3212,7 +3160,7 @@ def __init__(self, if on_duplicate_transition is None: on_duplicate_transition = duplicate_transition_ignore if hasattr(on_duplicate_transition, '__call__'): - self.on_duplicate_transition=on_duplicate_transition + self.on_duplicate_transition = on_duplicate_transition else: raise TypeError('on_duplicate_transition must be callable') @@ -3221,10 +3169,10 @@ def __init__(self, elif hasattr(data, 'items'): # data is a dict (or something similar), # format: key = from_state, value = iterator of transitions - for (sf, iter_transitions) in six.iteritems(data): + for (sf, iter_transitions) in data.items(): self.add_state(sf) if hasattr(iter_transitions, 'items'): - for (st, transition) in six.iteritems(iter_transitions): + for (st, transition) in iter_transitions.items(): self.add_state(st) if is_FSMTransition(transition): self.add_transition(transition) @@ -3275,18 +3223,13 @@ def __init__(self, self.construct_final_word_out(with_final_word_out) - #************************************************************************* + # ************************************************************************ # copy and hash - #************************************************************************* - + # ************************************************************************ def __copy__(self): """ - Returns a (shallow) copy of the finite state machine. - - INPUT: - - Nothing. + Return a (shallow) copy of the finite state machine. OUTPUT: @@ -3301,13 +3244,11 @@ def __copy__(self): """ raise NotImplementedError - copy = __copy__ - def empty_copy(self, memo=None, new_class=None): """ - Returns an empty deep copy of the finite state machine, i.e., + Return an empty deep copy of the finite state machine, i.e., ``input_alphabet``, ``output_alphabet``, ``on_duplicate_transition`` are preserved, but states and transitions are not. @@ -3353,10 +3294,9 @@ def empty_copy(self, memo=None, new_class=None): new._copy_from_other_(self, memo=memo, empty=True) return new - def __deepcopy__(self, memo): """ - Returns a deep copy of the finite state machine. + Return a deep copy of the finite state machine. INPUT: @@ -3376,10 +3316,9 @@ def __deepcopy__(self, memo): new._copy_from_other_(self) return new - def deepcopy(self, memo=None): """ - Returns a deep copy of the finite state machine. + Return a deep copy of the finite state machine. INPUT: @@ -3464,10 +3403,6 @@ def __getstate__(self): """ Return state for pickling excluding outgoing transitions. - INPUT: - - None - OUTPUT: A dictionary. @@ -3482,10 +3417,8 @@ def __getstate__(self): sage: loads(dumps(A)) == A True """ - odict = self.__dict__.copy() # copy the dict since we change it - odict.update({ - 'transitions': self.transitions() - }) + odict = self.__dict__.copy() # copy the dict since we change it + odict.update({'transitions': self.transitions()}) return odict def __setstate__(self, d): @@ -3513,13 +3446,13 @@ def __setstate__(self, d): transitions = d.pop('transitions') self.__dict__.update(d) for state in self.iter_states(): - state.transitions = [] # clean outgoing transitions + state.transitions = [] # clean outgoing transitions for transition in transitions: self.add_transition(transition) def relabeled(self, memo=None, labels=None): """ - Returns a deep copy of the finite state machine, but the + Return a deep copy of the finite state machine, but the states are relabeled. INPUT: @@ -3564,10 +3497,9 @@ def relabeled(self, memo=None, labels=None): del self._deepcopy_labels_ return new - def induced_sub_finite_state_machine(self, states): """ - Returns a sub-finite-state-machine of the finite state machine + Return a sub-finite-state-machine of the finite state machine induced by the given states. INPUT: @@ -3645,13 +3577,13 @@ def __hash__(self): """ if getattr(self, "_immutable", False): return hash((tuple(self.states()), tuple(self.transitions()))) - raise TypeError("Finite state machines are mutable, " \ - "and thus not hashable.") + raise TypeError("Finite state machines are mutable, " + "and thus not hashable.") - #************************************************************************* + # ************************************************************************ # operators - #************************************************************************* + # ************************************************************************ def __or__(self, other): @@ -3702,10 +3634,9 @@ def __iadd__(self, other): """ raise NotImplementedError - def __and__(self, other): """ - Returns the intersection of ``self`` with ``other``. + Return the intersection of ``self`` with ``other``. TESTS:: @@ -3717,7 +3648,6 @@ def __and__(self, other): if is_FiniteStateMachine(other): return self.intersection(other) - def __imul__(self, other): """ TESTS:: @@ -3963,41 +3893,34 @@ def __call__(self, *args, **kwargs): ....: automatic_output_type=True)) <... 'tuple'> """ - if len(args) == 0: + if not args: raise TypeError("Called with too few arguments.") if is_FiniteStateMachine(args[0]): return self.composition(*args, **kwargs) if hasattr(args[0], '__iter__'): - if not 'full_output' in kwargs: + if 'full_output' not in kwargs: kwargs['full_output'] = False - if not 'list_of_outputs' in kwargs: + if 'list_of_outputs' not in kwargs: kwargs['list_of_outputs'] = False - if not 'automatic_output_type' in kwargs: - kwargs['automatic_output_type'] = not 'format_output' in kwargs + if 'automatic_output_type' not in kwargs: + kwargs['automatic_output_type'] = 'format_output' not in kwargs input_tape = args[0] - if hasattr(input_tape, 'is_finite') and \ - not input_tape.is_finite(): - if not 'iterator_type' in kwargs: + if hasattr(input_tape, 'is_finite') and not input_tape.is_finite(): + if 'iterator_type' not in kwargs: kwargs['iterator_type'] = 'simple' return self.iter_process(*args, **kwargs) return self.process(*args, **kwargs) raise TypeError("Do not know what to do with that arguments.") - - #************************************************************************* + # ************************************************************************ # tests - #************************************************************************* - + # ************************************************************************ def __bool__(self): """ - Returns True if the finite state machine consists of at least + Return True if the finite state machine consists of at least one state. - INPUT: - - Nothing. - OUTPUT: True or False. @@ -4011,16 +3934,16 @@ def __bool__(self): __nonzero__ = __bool__ - def __eq__(left, right): + def __eq__(self, other): """ - Returns ``True`` if the two finite state machines are equal, + Return ``True`` if the two finite state machines are equal, i.e., if they have the same states and the same transitions. INPUT: - - ``left`` -- a finite state machine. + - ``self`` -- a finite state machine. - - ``right`` -- a finite state machine. + - ``other`` -- a finite state machine. OUTPUT: @@ -4064,53 +3987,49 @@ def __eq__(left, right): sage: F == G True """ - if not is_FiniteStateMachine(right): - raise TypeError('Only instances of FiniteStateMachine ' - 'can be compared.') - if len(left._states_) != len(right._states_): + if not is_FiniteStateMachine(other): + return False + if len(self._states_) != len(other._states_): return False colors_equal = True - for state in left.iter_states(): + for state in self.iter_states(): try: - right_state = right.state(state.label()) + other_state = other.state(state.label()) except LookupError: return False # we handle colors separately - if not state.fully_equal(right_state, compare_color=False): + if not state.fully_equal(other_state, compare_color=False): return False - if state.color != right_state.color: + if state.color != other_state.color: colors_equal = False - left_transitions = state.transitions - right_transitions = right.state(state).transitions - if len(left_transitions) != len(right_transitions): + self_transitions = state.transitions + other_transitions = other.state(state).transitions + if len(self_transitions) != len(other_transitions): return False - for t in left_transitions: - if t not in right_transitions: + for t in self_transitions: + if t not in other_transitions: return False # handle colors if colors_equal: return True - if left.is_monochromatic() and right.is_monochromatic(): - return True - return False + return self.is_monochromatic() and other.is_monochromatic() - - def __ne__(left, right): + def __ne__(self, other): """ Tests for inequality, complement of :meth:`.__eq__`. INPUT: - - ``left`` -- a finite state machine. + - ``self`` -- a finite state machine. - - ``right`` -- a finite state machine. + - ``other`` -- a finite state machine. OUTPUT: - True or False. + ``True`` or ``False`` EXAMPLES:: @@ -4122,13 +4041,14 @@ def __ne__(left, right): sage: E == G False """ - return (not (left == right)) - + return not (self == other) def __contains__(self, item): """ - Returns true, if the finite state machine contains the - state or transition item. Note that only the labels of the + Return ``True``, if the finite state machine contains the + state or transition item. + + Note that only the labels of the states and the input and output words are tested. INPUT: @@ -4137,7 +4057,7 @@ def __contains__(self, item): OUTPUT: - True or False. + ``True`` or ``False`` EXAMPLES:: @@ -4275,9 +4195,9 @@ def default_is_zero(expression): for state in self.iter_states()) - #************************************************************************* + # ************************************************************************ # representations / LaTeX - #************************************************************************* + # ************************************************************************ def _repr_(self): @@ -4350,7 +4270,6 @@ def format_letter_negative(self, letter): \path[->] (v0) edge[loop above] node {$\overline{1}$} (); \end{tikzpicture} """ - from sage.rings.integer_ring import ZZ if letter in ZZ and letter < 0: return r'\overline{%d}' % -letter else: @@ -4856,11 +4775,7 @@ def latex_options(self, def _latex_(self): r""" - Returns a LaTeX code for the graph of the finite state machine. - - INPUT: - - Nothing. + Return a LaTeX code for the graph of the finite state machine. OUTPUT: @@ -4957,7 +4872,7 @@ def label_rotation(angle, both_directions): accepting_show_empty = False result = "\\begin{tikzpicture}[%s]\n" % ", ".join(options) - j = 0; + j = 0 for vertex in self.iter_states(): if not hasattr(vertex, "coordinates"): vertex.coordinates = (3*cos(2*pi*j/len(self.states())), @@ -5018,15 +4933,15 @@ def key_function(s): key=key_function )) - for ((source, target), transitions) in six.iteritems(adjacent): - if len(transitions) > 0: + for ((source, target), transitions) in adjacent.items(): + if transitions: labels = [] for transition in transitions: if hasattr(transition, "format_label"): labels.append(transition.format_label()) else: labels.append(self._latex_transition_label_( - transition, self.format_transition_label)) + transition, self.format_transition_label)) label = ", ".join(labels) if source != target: angle = atan2( @@ -5058,17 +4973,16 @@ def key_function(s): result += "\\end{tikzpicture}" return result - def _latex_transition_label_(self, transition, format_function=None): r""" - Returns the proper transition label. + Return the proper transition label. INPUT: - - ``transition`` - a transition + - ``transition`` -- a transition - - ``format_function`` - a function formatting the labels + - ``format_function`` -- a function formatting the labels OUTPUT: @@ -5141,15 +5055,14 @@ def set_coordinates(self, coordinates, default=True): state.coordinates = (3*cos(2*pi*j/n), 3*sin(2*pi*j/n)) - - #************************************************************************* + # ************************************************************************ # other - #************************************************************************* - + # ************************************************************************ def _matrix_(self, R=None): """ - Returns the adjacency matrix of the finite state machine. + Return the adjacency matrix of the finite state machine. + See :meth:`.adjacency_matrix` for more information. EXAMPLES:: @@ -5169,11 +5082,10 @@ def _matrix_(self, R=None): """ return self.adjacency_matrix() - def adjacency_matrix(self, input=None, entry=None): """ - Returns the adjacency matrix of the underlying graph. + Return the adjacency matrix of the underlying graph. INPUT: @@ -5246,7 +5158,6 @@ def adjacency_matrix(self, input=None, [1 1 0] """ - from sage.rings.integer_ring import ZZ def default_function(transitions): x = var('x') @@ -5327,7 +5238,6 @@ def determine_input_alphabet(self, reset=True): ain.add(letter) self.input_alphabet = list(ain) - def determine_output_alphabet(self, reset=True): """ Determine the output alphabet according to the transitions @@ -5381,7 +5291,6 @@ def determine_output_alphabet(self, reset=True): aout.add(letter) self.output_alphabet = list(aout) - def determine_alphabets(self, reset=True): """ Determine the input and output alphabet according to the @@ -5425,19 +5334,13 @@ def determine_alphabets(self, reset=True): self.determine_input_alphabet(reset) self.determine_output_alphabet(reset) - - #************************************************************************* + # ************************************************************************ # get states and transitions - #************************************************************************* - + # ************************************************************************ def states(self): """ - Returns the states of the finite state machine. - - INPUT: - - Nothing. + Return the states of the finite state machine. OUTPUT: @@ -5453,11 +5356,7 @@ def states(self): def iter_states(self): """ - Returns an iterator of the states. - - INPUT: - - Nothing. + Return an iterator of the states. OUTPUT: @@ -5471,10 +5370,9 @@ def iter_states(self): """ return iter(self._states_) - def transitions(self, from_state=None): """ - Returns a list of all transitions. + Return a list of all transitions. INPUT: @@ -5494,10 +5392,9 @@ def transitions(self, from_state=None): """ return list(self.iter_transitions(from_state)) - def iter_transitions(self, from_state=None): """ - Returns an iterator of all transitions. + Return an iterator of all transitions. INPUT: @@ -5526,14 +5423,9 @@ def iter_transitions(self, from_state=None): else: return iter(self.state(from_state).transitions) - def _iter_transitions_all_(self): """ - Returns an iterator over all transitions. - - INPUT: - - Nothing. + Return an iterator over all transitions. OUTPUT: @@ -5550,14 +5442,9 @@ def _iter_transitions_all_(self): for t in state.transitions: yield t - def initial_states(self): """ - Returns a list of all initial states. - - INPUT: - - Nothing. + Return a list of all initial states. OUTPUT: @@ -5574,14 +5461,9 @@ def initial_states(self): """ return list(self.iter_initial_states()) - def iter_initial_states(self): """ - Returns an iterator of the initial states. - - INPUT: - - Nothing. + Return an iterator of the initial states. OUTPUT: @@ -5600,11 +5482,7 @@ def iter_initial_states(self): def final_states(self): """ - Returns a list of all final states. - - INPUT: - - Nothing. + Return a list of all final states. OUTPUT: @@ -5622,14 +5500,9 @@ def final_states(self): """ return list(self.iter_final_states()) - def iter_final_states(self): """ - Returns an iterator of the final states. - - INPUT: - - Nothing. + Return an iterator of the final states. OUTPUT: @@ -5649,7 +5522,7 @@ def iter_final_states(self): def state(self, state): """ - Returns the state of the finite state machine. + Return the state of the finite state machine. INPUT: @@ -5659,8 +5532,7 @@ def state(self, state): OUTPUT: - Returns the state of the finite state machine corresponding to - ``state``. + The state of the finite state machine corresponding to ``state``. If no state is found, then a ``LookupError`` is thrown. @@ -5693,10 +5565,9 @@ def what(s, switch): pass raise LookupError("No state with label %s found." % (what(state, switch),)) - def transition(self, transition): """ - Returns the transition of the finite state machine. + Return the transition of the finite state machine. INPUT: @@ -5706,8 +5577,8 @@ def transition(self, transition): OUTPUT: - Returns the transition of the finite state machine - corresponding to ``transition``. + The transition of the finite state machine corresponding + to ``transition``. If no transition is found, then a ``LookupError`` is thrown. @@ -5728,15 +5599,13 @@ def transition(self, transition): return s raise LookupError("No transition found.") - - #************************************************************************* + # ************************************************************************ # properties (state and transitions) - #************************************************************************* - + # ************************************************************************ def has_state(self, state): """ - Returns whether ``state`` is one of the states of the finite + Return whether ``state`` is one of the states of the finite state machine. INPUT: @@ -5758,10 +5627,9 @@ def has_state(self, state): except LookupError: return False - def has_transition(self, transition): """ - Returns whether ``transition`` is one of the transitions of + Return whether ``transition`` is one of the transitions of the finite state machine. INPUT: @@ -5787,10 +5655,9 @@ def has_transition(self, transition): return transition in self.iter_transitions() raise TypeError("Transition is not an instance of FSMTransition.") - def has_initial_state(self, state): """ - Returns whether ``state`` is one of the initial states of the + Return whether ``state`` is one of the initial states of the finite state machine. INPUT: @@ -5812,14 +5679,9 @@ def has_initial_state(self, state): except LookupError: return False - def has_initial_states(self): """ - Returns whether the finite state machine has an initial state. - - INPUT: - - Nothing. + Return whether the finite state machine has an initial state. OUTPUT: @@ -5830,12 +5692,11 @@ def has_initial_states(self): sage: FiniteStateMachine().has_initial_states() False """ - return len(self.initial_states()) > 0 - + return bool(self.initial_states()) def has_final_state(self, state): """ - Returns whether ``state`` is one of the final states of the + Return whether ``state`` is one of the final states of the finite state machine. INPUT: @@ -5856,14 +5717,9 @@ def has_final_state(self, state): except LookupError: return False - def has_final_states(self): """ - Returns whether the finite state machine has a final state. - - INPUT: - - Nothing. + Return whether the finite state machine has a final state. OUTPUT: @@ -5874,25 +5730,19 @@ def has_final_states(self): sage: FiniteStateMachine().has_final_states() False """ - return len(self.final_states()) > 0 + return bool(self.final_states()) - - #************************************************************************* + # ************************************************************************ # properties - #************************************************************************* - + # ************************************************************************ def is_deterministic(self): """ Return whether the finite finite state machine is deterministic. - INPUT: - - Nothing. - OUTPUT: - ``True`` or ``False``. + ``True`` or ``False`` A finite state machine is considered to be deterministic if each transition has input label of length one and for each @@ -5926,7 +5776,7 @@ def is_deterministic(self): sage: Automaton(initial_states=[0, 1]).is_deterministic() False """ - if len(self.initial_states())>1: + if len(self.initial_states()) > 1: return False for state in self.iter_states(): for transition in state.transitions: @@ -5937,23 +5787,18 @@ def is_deterministic(self): state.transitions, key=lambda t: t.word_in) - for key,transition_class in transition_classes_by_word_in: + for key, transition_class in transition_classes_by_word_in: if len(transition_class) > 1: return False return True - def is_complete(self): """ - Returns whether the finite state machine is complete. - - INPUT: - - Nothing. + Return whether the finite state machine is complete. OUTPUT: - ``True`` or ``False``. + ``True`` or ``False`` A finite state machine is considered to be complete if each transition has an input label of length one and for each @@ -6021,9 +5866,9 @@ def is_connected(self): raise NotImplementedError - #************************************************************************* + # ************************************************************************ # let the finite state machine work - #************************************************************************* + # ************************************************************************ _process_default_options_ = {'full_output': True, 'list_of_outputs': None, @@ -6031,10 +5876,9 @@ def is_connected(self): 'always_include_output': False, 'automatic_output_type': False} - def process(self, *args, **kwargs): """ - Returns whether the finite state machine accepts the input, the state + Return whether the finite state machine accepts the input, the state where the computation stops and which output is generated. INPUT: @@ -6639,14 +6483,14 @@ def _iter_process_simple_(self, iterator): "'simple' iterator cannot be used " "here." % (len(current),)) - pos, states = next(six.iteritems(current)) + pos, states = next(iter(current.items())) if len(states) > 1: raise RuntimeError("Process has branched " "(visiting %s states in branch). The " "'simple' iterator cannot be used " "here." % (len(states),)) - state, branch = next(six.iteritems(states)) + state, branch = next(iter(states.items())) if len(branch.outputs) > 1: raise RuntimeError("Process has branched. " "(%s different outputs in branch). The " @@ -6656,15 +6500,14 @@ def _iter_process_simple_(self, iterator): for o in branch.outputs[0]: yield o - branch.outputs[0] = [] # Reset output so that in the next round - # (of "for current in iterator") only new - # output is returned (by the yield). + branch.outputs[0] = [] + # Reset output so that in the next round + # (of "for current in iterator") only new + # output is returned (by the yield). - - #************************************************************************* + # ************************************************************************ # change finite state machine (add/remove state/transitions) - #************************************************************************* - + # ************************************************************************ def add_state(self, state): """ @@ -6800,7 +6643,7 @@ def add_transition(self, *args, **kwargs): if is_FSMTransition(d): return self._add_fsm_transition_(d) else: - d = next(itervalues(kwargs)) + d = next(iter(kwargs.values())) if hasattr(d, 'items'): args = [] kwargs = d @@ -6960,9 +6803,9 @@ def add_from_transition_function(self, function, initial_states=None, state.is_initial = True not_done.append(state) else: - raise TypeError('Initial states must be iterable ' \ - '(e.g. a list of states).') - if len(not_done) == 0: + raise TypeError('Initial states must be iterable ' + '(e.g. a list of states).') + if not not_done: raise ValueError("No state is initial.") if explore_existing_states: ignore_done = self.states() @@ -6997,7 +6840,7 @@ def add_from_transition_function(self, function, initial_states=None, for (st_label, word) in return_value: if not self.has_state(st_label): not_done.append(self.add_state(st_label)) - elif len(ignore_done) > 0: + elif ignore_done: u = self.state(st_label) if u in ignore_done: not_done.append(u) @@ -7091,14 +6934,14 @@ def add_transitions_from_function(self, function, labels_as_input=True): transitions = return_value for t in transitions: if not hasattr(t, '__getitem__'): - raise ValueError("The callback function for " - "add_transitions_from_function " - "is expected to return a " - "pair (word_in, word_out) or a " - "list of such pairs. For " - "states %s and %s however, it " - "returned %s, which is not " - "acceptable." % (s_from, s_to, return_value)) + raise ValueError("The callback function for " + "add_transitions_from_function " + "is expected to return a " + "pair (word_in, word_out) or a " + "list of such pairs. For " + "states %s and %s however, it " + "returned %s, which is not " + "acceptable." % (s_from, s_to, return_value)) label_in = t[0] try: label_out = t[1] @@ -7184,10 +7027,9 @@ def remove_epsilon_transitions(self): """ raise NotImplementedError - def epsilon_successors(self, state): """ - Returns the dictionary with states reachable from ``state`` + Return the dictionary with states reachable from ``state`` without reading anything from an input tape as keys. The values are lists of outputs. @@ -7271,29 +7113,27 @@ def accessible_components(self): sage: F.accessible_components() Automaton with 3 states """ - - if len(self.initial_states()) == 0: + if not self.initial_states(): return deepcopy(self) - memo = {} + def accessible(from_state, read): return [(deepcopy(x.to_state, memo), x.word_out) for x in self.iter_transitions(from_state) if x.word_in[0] == read] - new_initial_states=[deepcopy(x, memo) for x in self.initial_states()] + new_initial_states = [deepcopy(x, memo) for x in self.initial_states()] result = self.empty_copy() result.add_from_transition_function(accessible, initial_states=new_initial_states) for final_state in self.iter_final_states(): try: - new_final_state=result.state(final_state.label) - new_final_state.is_final=True + new_final_state = result.state(final_state.label) + new_final_state.is_final = True except LookupError: pass return result - def coaccessible_components(self): r""" Return the sub-machine induced by the coaccessible states of this @@ -7814,7 +7654,7 @@ def product_FiniteStateMachine(self, other, function, final_function=None, new_class=None): r""" - Returns a new finite state machine whose states are + Return a new finite state machine whose states are `d`-tuples of states of the original finite state machines. INPUT: @@ -8081,7 +7921,7 @@ def default_final_function(*args): def composition(self, other, algorithm=None, only_accessible_components=True): """ - Returns a new transducer which is the composition of ``self`` + Return a new transducer which is the composition of ``self`` and ``other``. INPUT: @@ -8379,16 +8219,9 @@ def composition(self, other, algorithm=None, "possible.") if algorithm is None: - if (any(len(t.word_out) > 1 - for t in other.iter_transitions()) + if (any(len(t.word_out) > 1 for t in other.iter_transitions()) or - any(len(t.word_in) != 1 - for t in self.iter_transitions()) - #this might be used for multi-tape mode. - #or - #any(isinstance(t.word_in[0], tuple) and None in t.word_in[0] - # for t in self.iter_transitions()) - ): + any(len(t.word_in) != 1 for t in self.iter_transitions())): algorithm = 'explorative' else: algorithm = 'direct' @@ -8399,7 +8232,6 @@ def composition(self, other, algorithm=None, else: raise ValueError("Unknown algorithm %s." % (algorithm,)) - def _composition_direct_(self, other, only_accessible_components=True): """ See :meth:`.composition` for details. @@ -8545,16 +8377,11 @@ def composition_transition(states, input): F.output_alphabet = second.output_alphabet return F - def input_projection(self): """ - Returns an automaton where the output of each transition of + Return an automaton where the output of each transition of self is deleted. - INPUT: - - Nothing - OUTPUT: An automaton. @@ -8571,16 +8398,11 @@ def input_projection(self): """ return self.projection(what='input') - def output_projection(self): """ - Returns a automaton where the input of each transition of self + Return a automaton where the input of each transition of self is deleted and the new input is the original output. - INPUT: - - Nothing - OUTPUT: An automaton. @@ -8615,10 +8437,9 @@ def output_projection(self): """ return self.projection(what='output') - def projection(self, what='input'): """ - Returns an Automaton which transition labels are the projection + Return an Automaton which transition labels are the projection of the transition labels of the input. INPUT: @@ -8683,10 +8504,9 @@ def projection(self, what='input'): return new - def transposition(self, reverse_output_labels=True): """ - Returns a new finite state machine, where all transitions of the + Return a new finite state machine, where all transitions of the input finite state machine are reversed. INPUT: @@ -8785,17 +8605,12 @@ def transposition(self, reverse_output_labels=True): return transposition - def split_transitions(self): """ - Returns a new transducer, where all transitions in self with input + Return a new transducer, where all transitions in self with input labels consisting of more than one letter are replaced by a path of the corresponding length. - INPUT: - - Nothing. - OUTPUT: A new transducer. @@ -8826,16 +8641,11 @@ def split_transitions(self): transition.word_out)) return new - def final_components(self): """ - Returns the final components of a finite state machine as finite + Return the final components of a finite state machine as finite state machines. - INPUT: - - Nothing. - OUTPUT: A list of finite state machines, each representing a final @@ -9007,7 +8817,6 @@ def completion(self, sink=None): except LookupError: pass else: - from sage.rings.integer_ring import ZZ sink = 1 + max(itertools.chain( [-1], (s.label() for s in result.iter_states() @@ -9206,11 +9015,7 @@ def find_common_output(state): def equivalence_classes(self): r""" - Returns a list of equivalence classes of states. - - INPUT: - - Nothing. + Return a list of equivalence classes of states. OUTPUT: @@ -9284,7 +9089,7 @@ def equivalence_classes(self): state.final_word_out) states_grouped = full_group_by(self.states(), key=key_0) classes_current = [equivalence_class for - (key,equivalence_class) in states_grouped] + (key, equivalence_class) in states_grouped] while len(classes_current) != len(classes_previous): class_of = {} @@ -9304,15 +9109,13 @@ def equivalence_classes(self): for class_previous in classes_previous: states_grouped = full_group_by(class_previous, key=key_current) classes_current.extend([equivalence_class for - (key,equivalence_class) in states_grouped]) + (key, equivalence_class) in states_grouped]) return classes_current - def quotient(self, classes): r""" - Constructs the quotient with respect to the equivalence - classes. + Construct the quotient with respect to the equivalence classes. INPUT: @@ -9405,10 +9208,10 @@ def quotient(self, classes): for t in c[0].transitions]) for transition in self.iter_transitions(c[0]): new.add_transition( - from_state = new_state, - to_state = state_mapping[transition.to_state], - word_in = transition.word_in, - word_out = transition.word_out) + from_state=new_state, + to_state=state_mapping[transition.to_state], + word_in=transition.word_in, + word_out=transition.word_out) # check that all class members have the same information (modulo classes) for state in c: @@ -9478,7 +9281,7 @@ def key(transition): memo = {} for state in self.states(): - new_state = deepcopy(state,memo) + new_state = deepcopy(state, memo) state_dict[state] = new_state new.add_state(new_state) @@ -9817,7 +9620,6 @@ def construct_final_word_out(self, letters, allow_non_final=True): sage: F.state(0).final_word_out [] """ - from itertools import cycle if not isinstance(letters, list): letters = [letters] @@ -9876,27 +9678,25 @@ def find_final_word_out(state): assert(not in_progress) # trailing_letters is an infinite iterator additionally # marking positions - trailing_letters = cycle(enumerate(letters)) + trailing_letters = itertools.cycle(enumerate(letters)) find_final_word_out(state) # actual modifications can only be carried out after all final words # have been computed as it may not be permissible to stop at a # formerly non-final state unless a cycle has been completed. - for (state, position), final_word_out in six.iteritems(cache): + for (state, position), final_word_out in cache.items(): if position == 0 and final_word_out is not None: state.is_final = True state.final_word_out = final_word_out - # ************************************************************************* # other # ************************************************************************* - def graph(self, edge_labels='words_in_out'): """ - Returns the graph of the finite state machine with labeled + Return the graph of the finite state machine with labeled vertices and labeled edges. INPUT: @@ -9931,7 +9731,7 @@ def graph(self, edge_labels='words_in_out'): :class:`DiGraph` """ if edge_labels == 'words_in_out': - label_fct = lambda t:t._in_out_label_() + label_fct = lambda t: t._in_out_label_() elif hasattr(edge_labels, '__call__'): label_fct = edge_labels else: @@ -9941,7 +9741,7 @@ def graph(self, edge_labels='words_in_out'): isolated_vertices = [] for state in self.iter_states(): transitions = state.transitions - if len(transitions) == 0: + if not transitions: isolated_vertices.append(state.label()) for t in transitions: graph_data.append((t.from_state.label(), t.to_state.label(), @@ -10018,7 +9818,7 @@ def predecessors(self, state, valid_input=None): valid_list.append(input_list) valid_input = valid_list - unhandeled_direct_predecessors = {s:[] for s in self.states() } + unhandeled_direct_predecessors = {s: [] for s in self.states()} for t in self.transitions(): if valid_input is None or t.word_in in valid_input: unhandeled_direct_predecessors[t.to_state].append(t.from_state) @@ -10145,8 +9945,7 @@ def number_of_words(self, variable=var('n'), NotImplementedError: Finite State Machine must be deterministic. """ from sage.modules.free_module_element import vector - from sage.arith.all import falling_factorial - from sage.rings.integer_ring import ZZ + from sage.arith.all import binomial from sage.symbolic.ring import SR if base_ring is None: base_ring = QQbar @@ -10156,7 +9955,7 @@ def jordan_block_power(block, exponent): return matrix(block.nrows(), block.nrows(), lambda i, j: eigenvalue**(exponent-(j-i)) * - falling_factorial(exponent, j-i) / ZZ(j-i).factorial() + binomial(exponent, j - i) if j >= i else 0) if not self.is_deterministic(): @@ -10175,7 +9974,7 @@ def jordan_block_power(block, exponent): def asymptotic_moments(self, variable=var('n')): r""" - Returns the main terms of expectation and variance of the sum + Return the main terms of expectation and variance of the sum of output labels and its covariance with the sum of input labels. @@ -10497,8 +10296,9 @@ def asymptotic_moments(self, variable=var('n')): sage: moments['covariance'] -1/8*n + Order(1) - This warning can be silenced by :func:`~sage.misc.misc.set_verbose`:: + This warning can be silenced by :func:`~sage.misc.verbose.set_verbose`:: + sage: from sage.misc.verbose import set_verbose sage: set_verbose(-1, "finite_state_machine.py") sage: moments = T.asymptotic_moments() sage: moments['expectation'] @@ -10606,9 +10406,11 @@ def get_matrix(fsm, x, y): y = R.symbol() z = R.symbol() M = get_matrix(self, x, y) + def substitute_one(g): return g.subs({x: 1, y: 1, z: 1}) else: + def substitute_one(g): # the result of the substitution shall live in QQ, # not in the polynomial ring R, so the method @@ -10976,8 +10778,7 @@ def default_is_zero(expression): def entry(transition): word_out = transition.word_out - if len(word_out) == 0 or ( - len(word_out) == 1 and not test(word_out[0])): + if not word_out or (len(word_out) == 1 and not test(word_out[0])): return transition.word_in[0] else: return 0 @@ -10985,7 +10786,6 @@ def entry(transition): relabeled = self.relabeled() n = len(relabeled.states()) assert [s.label() for s in relabeled.states()] == list(range(n)) - from sage.rings.integer_ring import ZZ entry_vector = vector(ZZ(s.is_initial) for s in relabeled.states()) exit_vector = vector([1] * n) @@ -11016,17 +10816,14 @@ def entry(transition): # ring, extend it instead of creating a univariate # polynomial ring over a polynomial ring. This # should improve performance. - R = PolynomialRing( - base_ring.base_ring(), - base_ring.variable_names() - + ('Z_waiting_time',)) + R = PolynomialRing(base_ring.base_ring(), + base_ring.variable_names() + + ('Z_waiting_time',)) else: R = PolynomialRing(base_ring, 'Z_waiting_time') Z = R.gens()[-1] - system_matrix = identity_matrix(n) - Z * \ - transition_matrix - G = entry_vector * system_matrix.solve_right( - exit_vector) + system_matrix = identity_matrix(n) - Z * transition_matrix + G = entry_vector * system_matrix.solve_right(exit_vector) expectation = G.subs({Z: 1}) variance = 2 * G.derivative(Z).subs({Z: 1}) \ + expectation \ @@ -11038,18 +10835,13 @@ def entry(transition): return {'expectation': expectation, 'variance': variance} - def is_monochromatic(self): """ - Checks whether the colors of all states are equal. - - INPUT: - - Nothing. + Check whether the colors of all states are equal. OUTPUT: - ``True`` or ``False``. + ``True`` or ``False`` EXAMPLES:: @@ -11064,7 +10856,6 @@ def is_monochromatic(self): """ return equal(s.color for s in self.iter_states()) - def language(self, max_length=None, **kwargs): r""" Return all words that can be written by this transducer. @@ -11159,7 +10950,7 @@ def language(self, max_length=None, **kwargs): it._finished_ = [] -#***************************************************************************** +# **************************************************************************** def is_Automaton(FSM): @@ -11279,13 +11070,13 @@ def _repr_(self): def _latex_transition_label_(self, transition, format_function=None): r""" - Returns the proper transition label. + Return the proper transition label. INPUT: - - ``transition`` - a transition + - ``transition`` -- a transition - - ``format_function`` - a function formatting the labels + - ``format_function`` -- a function formatting the labels OUTPUT: @@ -11312,10 +11103,9 @@ def _latex_transition_label_(self, transition, format_function = latex return format_function(transition.word_in) - def intersection(self, other, only_accessible_components=True): """ - Returns a new automaton which accepts an input if it is + Return a new automaton which accepts an input if it is accepted by both given automata. INPUT: @@ -11410,19 +11200,13 @@ def function(transition1, transition2): function, only_accessible_components=only_accessible_components) - cartesian_product = intersection - def determinisation(self): """ - Returns a deterministic automaton which accepts the same input + Return a deterministic automaton which accepts the same input words as the original one. - INPUT: - - Nothing. - OUTPUT: A new automaton, which is deterministic. @@ -11597,10 +11381,9 @@ def set_transition(states, letter): return result - def minimization(self, algorithm=None): """ - Returns the minimization of the input automaton as a new automaton. + Return the minimization of the input automaton as a new automaton. INPUT: @@ -11673,10 +11456,9 @@ def minimization(self, algorithm=None): else: raise NotImplementedError("Algorithm '%s' is not implemented. Choose 'Moore' or 'Brzozowski'" % algorithm) - def _minimization_Brzozowski_(self): """ - Returns a minimized automaton by using Brzozowski's algorithm. + Return a minimized automaton by using Brzozowski's algorithm. See also :meth:`.minimization`. @@ -11691,10 +11473,9 @@ def _minimization_Brzozowski_(self): """ return self.transposition().determinisation().transposition().determinisation() - def _minimization_Moore_(self): """ - Returns a minimized automaton by using Moore's algorithm. + Return a minimized automaton by using Moore's algorithm. See also :meth:`.minimization`. @@ -11713,10 +11494,9 @@ def _minimization_Moore_(self): if self.is_deterministic(): return self.quotient(self.equivalence_classes()) else: - raise NotImplementedError("Minimization via Moore's Algorithm is only " \ + raise NotImplementedError("Minimization via Moore's Algorithm is only " "implemented for deterministic finite state machines") - def complement(self): r""" Return the complement of this automaton. @@ -11803,7 +11583,7 @@ def is_equivalent(self, other): B = other.minimization().relabeled() labels = {B.process(path)[1].label(): state.label() - for (state, path) in six.iteritems(address)} + for (state, path) in address.items()} try: return A == B.relabeled(labels=labels) except KeyError: @@ -12205,7 +11985,6 @@ def shannon_parry_markov_chain(self): raise NotImplementedError("Automaton must be strongly connected.") if not all(s.is_final for s in self.iter_states()): raise NotImplementedError("All states must be final.") - from sage.rings.integer_ring import ZZ M = self.adjacency_matrix().change_ring(ZZ) states = {state: i for i, state in enumerate(self.iter_states())} w_all = sorted(M.eigenvectors_right(), @@ -12234,8 +12013,8 @@ def shannon_parry_markov_chain(self): P.state(s.label()).color = 1/(w[states[s]] * ff) P.state(s.label()).initial_probability = w[states[s]] * u[states[s]] return P - - + + def with_output(self, word_out_function=None): r""" Construct a transducer out of this automaton. @@ -12383,7 +12162,7 @@ def language(self, max_length=None, **kwargs): return T.language(max_length) -#***************************************************************************** +# **************************************************************************** def is_Transducer(FSM): @@ -12483,13 +12262,13 @@ def _repr_(self): def _latex_transition_label_(self, transition, format_function=None): r""" - Returns the proper transition label. + Return the proper transition label. INPUT: - - ``transition`` - a transition + - ``transition`` -- a transition - - ``format_function`` - a function formatting the labels + - ``format_function`` -- a function formatting the labels OUTPUT: @@ -12517,10 +12296,9 @@ def _latex_transition_label_(self, transition, return (format_function(transition.word_in) + "\\mid " + format_function(transition.word_out)) - def intersection(self, other, only_accessible_components=True): """ - Returns a new transducer which accepts an input if it is accepted by + Return a new transducer which accepts an input if it is accepted by both given finite state machines producing the same output. INPUT: @@ -12806,14 +12584,14 @@ def cartesian_product(self, other, only_accessible_components=True): def function(*transitions): if equal(t.word_in for t in transitions): return (transitions[0].word_in, - list(zip_longest( + list(itertools.zip_longest( *(t.word_out for t in transitions) ))) else: raise LookupError def final_function(*states): - return list(zip_longest(*(s.final_word_out + return list(itertools.zip_longest(*(s.final_word_out for s in states))) return self.product_FiniteStateMachine( @@ -12822,14 +12600,9 @@ def final_function(*states): final_function=final_function, only_accessible_components=only_accessible_components) - def simplification(self): """ - Returns a simplified transducer. - - INPUT: - - Nothing. + Return a simplified transducer. OUTPUT: @@ -13226,10 +12999,10 @@ class is created and is used during the processing. result = super(Transducer, self).process(*args, **options) if (condensed_output and not result or - not options['full_output'] and result is None): - raise ValueError("Invalid input sequence.") + not options['full_output'] and result is None): + raise ValueError("Invalid input sequence.") if condensed_output and len(result) >= 2: - raise ValueError("Found more than one accepting path.") + raise ValueError("Found more than one accepting path.") if condensed_output: return result[0] @@ -13274,7 +13047,7 @@ def _process_convert_output_(self, output_data, **kwargs): return output -#***************************************************************************** +# **************************************************************************** class _FSMTapeCache_(SageObject): @@ -13372,14 +13145,9 @@ def __init__(self, tape_cache_manager, tape, tape_ended, self.tape_cache_manager.append(self) self.cache = tuple(collections.deque() for _ in self.tape) - def _repr_(self): """ - Returns a string representation of ``self``. - - INPUT: - - Nothing. + Return a string representation of ``self``. OUTPUT: @@ -13436,7 +13204,7 @@ def __deepcopy__(self, memo): def deepcopy(self, memo=None): """ - Returns a deepcopy of ``self``. + Return a deepcopy of ``self``. INPUT: @@ -13520,10 +13288,9 @@ def read(self, track_number): return (True, newval) - def finished(self, track_number=None): r""" - Returns whether the tape (or a particular track) has reached an + Return whether the tape (or a particular track) has reached an end, i.e., there are no more letters in the cache and nothing more to read on the original tape. @@ -13534,7 +13301,7 @@ def finished(self, track_number=None): OUTPUT: - ``True`` or ``False``. + ``True`` or ``False`` TESTS:: @@ -13689,7 +13456,7 @@ def preview_word(self, track_number=None, length=1, return_word=False): def compare_to_tape(self, track_number, word): """ - Returns whether it is possible to read ``word`` from the given + Return whether it is possible to read ``word`` from the given track successfully. INPUT: @@ -13700,7 +13467,7 @@ def compare_to_tape(self, track_number, word): OUTPUT: - ``True`` or ``False``. + ``True`` or ``False`` TESTS:: @@ -13950,7 +13717,7 @@ def _transition_possible_test_(self, word_in): for track_number, word in enumerate(word_in_transposed)) -#***************************************************************************** +# **************************************************************************** class _FSMTapeCacheDetectEpsilon_(_FSMTapeCache_): @@ -14022,7 +13789,7 @@ def _transition_possible_test_(self, word_in): return self._transition_possible_epsilon_(word_in) -#***************************************************************************** +# **************************************************************************** class _FSMTapeCacheDetectAll_(_FSMTapeCache_): @@ -14081,7 +13848,7 @@ def compare_to_tape(self, track_number, word): return True -#***************************************************************************** +# **************************************************************************** def tupleofwords_to_wordoftuples(tupleofwords): @@ -14107,7 +13874,7 @@ def tupleofwords_to_wordoftuples(tupleofwords): ....: ([1, 2], [3, 4, 5, 6], [7])) [(1, 3, 7), (2, 4, None), (None, 5, None), (None, 6, None)] """ - return list(zip_longest(*tupleofwords, fillvalue=None)) + return list(itertools.zip_longest(*tupleofwords, fillvalue=None)) def wordoftuples_to_tupleofwords(wordoftuples): @@ -14134,13 +13901,14 @@ def wordoftuples_to_tupleofwords(wordoftuples): """ if not equal(len(t) for t in wordoftuples): raise ValueError("Not all entries of input have the same length.") + def remove_empty_letters(word): return [letter for letter in word if letter is not None] return tuple(remove_empty_letters(word) for word in zip(*wordoftuples)) -#***************************************************************************** +# **************************************************************************** def is_FSMProcessIterator(PI): @@ -14156,7 +13924,7 @@ def is_FSMProcessIterator(PI): return isinstance(PI, FSMProcessIterator) -#***************************************************************************** +# **************************************************************************** class FSMProcessIterator(SageObject, @@ -14412,11 +14180,7 @@ class Current(dict): """ def __repr__(self): """ - Returns a nice representation of ``self``. - - INPUT: - - Nothing. + Return a nice representation of ``self``. OUTPUT: @@ -14440,8 +14204,8 @@ def __repr__(self): """ data = sorted( (state, pos, tape_cache, outputs) - for pos, states in six.iteritems(self) - for state, (tape_cache, outputs) in six.iteritems(states)) + for pos, states in self.items() + for state, (tape_cache, outputs) in states.items()) branch = "branch" if len(data) == 1 else "branches" result = "process (%s %s)" % (len(data), branch) for s, sdata in itertools.groupby(data, lambda x: x[0]): @@ -14717,7 +14481,7 @@ def _push_branches_(self, state, tape_cache, outputs): 'but output is written.' % (state,)) for eps_state, eps_outputs in \ - six.iteritems(state._epsilon_successors_(self.fsm)): + state._epsilon_successors_(self.fsm).items(): if eps_state == state: continue # "eps_state == state" means epsilon cycle @@ -14910,18 +14674,16 @@ def step(current_state, input_tape, outputs): return states_dict = self._current_.pop(heapq.heappop(self._current_positions_)) - for state, branch in six.iteritems(states_dict): + for state, branch in states_dict.items(): step(state, branch.tape_cache, branch.outputs) return self._current_ - next = __next__ - def result(self, format_output=None): """ - Returns the already finished branches during process. + Return the already finished branches during process. INPUT: @@ -14968,7 +14730,7 @@ def result(self, format_output=None): def preview_word(self, track_number=None, length=1, return_word=False): """ - Reads a word from the input tape. + Read a word from the input tape. INPUT: @@ -15023,7 +14785,7 @@ def preview_word(self, track_number=None, length=1, return_word=False): track_number, length, return_word) -#***************************************************************************** +# **************************************************************************** class _FSMProcessIteratorEpsilon_(FSMProcessIterator): @@ -15470,7 +15232,7 @@ def __init__(self, *args, **kwargs): return super(_FSMProcessIteratorAll_, self).__init__(*args, **kwargs) -#***************************************************************************** +# **************************************************************************** @cached_function @@ -15480,14 +15242,6 @@ def setup_latex_preamble(): to the preamble of Latex so that the finite state machines can be drawn nicely. - INPUT: - - Nothing. - - OUTPUT: - - Nothing. - See the section on :ref:`finite_state_machine_LaTeX_output` in the introductory examples of this module. diff --git a/src/sage/combinat/finite_state_machine_generators.py b/src/sage/combinat/finite_state_machine_generators.py index e840ea303c5..ba7d86b0d49 100644 --- a/src/sage/combinat/finite_state_machine_generators.py +++ b/src/sage/combinat/finite_state_machine_generators.py @@ -1990,7 +1990,7 @@ def f(n): if missing_initial_values: raise ValueError( "Missing initial values for %s." % - sorted(list(missing_initial_values))) + sorted(missing_initial_values)) for cycle in recursion_digraph.all_simple_cycles(): assert cycle[0] is cycle[-1] @@ -2020,7 +2020,7 @@ def f(n): if superfluous_initial_values: raise ValueError( "Superfluous initial values for %s." % - sorted(list(superfluous_initial_values))) + sorted(superfluous_initial_values)) for state in T.iter_states(): state.is_final = True diff --git a/src/sage/combinat/free_dendriform_algebra.py b/src/sage/combinat/free_dendriform_algebra.py index 176aa6cf759..32709cfaf7c 100644 --- a/src/sage/combinat/free_dendriform_algebra.py +++ b/src/sage/combinat/free_dendriform_algebra.py @@ -14,7 +14,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ # **************************************************************************** -from six import iteritems from sage.categories.hopf_algebras import HopfAlgebras from sage.combinat.free_module import CombinatorialFreeModule @@ -166,7 +165,7 @@ def __init__(self, R, names=None): self._alphabet = names # Here one would need LabelledBinaryTrees(names) # so that one can restrict the labels to some fixed set - + cat = HopfAlgebras(R).WithBasis().Graded().Connected() CombinatorialFreeModule.__init__(self, R, Trees, latex_prefix="", @@ -252,7 +251,7 @@ def algebra_generators(self): """ Trees = self.basis().keys() return Family(self._alphabet, lambda a: self.monomial(Trees([], a))) - + def change_ring(self, R): """ Return the free dendriform algebra in the same variables over `R`. @@ -824,7 +823,8 @@ def _apply_functor_to_morphism(self, f): def action(x): return codom._from_dict({a: f(b) - for a, b in iteritems(x.monomial_coefficients())}) + for a, b in + x.monomial_coefficients().items()}) return dom.module_morphism(function=action, codomain=codom) def __eq__(self, other): diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index 77ca49304af..41730a2a063 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -11,7 +11,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six.moves import range from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent @@ -29,12 +28,10 @@ from sage.categories.all import Category, Sets, ModulesWithBasis, GradedAlgebrasWithBasis from sage.categories.tensor import tensor import sage.data_structures.blas_dict as blas -from sage.typeset.ascii_art import AsciiArt -from sage.typeset.unicode_art import UnicodeArt +from sage.typeset.ascii_art import AsciiArt, ascii_art +from sage.typeset.unicode_art import UnicodeArt, unicode_art from sage.misc.superseded import deprecation -import six - class CombinatorialFreeModule(UniqueRepresentation, Module, IndexedGenerators): r""" @@ -338,7 +335,7 @@ def element_class(self): EXAMPLES:: sage: A = Algebras(QQ).WithBasis().example(); A - An example of an algebra with basis: + An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field sage: A.element_class.mro() @@ -487,7 +484,6 @@ def _unicode_art_term(self, m): sage: unicode_art(R.one()) # indirect doctest 1 """ - from sage.typeset.unicode_art import UnicodeArt try: if m == self.one_basis(): return UnicodeArt(["1"]) @@ -585,7 +581,8 @@ def _element_constructor_(self, x): sage: QS3.monomial(P([2,3,1])) # indirect doctest [2, 3, 1] - or: + or:: + sage: B = QS3.basis() sage: B[P([2,3,1])] [2, 3, 1] @@ -958,7 +955,7 @@ def _order_key(self, x): """ return self._rank_basis(x) - def from_vector(self, vector): + def from_vector(self, vector, order=None): """ Build an element of ``self`` from a (sparse) vector. @@ -973,8 +970,9 @@ def from_vector(self, vector): sage: a == b True """ - cc = self.get_order() - return self._from_dict({cc[index]: coeff for (index,coeff) in six.iteritems(vector)}) + if order is None: + order = self.get_order() + return self._from_dict({order[index]: coeff for (index,coeff) in vector.items()}) def sum(self, iter_of_elements): """ @@ -1003,7 +1001,7 @@ def linear_combination(self, iter_of_elements_coeff, factor_on_left=True): Return the linear combination `\lambda_1 v_1 + \cdots + \lambda_k v_k` (resp. the linear combination `v_1 \lambda_1 + \cdots + v_k \lambda_k`) where ``iter_of_elements_coeff`` iterates - through the sequence `((\lambda_1, v_1), ..., (\lambda_k, v_k))`. + through the sequence `((v_1, \lambda_1), ..., (v_k, \lambda_k))`. INPUT: @@ -1186,9 +1184,9 @@ def _from_dict(self, d, coerce=False, remove_zeros=True): assert isinstance(d, dict) if coerce: R = self.base_ring() - d = {key: R(coeff) for key, coeff in six.iteritems(d)} + d = {key: R(coeff) for key, coeff in d.items()} if remove_zeros: - d = {key: coeff for key, coeff in six.iteritems(d) if coeff} + d = {key: coeff for key, coeff in d.items() if coeff} return self.element_class(self, d) @@ -1354,11 +1352,16 @@ def _ascii_art_(self, term): sage: R = NonCommutativeSymmetricFunctions(QQ).R() sage: Partitions.options(diagram_str="#", convention="french") - sage: ascii_art(tensor((R[1,2], R[3,1,2]))) + sage: s = ascii_art(tensor((R[1,2], R[3,1,2]))); s R # R # ### ## # ## + + Check that the breakpoints are correct (:trac:`29202`):: + + sage: s._breakpoints + [6] """ if hasattr(self, "_print_options"): symb = self._print_options['tensor_symbol'] @@ -1366,13 +1369,9 @@ def _ascii_art_(self, term): symb = tensor.symbol else: symb = tensor.symbol - it = iter(zip(self._sets, term)) - module, t = next(it) - rpr = module._ascii_art_term(t) - for (module,t) in it: - rpr += AsciiArt([symb], [len(symb)]) - rpr += module._ascii_art_term(t) - return rpr + return ascii_art(*(module._ascii_art_term(t) + for module, t in zip(self._sets, term)), + sep=AsciiArt([symb], breakpoints=[len(symb)])) _ascii_art_term = _ascii_art_ @@ -1382,12 +1381,17 @@ def _unicode_art_(self, term): sage: R = NonCommutativeSymmetricFunctions(QQ).R() sage: Partitions.options(diagram_str="#", convention="french") - sage: unicode_art(tensor((R[1,2], R[3,1,2]))) + sage: s = unicode_art(tensor((R[1,2], R[3,1,2]))); s R # R ┌┐ ┌┬┬┐ ├┼┐ └┴┼┤ └┴┘ ├┼┐ └┴┘ + + Check that the breakpoints are correct (:trac:`29202`):: + + sage: s._breakpoints + [7] """ if hasattr(self, "_print_options"): symb = self._print_options['tensor_symbol'] @@ -1395,13 +1399,9 @@ def _unicode_art_(self, term): symb = tensor.symbol else: symb = tensor.symbol - it = iter(zip(self._sets, term)) - module, t = next(it) - rpr = module._unicode_art_term(t) - for (module, t) in it: - rpr += UnicodeArt([symb], [len(symb)]) - rpr += module._unicode_art_term(t) - return rpr + return unicode_art(*(module._unicode_art_term(t) + for module, t in zip(self._sets, term)), + sep=UnicodeArt([symb], breakpoints=[len(symb)])) _unicode_art_term = _unicode_art_ diff --git a/src/sage/combinat/free_prelie_algebra.py b/src/sage/combinat/free_prelie_algebra.py index ce6abd8f63d..3ca4bd3adbe 100644 --- a/src/sage/combinat/free_prelie_algebra.py +++ b/src/sage/combinat/free_prelie_algebra.py @@ -15,7 +15,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six import string_types from sage.categories.magmatic_algebras import MagmaticAlgebras from sage.categories.lie_algebras import LieAlgebras @@ -177,7 +176,7 @@ def __classcall_private__(cls, R, names=None): True """ if names is not None: - if isinstance(names, string_types) and ',' in names: + if isinstance(names, str) and ',' in names: names = [u for u in names if u != ','] names = Alphabet(names) diff --git a/src/sage/combinat/fully_packed_loop.py b/src/sage/combinat/fully_packed_loop.py index 3f609ba6194..2afdc0b6887 100644 --- a/src/sage/combinat/fully_packed_loop.py +++ b/src/sage/combinat/fully_packed_loop.py @@ -25,7 +25,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import division, print_function -from six import add_metaclass from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.structure.unique_representation import UniqueRepresentation @@ -96,8 +95,8 @@ def _make_color_list(n, colors=None, color_map=None, randomize=False): return colors -@add_metaclass(InheritComparisonClasscallMetaclass) -class FullyPackedLoop(Element): + +class FullyPackedLoop(Element, metaclass=InheritComparisonClasscallMetaclass): r""" A class for fully packed loops. @@ -1155,10 +1154,14 @@ def link_pattern(self): i,j = unrank(k) # initial direction - if i == -1: d = R - elif i == n: d = L - elif j == -1: d = U - elif j == n: d = D + if i == -1: + d = R + elif i == n: + d = L + elif j == -1: + d = U + elif j == n: + d = D # go through the link while True: diff --git a/src/sage/combinat/gelfand_tsetlin_patterns.py b/src/sage/combinat/gelfand_tsetlin_patterns.py index 52d39ddb8c2..94e9d037fd6 100644 --- a/src/sage/combinat/gelfand_tsetlin_patterns.py +++ b/src/sage/combinat/gelfand_tsetlin_patterns.py @@ -37,8 +37,6 @@ # https://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from six.moves import range -from six import add_metaclass from sage.structure.parent import Parent from sage.structure.list_clone import ClonableArray @@ -55,8 +53,8 @@ from sage.misc.all import prod -@add_metaclass(InheritComparisonClasscallMetaclass) -class GelfandTsetlinPattern(ClonableArray): +class GelfandTsetlinPattern(ClonableArray, + metaclass=InheritComparisonClasscallMetaclass): r""" A Gelfand-Tsetlin (sometimes written as Gelfand-Zetlin or Gelfand-Cetlin) pattern. They were originally defined in [GC50]_. @@ -509,6 +507,75 @@ def Tokuyama_coefficient(self, name='t'): return R.zero() return (t+1)**(self.number_of_special_entries()) * t**(self.number_of_boxes()) + def bender_knuth_involution(self,i): + r""" + Return the image of ``self`` under the `i`-th Bender-Knuth involution. + + If the triangle ``self`` has size `n` then this is defined for `0 < i < n`. + + The entries of ``self`` can take values in any ordered ring. Usually, + this will be the integers but can also be the rationals or the real numbers. + + This implements the construction of the Bender-Knuth involution using toggling + due to Berenstein-Kirillov. + + This agrees with the Bender-Knuth involution on semistandard tableaux. + + EXAMPLES:: + + sage: G = GelfandTsetlinPattern([[5,3,2,1,0],[4,3,2,0],[4,2,1],[3,2],[3]]) + sage: G.bender_knuth_involution(2) + [[5, 3, 2, 1, 0], [4, 3, 2, 0], [4, 2, 1], [4, 1], [3]] + + sage: G = GelfandTsetlinPattern([[3,2,0],[2.2,0],[2]]) + sage: G.bender_knuth_involution(2) + [[3, 2, 0], [2.80000000000000, 2], [2]] + + TESTS:: + + sage: all(all( G.bender_knuth_involution(i).to_tableau() == G.to_tableau().bender_knuth_involution(i) + ....: for i in range(1,len(G)) ) for G in GelfandTsetlinPatterns(top_row=[3,3,3,0,0])) + True + + sage: G = GelfandTsetlinPattern([[2,1,0],[1,0],[0]]) + sage: G.bender_knuth_involution(0) + Traceback (most recent call last): + ... + ValueError: must have 0 < 0 < 3 + sage: G.bender_knuth_involution(3) + Traceback (most recent call last): + ... + ValueError: must have 0 < 3 < 3 + + """ + #from copy import copy + n = len(self) + + def toggle(i,j): + """ + Return the toggle of entry 'G[i][j]' in a Gelfand-Tsetlin pattern, 'G'. + """ + if i == n-1: + return self[n-2][0]+self[n-2][1]-self[n-1][0] + + if j == 0: + left = self[i-1][0] + else: + left = min(self[i-1][j], self[i+1][j-1]) + if j == n-i-1: + right = self[i-1][j+1] + else: + right = max(self[i-1][j+1], self[i+1][j]) + + return left + right - self[i][j] + + if not 0 < i < n: + raise ValueError(f"must have 0 < {i} < {n}") + r = n-i + P = self.parent() + data = [list(row) for row in self] + data[r] = [toggle(r,s) for s in range(i)] + return P.element_class(P, data) class GelfandTsetlinPatterns(UniqueRepresentation, Parent): """ diff --git a/src/sage/combinat/gray_codes.py b/src/sage/combinat/gray_codes.py index d30f24d6847..e19890f61b7 100644 --- a/src/sage/combinat/gray_codes.py +++ b/src/sage/combinat/gray_codes.py @@ -1,19 +1,10 @@ r""" Gray codes -REFERENCES: - -.. [Knuth-TAOCP2A] \D. Knuth "The art of computer programming", fascicules 2A, - "generating all n-tuples" - -.. [Knuth-TAOCP3A] \D. Knuth "The art of computer programming", fascicule 3A - "generating all combinations" - Functions --------- """ from __future__ import print_function -from six.moves import range def product(m): @@ -26,8 +17,8 @@ def product(m): apply the increment ``i`` at the position ``p``. By construction, the increment is either ``+1`` or ``-1``. - This is algorithm H in [Knuth-TAOCP2A]_: loopless reflected mixed-radix Gray - generation. + This is algorithm H in [Knu2011]_ Section 7.2.1.1, "Generating All + `n`-Tuples": loopless reflected mixed-radix Gray generation. INPUT: @@ -124,7 +115,7 @@ def combinations(n,t): The ground set is always `\{0, 1, ..., n-1\}`. Note that ``n`` can be infinity in that algorithm. - See [Knuth-TAOCP3A]_. + See [Knu2011]_ Section 7.2.1.3, "Generating All Combinations". INPUT: @@ -230,7 +221,7 @@ def _revolving_door_odd(n,t): sage: sum(1 for _ in _revolving_door_odd(10,5)) == binomial(10,5) - 1 True """ - # note: the numerotation of the steps below follows Kunth TAOCP + # note: the numbering of the steps below follows Knuth TAOCP c = list(range(t)) + [n] # the combination (ordered list of numbers of length t+1) while True: @@ -275,7 +266,7 @@ def _revolving_door_even(n,t): sage: sum(1 for _ in _revolving_door_even(12,6)) == binomial(12,6) - 1 True """ - # note: the numerotation of the setps below follows Kunth TAOCP + # note: the numbering of the steps below follows Knuth TAOCP c = list(range(t)) + [n] # the combination (ordered list of numbers of length t+1) diff --git a/src/sage/combinat/growth.py b/src/sage/combinat/growth.py index 141fe345d4b..d26bf37e0b5 100644 --- a/src/sage/combinat/growth.py +++ b/src/sage/combinat/growth.py @@ -471,7 +471,9 @@ # https://www.gnu.org/licenses/ # *************************************************************************** -from six.moves import zip_longest +from copy import copy +from itertools import zip_longest + from sage.structure.sage_object import SageObject from sage.structure.unique_representation import UniqueRepresentation from sage.combinat.posets.posets import Poset @@ -485,7 +487,6 @@ from sage.combinat.core import Core, Cores from sage.combinat.k_tableau import WeakTableau, StrongTableau from sage.combinat.shifted_primed_tableau import ShiftedPrimedTableau -from copy import copy from sage.graphs.digraph import DiGraph def _make_partition(l): diff --git a/src/sage/combinat/integer_list_old.py b/src/sage/combinat/integer_list_old.py index 885ae4126b6..2912a2d6e0c 100644 --- a/src/sage/combinat/integer_list_old.py +++ b/src/sage/combinat/integer_list_old.py @@ -31,7 +31,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import -from six.moves import builtins from sage.arith.all import binomial from sage.rings.integer_ring import ZZ @@ -417,7 +416,7 @@ def iterator(n, min_length, max_length, floor, ceiling, min_slope, max_slope): succ = lambda x: next(x, min_length, max_length, floor, ceiling, min_slope, max_slope) # Handle the case where n is a list of integers - if isinstance(n, builtins.list): + if isinstance(n, list): for i in range(n[0], min(n[1]+1, upper_bound(min_length, max_length, floor, ceiling, min_slope, max_slope))): for el in iterator(i, min_length, max_length, floor, ceiling, min_slope, max_slope): yield el @@ -912,7 +911,7 @@ def __init__(self, # Is ``floor`` an iterable? # Not ``floor[:]`` because we want ``self.floor_list`` # mutable, and applying [:] to a tuple gives a tuple. - self.floor_list = builtins.list(floor) + self.floor_list = list(floor) # Make sure the floor list will make the list satisfy the constraints if min_slope != float('-inf'): for i in range(1, len(self.floor_list)): @@ -931,7 +930,7 @@ def __init__(self, else: try: # Is ``ceiling`` an iterable? - self.ceiling_list = builtins.list(ceiling) + self.ceiling_list = list(ceiling) # Make sure the ceiling list will make the list satisfy the constraints if max_slope != float('+inf'): for i in range(1, len(self.ceiling_list)): @@ -1211,6 +1210,6 @@ def __contains__(self, v): sage: all(v in C for v in C) True """ - if isinstance(v, self.element_class) or isinstance(v, builtins.list): + if isinstance(v, self.element_class) or isinstance(v, list): return is_a(v, *(self.build_args())) and sum(v) in self.n_range return False diff --git a/src/sage/combinat/integer_lists/lists.py b/src/sage/combinat/integer_lists/lists.py index 924cd0647dd..8795add6e56 100644 --- a/src/sage/combinat/integer_lists/lists.py +++ b/src/sage/combinat/integer_lists/lists.py @@ -25,7 +25,6 @@ from sage.structure.list_clone import ClonableArray from sage.structure.parent import Parent from sage.combinat.integer_lists.base import IntegerListsBackend -from six import get_method_function class IntegerList(ClonableArray): @@ -174,9 +173,9 @@ def __eq__(self, other): a = self._element_constructor_ b = other._element_constructor_ if ismethod(a): - a = get_method_function(a) + a = a.__func__ if ismethod(b): - b = get_method_function(b) + b = b.__func__ return a == b def __ne__(self, other): @@ -208,7 +207,7 @@ def __hash__(self): """ a = self._element_constructor_ if ismethod(a): - a = get_method_function(a) + a = a.__func__ return hash((self.__class__, a)) def __iter__(self): diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index 386176ffd51..415ef8d43b0 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -28,11 +28,10 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import, division -from six.moves import range -from six import add_metaclass from sage.combinat.integer_lists import IntegerListsLex from itertools import product +import numbers from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -448,8 +447,7 @@ def check(self): raise ValueError("all entries must be non-negative") -@add_metaclass(ClasscallMetaclass) -class IntegerVectors(Parent): +class IntegerVectors(Parent, metaclass=ClasscallMetaclass): """ The class of (non-negative) integer vectors. @@ -571,7 +569,7 @@ class IntegerVectors(Parent): [[4], [3, 1], [2, 2], [1, 3], [0, 4]] .. SEEALSO:: - + :class: `sage.combinat.integer_lists.invlex.IntegerListsLex`. """ @staticmethod @@ -592,6 +590,13 @@ def __classcall_private__(cls, n=None, k=None, **kwargs): Traceback (most recent call last): ... ValueError: k and length both specified + + :trac:`29524`:: + + sage: IntegerVectors(3, 3/1) + Traceback (most recent call last): + ... + TypeError: 'k' must be an integer or a tuple, got Rational """ if 'length' in kwargs: if k is not None: @@ -607,12 +612,12 @@ def __classcall_private__(cls, n=None, k=None, **kwargs): if n is None: return IntegerVectors_k(k) - try: + if isinstance(k, numbers.Integral): + return IntegerVectors_nk(n, k) + elif isinstance(k, (tuple, list)): return IntegerVectors_nnondescents(n, tuple(k)) - except TypeError: - pass - - return IntegerVectors_nk(n, k) + else: + raise TypeError("'k' must be an integer or a tuple, got {}".format(type(k).__name__)) def __init__(self, category=None): """ diff --git a/src/sage/combinat/integer_vectors_mod_permgroup.py b/src/sage/combinat/integer_vectors_mod_permgroup.py index 98f3c656469..707fd871cf9 100644 --- a/src/sage/combinat/integer_vectors_mod_permgroup.py +++ b/src/sage/combinat/integer_vectors_mod_permgroup.py @@ -1,14 +1,14 @@ r""" Integer vectors modulo the action of a permutation group """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2010-12 Nicolas Borie # # Distributed under the terms of the GNU General Public License (GPL) # # The full text of the GPL is available at: -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from __future__ import print_function from sage.structure.unique_representation import UniqueRepresentation @@ -18,12 +18,13 @@ from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.structure.list_clone import ClonableIntArray -from sage.combinat.backtrack import SearchForest +from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest from sage.combinat.enumeration_mod_permgroup import is_canonical, orbit, canonical_children, canonical_representative_of_orbit_of from sage.combinat.integer_vector import IntegerVectors + class IntegerVectorsModPermutationGroup(UniqueRepresentation): r""" Returns an enumerated set containing integer vectors which are @@ -229,7 +230,7 @@ def __classcall__(cls, G, sum=None, max_part=None, sgs=None): assert (max_part == NN(max_part)) return IntegerVectorsModPermutationGroup_with_constraints(G, sum, max_part, sgs=sgs) -class IntegerVectorsModPermutationGroup_All(UniqueRepresentation, SearchForest): +class IntegerVectorsModPermutationGroup_All(UniqueRepresentation, RecursivelyEnumeratedSet_forest): r""" A class for integer vectors enumerated up to the action of a permutation group. @@ -276,7 +277,7 @@ def __init__(self, G, sgs=None): Category of infinite enumerated quotients of sets sage: TestSuite(I).run() """ - SearchForest.__init__(self, algorithm = 'breadth', category = InfiniteEnumeratedSets().Quotients()) + RecursivelyEnumeratedSet_forest.__init__(self, algorithm = 'breadth', category = InfiniteEnumeratedSets().Quotients()) self._permgroup = G self.n = G.degree() @@ -368,7 +369,7 @@ def roots(self): r""" Returns the root of generation of ``self``. This method is required to build the tree structure of ``self`` which - inherits from the class :class:`~sage.combinat.backtrack.SearchForest`. + inherits from the class :class:`~sage.sets.recursively_enumerated_set.RecursivelyEnumeratedSet_forest`. EXAMPLES:: @@ -382,7 +383,7 @@ def children(self, x): r""" Returns the list of children of the element ``x``. This method is required to build the tree structure of ``self`` which - inherits from the class :class:`~sage.combinat.backtrack.SearchForest`. + inherits from the class :class:`~sage.sets.recursively_enumerated_set.RecursivelyEnumeratedSet_forest`. EXAMPLES:: @@ -557,7 +558,7 @@ def check(self): assert self.parent().is_canonical(self) -class IntegerVectorsModPermutationGroup_with_constraints(UniqueRepresentation, SearchForest): +class IntegerVectorsModPermutationGroup_with_constraints(UniqueRepresentation, RecursivelyEnumeratedSet_forest): r""" This class models finite enumerated sets of integer vectors with constraint enumerated up to the action of a permutation group. @@ -598,7 +599,7 @@ def __init__(self, G, d, max_part, sgs=None): sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6, max_part=4) """ - SearchForest.__init__(self, algorithm = 'breadth', category = (FiniteEnumeratedSets(), FiniteEnumeratedSets().Quotients())) + RecursivelyEnumeratedSet_forest.__init__(self, algorithm = 'breadth', category = (FiniteEnumeratedSets(), FiniteEnumeratedSets().Quotients())) self._permgroup = G self.n = G.degree() self._sum = d @@ -639,7 +640,7 @@ def roots(self): Returns the root of generation of ``self``.This method is required to build the tree structure of ``self`` which inherits from the class - :class:`~sage.combinat.backtrack.SearchForest`. + :class:`~sage.sets.recursively_enumerated_set.RecursivelyEnumeratedSet_forest`. EXAMPLES:: @@ -654,7 +655,7 @@ def children(self, x): Returns the list of children of the element ``x``. This method is required to build the tree structure of ``self`` which inherits from the class - :class:`~sage.combinat.backtrack.SearchForest`. + :class:`~sage.sets.recursively_enumerated_set.RecursivelyEnumeratedSet_forest`. EXAMPLES:: @@ -751,7 +752,7 @@ def __iter__(self): if self._max_part < 0: return self.elements_of_depth_iterator(self._sum) else: - SF = SearchForest((self([0]*(self.n), check=False),), + SF = RecursivelyEnumeratedSet_forest((self([0]*(self.n), check=False),), lambda x : [self(y, check=False) for y in canonical_children(self._sgs, x, self._max_part)], algorithm = 'breadth') if self._sum is None: @@ -963,5 +964,5 @@ def check(self): if self.parent()._sum is not None: assert sum(self) == self.parent()._sum, '%s should be a integer vector of sum %s'%(self, self.parent()._sum) if self.parent()._max_part >= 0: - assert max(self) <= self.parent()._max_part, 'Entries of %s must be inferiors to %s'%(self, self.parent()._max_part) + assert max(self) <= self.parent()._max_part, 'Entries of %s must be inferior to %s'%(self, self.parent()._max_part) assert self.parent().is_canonical(self) diff --git a/src/sage/combinat/interval_posets.py b/src/sage/combinat/interval_posets.py index de37d92a461..2335bc71276 100644 --- a/src/sage/combinat/interval_posets.py +++ b/src/sage/combinat/interval_posets.py @@ -31,8 +31,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six.moves import range -from six import add_metaclass from sage.categories.enumerated_sets import EnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -61,8 +59,8 @@ from sage.graphs.digraph import DiGraph -@add_metaclass(InheritComparisonClasscallMetaclass) -class TamariIntervalPoset(Element): +class TamariIntervalPoset(Element, + metaclass=InheritComparisonClasscallMetaclass): r""" The class of Tamari interval-posets. diff --git a/src/sage/combinat/k_tableau.py b/src/sage/combinat/k_tableau.py index 8770fb9c464..88233da3c29 100644 --- a/src/sage/combinat/k_tableau.py +++ b/src/sage/combinat/k_tableau.py @@ -30,7 +30,6 @@ # http://www.gnu.org/licenses/ #**************************************************************************** from __future__ import print_function, absolute_import -from six import add_metaclass, iteritems from sage.structure.unique_representation import UniqueRepresentation from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -259,8 +258,8 @@ def WeakTableaux(k, shape , weight, representation = "core"): raise NotImplementedError("The representation option needs to be 'core', 'bounded', or 'factorized_permutation'") #Abstract class for the elements of weak tableau -@add_metaclass(InheritComparisonClasscallMetaclass) -class WeakTableau_abstract(ClonableList): +class WeakTableau_abstract(ClonableList, + metaclass=InheritComparisonClasscallMetaclass): r""" Abstract class for the various element classes of WeakTableau. """ @@ -1020,7 +1019,7 @@ def list_of_standard_cells(self): r = self[0].count(1) - i - 1 for v in range(1,mu[i]): D = self.dictionary_of_coordinates_at_residues(v+1) - new_D = {a: b for (a, b) in iteritems(D) + new_D = {a: b for (a, b) in D.items() if all(x not in already_used for x in b)} r = (r - min([self.k+1 - (x-r)%(self.k+1) for x in new_D]))%(self.k+1) standard_cells.append(new_D[r][-1]) @@ -1277,7 +1276,7 @@ def __init__(self, k, shape, weight): sage: TestSuite(T).run() """ self.k = k - self._skew = shape[1]!=[] + self._skew = bool(shape[1]) self._outer_shape = shape[0] self._inner_shape = shape[1] self._shape = (self._outer_shape, self._inner_shape) @@ -1743,7 +1742,7 @@ def __init__(self, k, shape, weight): sage: TestSuite(T).run() """ self.k = k - self._skew = shape[1]!=[] + self._skew = bool(shape[1]) self._outer_shape = Partition(shape[0]) self._inner_shape = Partition(shape[1]) self._shape = (self._outer_shape, self._inner_shape) @@ -2164,7 +2163,7 @@ def __init__(self, k, shape, weight): sage: TestSuite(T).run() # long time """ self.k = k - self._skew = shape[1]!=[] + self._skew = bool(shape[1]) self._outer_shape = Core(shape[0], k+1) self._inner_shape = Core(shape[1], k+1) self._shape = (self._outer_shape, self._inner_shape) @@ -2206,8 +2205,7 @@ def __iter__(self): ######## END weak tableaux BEGIN strong tableaux -@add_metaclass(InheritComparisonClasscallMetaclass) -class StrongTableau(ClonableList): +class StrongTableau(ClonableList, metaclass=InheritComparisonClasscallMetaclass): r""" A (standard) strong `k`-tableau is a (saturated) chain in Bruhat order. @@ -4056,7 +4054,7 @@ def shape(self): sage: StrongTableaux( 4, [[2,1], [1]] ).shape() ([2, 1], [1]) """ - if self._inner_shape != []: + if self._inner_shape: return (self._outer_shape, self._inner_shape) return self._outer_shape diff --git a/src/sage/combinat/knutson_tao_puzzles.py b/src/sage/combinat/knutson_tao_puzzles.py index 9d18df326f1..efa2f6d6800 100644 --- a/src/sage/combinat/knutson_tao_puzzles.py +++ b/src/sage/combinat/knutson_tao_puzzles.py @@ -1315,7 +1315,8 @@ def tikztriangle_fill(color, k, d, i, *args): def tikztriangle_edges(color, k, d, i, label1, label2, label3): s = "" - if i == 1: return s + if i == 1: + return s tikzcmd = r"""\draw[color=%s, fill=none] (%s, %s) -- (%s, %s);""" + "\n" if edge_colors[label1]: s += tikzcmd % (edge_colors[label1], k-1, d-1, k+1, d-1) diff --git a/src/sage/combinat/lr_tableau.py b/src/sage/combinat/lr_tableau.py index ce9691c56d1..6d14d998066 100644 --- a/src/sage/combinat/lr_tableau.py +++ b/src/sage/combinat/lr_tableau.py @@ -27,6 +27,8 @@ # http://www.gnu.org/licenses/ #**************************************************************************** +from itertools import zip_longest + from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.combinat.tableau import SemistandardTableau, SemistandardTableaux from sage.combinat.partition import Partition, Partitions @@ -300,6 +302,5 @@ def _tableau_join(t1, t2, shift=0): sage: _tableau_join([[1,2]],[[None,None,2],[3]],shift=5) [[1, 2, 7], [8]] """ - from six.moves import zip_longest return [[e1 for e1 in row1] + [e2+shift for e2 in row2 if e2 is not None] for (row1, row2) in zip_longest(t1, t2, fillvalue=[])] diff --git a/src/sage/combinat/matrices/dancing_links.pyx b/src/sage/combinat/matrices/dancing_links.pyx index d1e407c8933..c62780c5d74 100644 --- a/src/sage/combinat/matrices/dancing_links.pyx +++ b/src/sage/combinat/matrices/dancing_links.pyx @@ -91,6 +91,7 @@ cdef extern from "dancing_links_c.h": int search_is_started() int search() +from sage.misc.cachefunc import cached_method cdef class dancing_linksWrapper: r""" @@ -891,6 +892,247 @@ cdef class dancing_linksWrapper: indices = [i for (i,row) in enumerate(self._rows) if column in row] return sum(val for (args_kwds, val) in nb_sol(indices)) + @cached_method + def to_sat_solver(self, solver=None): + r""" + Return the SAT solver solving an equivalent problem. + + Note that row index `i` in the dancing links solver corresponds to + the boolean variable index `ì+1` for the SAT solver to avoid + the variable index `0`. + + See also :mod:`sage.sat.solvers.satsolver`. + + INPUT: + + - ``solver`` -- string or ``None`` (default: ``None``), + possible values include ``'picosat'``, ``'cryptominisat'``, + ``'LP'``, ``'glucose'``, ``'glucose-syrup'``. + + OUTPUT: + + SAT solver instance + + EXAMPLES:: + + sage: from sage.combinat.matrices.dancing_links import dlx_solver + sage: rows = [[0,1,2], [0,2], [1], [3]] + sage: x = dlx_solver(rows) + sage: s = x.to_sat_solver() + + Using some optional SAT solvers:: + + sage: x.to_sat_solver('cryptominisat') # optional - cryptominisat + CryptoMiniSat solver: 4 variables, 7 clauses. + + """ + from sage.sat.solvers.satsolver import SAT + s = SAT(solver) + + # Note that row number i is associated to SAT variable i+1 to + # avoid a variable zero + columns = [[] for _ in range(self.ncols())] + for i,row in enumerate(self.rows(), start=1): + for a in row: + columns[a].append(i) + + # At least one 1 in each column + for clause in columns: + s.add_clause(clause) + + # At most one 1 in each column + import itertools + for clause in columns: + for p,q in itertools.combinations(clause, 2): + sub_clause = [-p,-q] + s.add_clause(sub_clause) + + return s + + def one_solution_using_sat_solver(self, solver=None): + r""" + Return a solution found using a SAT solver. + + INPUT: + + - ``solver`` -- string or ``None`` (default: ``None``), + possible values include ``'picosat'``, ``'cryptominisat'``, + ``'LP'``, ``'glucose'``, ``'glucose-syrup'``. + + OUTPUT: + + list of rows or ``None`` if no solution is found + + .. NOTE:: + + When comparing the time taken by method `one_solution`, + have in mind that `one_solution_using_sat_solver` first + creates the SAT solver instance from the dancing links + solver. This copy of data may take many seconds depending on + the size of the problem. + + EXAMPLES:: + + sage: from sage.combinat.matrices.dancing_links import dlx_solver + sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]] + sage: d = dlx_solver(rows) + sage: solutions = [[0,1], [2,3], [4,5]] + sage: d.one_solution_using_sat_solver() in solutions + True + + Using optional solvers:: + + sage: s = d.one_solution_using_sat_solver('glucose') # optional - glucose + sage: s in solutions # optional - glucose + True + + When no solution is found:: + + sage: rows = [[0,1,2], [2,3,4,5], [0,1,2,3]] + sage: d = dlx_solver(rows) + sage: d.one_solution_using_sat_solver() is None + True + + """ + sat_solver = self.to_sat_solver(solver) + solution = sat_solver() + if not solution: + return None + return [key for (key,val) in enumerate(solution, start=-1) if val] + + @cached_method + def to_milp(self, solver=None): + r""" + Return the mixed integer linear program (MILP) representing an + equivalent problem. + + See also :mod:`sage.numerical.mip.MixedIntegerLinearProgram`. + + INPUT: + + - ``solver`` -- string or ``None`` (default: ``None``), possible + values include ``'GLPK'``, ``'GLPK/exact'``, ``'Coin'``, + ``'CPLEX'``, ``'Gurobi'``, ``'CVXOPT'``, ``'PPL'``, + ``'InteractiveLP'``. + + OUTPUT: + + - MixedIntegerLinearProgram instance + - MIPVariable of dimension 1 + + EXAMPLES:: + + sage: from sage.combinat.matrices.dancing_links import dlx_solver + sage: rows = [[0,1,2], [0,2], [1], [3]] + sage: d = dlx_solver(rows) + sage: p,x = d.to_milp() + sage: p + Boolean Program (no objective, 4 variables, 4 constraints) + sage: x + MIPVariable of dimension 1 + + In the reduction, the boolean variable x_i is True if and only if + the i-th row is in the solution:: + + sage: p.show() + Maximization: + + + Constraints: + one 1 in 0-th column: 1.0 <= x_0 + x_1 <= 1.0 + one 1 in 1-th column: 1.0 <= x_0 + x_2 <= 1.0 + one 1 in 2-th column: 1.0 <= x_0 + x_1 <= 1.0 + one 1 in 3-th column: 1.0 <= x_3 <= 1.0 + Variables: + x_0 is a boolean variable (min=0.0, max=1.0) + x_1 is a boolean variable (min=0.0, max=1.0) + x_2 is a boolean variable (min=0.0, max=1.0) + x_3 is a boolean variable (min=0.0, max=1.0) + + Using some optional MILP solvers:: + + sage: d.to_milp('gurobi') # optional - gurobi sage_numerical_backends_gurobi + (Boolean Program (no objective, 4 variables, 4 constraints), + MIPVariable of dimension 1) + + """ + from sage.numerical.mip import MixedIntegerLinearProgram + p = MixedIntegerLinearProgram(solver=solver) + + # x[i] == True iff i-th dlx row is in the solution + x = p.new_variable(binary=True, indices=range(self.nrows())) + + # Construction of the columns (transpose of the rows) + columns = [[] for _ in range(self.ncols())] + for i,row in enumerate(self.rows()): + for a in row: + columns[a].append(i) + + # Constraints: exactly one 1 in each column + for j,column in enumerate(columns): + S = p.sum(x[a] for a in column) + name = "one 1 in {}-th column".format(j) + p.add_constraint(S==1, name=name) + + return p,x + + def one_solution_using_milp_solver(self, solver=None): + r""" + Return a solution found using a MILP solver. + + INPUT: + + - ``solver`` -- string or ``None`` (default: ``None``), possible + values include ``'GLPK'``, ``'GLPK/exact'``, ``'Coin'``, + ``'CPLEX'``, ``'Gurobi'``, ``'CVXOPT'``, ``'PPL'``, + ``'InteractiveLP'``. + + OUTPUT: + + list of rows or ``None`` if no solution is found + + .. NOTE:: + + When comparing the time taken by method `one_solution`, have in + mind that `one_solution_using_milp_solver` first creates (and + caches) the MILP solver instance from the dancing links solver. + This copy of data may take many seconds depending on the size + of the problem. + + EXAMPLES:: + + sage: from sage.combinat.matrices.dancing_links import dlx_solver + sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]] + sage: d = dlx_solver(rows) + sage: solutions = [[0,1], [2,3], [4,5]] + sage: d.one_solution_using_milp_solver() in solutions + True + + Using optional solvers:: + + sage: s = d.one_solution_using_milp_solver('gurobi') # optional - gurobi sage_numerical_backends_gurobi + sage: s in solutions # optional - gurobi sage_numerical_backends_gurobi + True + + When no solution is found:: + + sage: rows = [[0,1,2], [2,3,4,5], [0,1,2,3]] + sage: d = dlx_solver(rows) + sage: d.one_solution_using_milp_solver() is None + True + + """ + from sage.numerical.mip import MIPSolverException + p,x = self.to_milp(solver) + try: + p.solve() + except MIPSolverException: + return None + else: + soln = p.get_values(x) + support = sorted(key for key in soln if soln[key]) + return support + def dlx_solver(rows): """ Internal-use wrapper for the dancing links C++ code. diff --git a/src/sage/combinat/matrices/dlxcpp.py b/src/sage/combinat/matrices/dlxcpp.py index d85380081e8..7dffdb0792e 100644 --- a/src/sage/combinat/matrices/dlxcpp.py +++ b/src/sage/combinat/matrices/dlxcpp.py @@ -79,8 +79,8 @@ def DLXCPP(rows): sage: [x for x in DLXCPP(rows)] [[3, 0], [3, 1, 2]] """ - - if len(rows) == 0: return + if not rows: + return x = dlx_solver(rows) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 7a2567fe557..45fb4cee9d9 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -55,9 +55,7 @@ #***************************************************************************** from __future__ import print_function -from six.moves import range -from six import itervalues -from six.moves.urllib.request import urlopen +from urllib.request import urlopen from sage.rings.integer_ring import ZZ from sage.matrix.constructor import matrix, block_matrix, block_diagonal_matrix, diagonal_matrix @@ -303,7 +301,7 @@ def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): prod = (M*M.transpose()).dict() if (len(prod) != n or - set(itervalues(prod)) != {n} or + set(prod.values()) != {n} or any((i, i) not in prod for i in range(n))): if verbose: print("The product M*M.transpose() is not equal to nI") @@ -653,8 +651,8 @@ def true(): elif ( e == 1 and not sqn is None and sqn%4 == 2 and - True == strongly_regular_graph(sqn-1,(sqn-2)//2,(sqn-6)//4, - existence=True) and + strongly_regular_graph(sqn-1,(sqn-2)//2,(sqn-6)//4, + existence=True) is True and is_prime_power(ZZ(sqn+1))): if existence: return true() @@ -666,8 +664,8 @@ def true(): for n1,e1 in product(divisors(n)[1:-1],[-1,1]): e2 = e1*e n2 = n//n1 - if (regular_symmetric_hadamard_matrix_with_constant_diagonal(n1,e1,existence=True) and - regular_symmetric_hadamard_matrix_with_constant_diagonal(n2,e2,existence=True)): + if (regular_symmetric_hadamard_matrix_with_constant_diagonal(n1,e1,existence=True) is True and + regular_symmetric_hadamard_matrix_with_constant_diagonal(n2,e2,existence=True)) is True: if existence: return true() M1 = regular_symmetric_hadamard_matrix_with_constant_diagonal(n1,e1) @@ -1122,7 +1120,7 @@ def true(): M = hadamard_matrix_paleyI(n, normalize=False) elif n % 8 == 0: - if skew_hadamard_matrix(n//2,existence=True): # (Lemma 14.1.6 in [Ha83]_) + if skew_hadamard_matrix(n//2,existence=True) is True: # (Lemma 14.1.6 in [Ha83]_) if existence: return true() H = skew_hadamard_matrix(n//2,check=False) @@ -1132,7 +1130,7 @@ def true(): for d in divisors(n)[2:-2]: # skip 1, 2, n/2, and n n1 = n//d if is_prime_power(d - 1) and (d % 4 == 0) and (n1 % 4 == 0)\ - and skew_hadamard_matrix(n1,existence=True): + and skew_hadamard_matrix(n1,existence=True) is True: if existence: return true() H = skew_hadamard_matrix(n1, check=False)-I(n1) @@ -1145,7 +1143,7 @@ def true(): M = A.tensor_product(I(n1))+(U*A).tensor_product(H) break if M is None: # try Williamson-Goethals-Seidel construction - if GS_skew_hadamard_smallcases(n, existence=True): + if GS_skew_hadamard_smallcases(n, existence=True) is True: if existence: return true() M = GS_skew_hadamard_smallcases(n) diff --git a/src/sage/combinat/matrices/latin.py b/src/sage/combinat/matrices/latin.py index 97dcce10ae4..5d85d559ac4 100644 --- a/src/sage/combinat/matrices/latin.py +++ b/src/sage/combinat/matrices/latin.py @@ -128,7 +128,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range from sage.matrix.all import matrix from sage.rings.all import ZZ diff --git a/src/sage/combinat/misc.py b/src/sage/combinat/misc.py index 666b7df513b..6e95cc23d14 100644 --- a/src/sage/combinat/misc.py +++ b/src/sage/combinat/misc.py @@ -17,7 +17,6 @@ #***************************************************************************** from __future__ import print_function -from six.moves import range from sage.misc.all import prod class DoublyLinkedList(): diff --git a/src/sage/combinat/multiset_partition_into_sets_ordered.py b/src/sage/combinat/multiset_partition_into_sets_ordered.py index 792e884c62b..46b317565d3 100755 --- a/src/sage/combinat/multiset_partition_into_sets_ordered.py +++ b/src/sage/combinat/multiset_partition_into_sets_ordered.py @@ -62,8 +62,6 @@ # **************************************************************************** from __future__ import absolute_import, division -from six.moves import range -from six import add_metaclass, iteritems from functools import reduce from itertools import chain @@ -96,8 +94,9 @@ from sage.combinat.crystals.letters import CrystalOfLetters as Letters from sage.combinat.root_system.cartan_type import CartanType -@add_metaclass(InheritComparisonClasscallMetaclass) -class OrderedMultisetPartitionIntoSets(ClonableArray): + +class OrderedMultisetPartitionIntoSets(ClonableArray, + metaclass=InheritComparisonClasscallMetaclass): r""" Ordered Multiset Partition into sets @@ -192,7 +191,7 @@ def __init__(self, parent, data): if not _has_nonempty_sets(co): raise ValueError("cannot view %s as an ordered partition of %s"%(co, parent._Xtup)) - ClonableArray.__init__(self, parent, [frozenset(list(k)) for k in co]) + ClonableArray.__init__(self, parent, [frozenset(k) for k in co]) self._multiset = _get_multiset(co) self._weight = _get_weight(self._multiset) self._order = sum(len(block) for block in self) @@ -1403,7 +1402,7 @@ def __classcall_private__(self, *args, **constraints): # Should be a 'dictionary' of letter-frequencies, but accept a weak composition w = constraints["weight"] if not isinstance(w, dict): - # make sure we didn't receive ``iteritems(some_dict)`` + # make sure we didn't receive ``some_dict.items()`` if len(w) > 0 and isinstance(w[0], (list, tuple)): w = dict(w) else: @@ -1411,7 +1410,7 @@ def __classcall_private__(self, *args, **constraints): if not all((a in ZZ and a > 0) for a in w.values()): raise ValueError("%s must be a dictionary of letter-frequencies or a weak composition"%w) else: - constraints["weight"] = tuple(iteritems(w)) + constraints["weight"] = tuple(w.items()) if "alphabet" in constraints: A = constraints["alphabet"] @@ -1454,7 +1453,7 @@ def __classcall_private__(self, *args, **constraints): suff = "" offenses = str(over_determined.pop()) raise ValueError("cannot pass multiset as first argument and %s as keyword argument%s" % (offenses, suff)) - X_items = tuple(iteritems(X)) + X_items = tuple(X.items()) if constraints == {}: return OrderedMultisetPartitionsIntoSets_X(X_items) else: @@ -1560,7 +1559,7 @@ def __init__(self, is_finite=None, **constraints): # pop keys with empty values, with the exception of 'size' or 'order' self.constraints = {} - for (key,val) in iteritems(constraints): + for (key,val) in constraints.items(): if val: self.constraints[key] = val elif key in ("size", "order", "length") and val is not None: @@ -1622,7 +1621,7 @@ def _constraint_repr_(self, cdict=None): A = sorted(cdict["alphabet"]) cdict["alphabet"] = "{" + repr(A)[1:-1] + "}" constr = "" - ss = ['%s=%s' % item for item in iteritems(cdict)] + ss = ['%s=%s' % item for item in cdict.items()] ss = sorted(ss) if len(ss) > 1: constr = " with constraints: " + ", ".join(ss) @@ -1723,7 +1722,7 @@ def _satisfies_constraints(self, x): False """ X = _concatenate(x) - P = OrderedMultisetPartitionsIntoSets_X(tuple(iteritems(_get_weight(X)))) + P = OrderedMultisetPartitionsIntoSets_X(tuple(_get_weight(X).items())) x = P.element_class(P, [frozenset(block) for block in x]) constr = self.full_constraints tsts = [] @@ -1952,8 +1951,7 @@ def subset(self, size): # slice by 'order' if "alphabet" in fc: - no_alpha = {k: v for (k, v) in iteritems(self.constraints) - if k != "alphabet"} + no_alpha = {k: v for (k, v) in self.constraints.items() if k != "alphabet"} return OrderedMultisetPartitionsIntoSets(fc["alphabet"], size, **no_alpha) # slice by 'size' @@ -2875,12 +2873,13 @@ def _iterator_size(size, length=None, alphabet=None): max_part=min(a, max_p)) for a in alpha]): if frozenset(_concatenate(p)).issubset(frozenset(alphabet)): - yield tuple(frozenset(list(k)) for k in p) + yield tuple(frozenset(k) for k in p) else: for alpha in IntegerListsLex(size, length=length, min_part=1, max_part=size): for p in cartesian_product([IntegerListsLex(a, min_slope=1, min_part=1) for a in alpha]): - yield tuple(frozenset(list(k)) for k in p) + yield tuple(frozenset(k) for k in p) + def _iterator_order(A, d, lengths=None): """ diff --git a/src/sage/combinat/ncsf_qsym/qsym.py b/src/sage/combinat/ncsf_qsym/qsym.py index 80926ed4776..5f8e2d64e5c 100644 --- a/src/sage/combinat/ncsf_qsym/qsym.py +++ b/src/sage/combinat/ncsf_qsym/qsym.py @@ -81,8 +81,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # **************************************************************************** -import six - from sage.misc.bindable_class import BindableClass from sage.categories.graded_hopf_algebras import GradedHopfAlgebras from sage.categories.rings import Rings @@ -703,8 +701,8 @@ def from_polynomial(self, f, check=True): assert self.base_ring() == f.base_ring() exponent_coefficient = f.dict() z = {} - for (e, c) in six.iteritems(exponent_coefficient): - I = Compositions()([ei for ei in e if ei > 0]) + for e, c in exponent_coefficient.items(): + I = Compositions()([ei for ei in e if ei]) if I not in z: z[I] = c out = self.Monomial()._from_dict(z) diff --git a/src/sage/combinat/ncsym/bases.py b/src/sage/combinat/ncsym/bases.py index 0840911968b..398883f1d80 100644 --- a/src/sage/combinat/ncsym/bases.py +++ b/src/sage/combinat/ncsym/bases.py @@ -1,27 +1,28 @@ r""" -Bases for `NCSym`. +Bases for `NCSym` AUTHORS: - Travis Scrimshaw (08-04-2013): Initial version """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2013 Travis Scrimshaw # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.abstract_method import abstract_method from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method from sage.misc.bindable_class import BindableClass from sage.categories.graded_hopf_algebras import GradedHopfAlgebras -from sage.categories.realizations import Category_realization_of_parent #, Realizations +from sage.categories.realizations import Category_realization_of_parent from sage.categories.all import ModulesWithBasis, tensor, Hom from sage.combinat.set_partition import SetPartition, SetPartitions from sage.combinat.free_module import CombinatorialFreeModule + class NCSymBasis_abstract(CombinatorialFreeModule, BindableClass): """ Abstract base class for a basis of `NCSym` or its dual. @@ -46,6 +47,7 @@ def _element_constructor_(self, x): x = SetPartition(x) return super(NCSymBasis_abstract, self)._element_constructor_(x) + class NCSymOrNCSymDualBases(Category_realization_of_parent): r""" Base category for the category of bases of symmetric functions @@ -109,10 +111,10 @@ def _repr_(self): deformed_coarse_powersum basis with parameter q """ str = "{} in the {} basis".format(self.realization_of(), self._realization_name()) - if hasattr(self,'_q'): + if hasattr(self, '_q'): str += " with parameter q" - if repr(self._q)!='q': - str += "="+repr(self._q) + if repr(self._q) != 'q': + str += "=" + repr(self._q) return str def __getitem__(self, i): @@ -299,9 +301,9 @@ def duality_pairing_matrix(self, basis, degree): from sage.matrix.constructor import matrix # TODO: generalize to keys indexing the basis of the graded component return matrix(self.base_ring(), - [[self.duality_pairing(self[I], basis[J]) \ - for J in SetPartitions(degree)] \ - for I in SetPartitions(degree)]) + [[self.duality_pairing(self[I], basis[J]) + for J in SetPartitions(degree)] + for I in SetPartitions(degree)]) class ElementMethods: def duality_pairing(self, other): @@ -326,6 +328,7 @@ def duality_pairing(self, other): """ return self.parent().duality_pairing(self, other) + class NCSymBases(Category_realization_of_parent): r""" Category of bases of symmetric functions in non-commuting variables. @@ -453,7 +456,7 @@ def primitive(self, A, i=1): p = self.realization_of().p() return self(p.primitive(A, i)) - @abstract_method(optional = True) + @abstract_method(optional=True) def internal_coproduct_on_basis(self, i): """ The internal coproduct of the algebra on the basis (optional). @@ -646,16 +649,16 @@ def to_wqsym(self): R = parent.base_ring() m = NCSym.monomial() from sage.combinat.chas.wqsym import WordQuasiSymmetricFunctions - from sage.combinat.set_partition_ordered import OrderedSetPartition M = WordQuasiSymmetricFunctions(R).M() from itertools import permutations OSP = M.basis().keys() + def to_wqsym_on_m_basis(A): # Return the image of `\mathbf{m}_A` under the inclusion # map `NCSym \to WQSym`. l = len(A) return M.sum_of_terms(((OSP([A[ui] for ui in u]), 1) - for u in permutations(range(l))), + for u in permutations(range(l))), distinct=True) return M.linear_combination((to_wqsym_on_m_basis(A), coeff) for A, coeff in m(self)) @@ -722,6 +725,7 @@ def omega(self): h = P.realization_of().h() return P(h.sum_of_terms(e(self))) + class MultiplicativeNCSymBases(Category_realization_of_parent): r""" Category of multiplicative bases of symmetric functions in non-commuting variables. @@ -824,11 +828,12 @@ def product_on_basis(self, A, B): sage: p.product_on_basis(A,B)==p(e(p(A))*e(p(B))) True """ - return self.monomial( A.pipe(B) ) + return self.monomial(A.pipe(B)) class ElementMethods: pass + class NCSymDualBases(Category_realization_of_parent): r""" Category of bases of dual symmetric functions in non-commuting variables. @@ -871,4 +876,3 @@ def _repr_(self): """ return "Category of bases of dual symmetric functions in non-commuting"\ " variables over the {}".format(self.base().base_ring()) - diff --git a/src/sage/combinat/ncsym/dual.py b/src/sage/combinat/ncsym/dual.py index 20db5249f31..dbf3c95830f 100644 --- a/src/sage/combinat/ncsym/dual.py +++ b/src/sage/combinat/ncsym/dual.py @@ -11,7 +11,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc_c import prod diff --git a/src/sage/combinat/ncsym/ncsym.py b/src/sage/combinat/ncsym/ncsym.py index da15d99f0ea..2c4a1ca714f 100644 --- a/src/sage/combinat/ncsym/ncsym.py +++ b/src/sage/combinat/ncsym/ncsym.py @@ -11,7 +11,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # **************************************************************************** -from six.moves import range from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod diff --git a/src/sage/combinat/necklace.py b/src/sage/combinat/necklace.py index 5a4b6b63ccb..69345c662f6 100644 --- a/src/sage/combinat/necklace.py +++ b/src/sage/combinat/necklace.py @@ -21,7 +21,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from sage.combinat.composition import Composition from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets diff --git a/src/sage/combinat/ordered_tree.py b/src/sage/combinat/ordered_tree.py index 95873af80dc..4322f309c9a 100644 --- a/src/sage/combinat/ordered_tree.py +++ b/src/sage/combinat/ordered_tree.py @@ -19,8 +19,6 @@ #python 3 support from __future__ import division, absolute_import, print_function, unicode_literals -from six import add_metaclass - import itertools from sage.structure.list_clone import ClonableArray, ClonableList @@ -41,8 +39,8 @@ from sage.rings.infinity import Infinity -@add_metaclass(InheritComparisonClasscallMetaclass) -class OrderedTree(AbstractClonableTree, ClonableList): +class OrderedTree(AbstractClonableTree, ClonableList, + metaclass=InheritComparisonClasscallMetaclass): """ The class of (ordered rooted) trees. @@ -356,7 +354,7 @@ def to_parallelogram_polyomino(self, bijection=None): INPUT: - - ``bijection`` -- (default:``'Boussicault-Socci'``) is the name of the + - ``bijection`` -- (default:``'Boussicault-Socci'``) is the name of the bijection to use. Possible values are ``'Boussicault-Socci'``, ``'via dyck and Delest-Viennot'``. @@ -386,7 +384,7 @@ def to_parallelogram_polyomino(self, bijection=None): def _to_parallelogram_polyomino_Boussicault_Socci(self): r""" - Return the polyomino parallelogram using the Boussicault-Socci + Return the polyomino parallelogram using the Boussicault-Socci bijection. EXAMPLES:: diff --git a/src/sage/combinat/output.py b/src/sage/combinat/output.py index 93d9f3b7971..f7980607df6 100644 --- a/src/sage/combinat/output.py +++ b/src/sage/combinat/output.py @@ -13,8 +13,6 @@ from __future__ import absolute_import, print_function -from six.moves import range - from string import Template from sage.combinat.tableau import Tableaux diff --git a/src/sage/combinat/parallelogram_polyomino.py b/src/sage/combinat/parallelogram_polyomino.py index 24a0d00081c..fc6fe7e1029 100644 --- a/src/sage/combinat/parallelogram_polyomino.py +++ b/src/sage/combinat/parallelogram_polyomino.py @@ -20,8 +20,6 @@ division, absolute_import, print_function, unicode_literals ) -from six.moves import range -from six import add_metaclass from sage.structure.list_clone import ClonableList from sage.structure.unique_representation import UniqueRepresentation @@ -877,8 +875,8 @@ def draw_point(self, p1, color=None, size=None): ) -@add_metaclass(InheritComparisonClasscallMetaclass) -class ParallelogramPolyomino(ClonableList): +class ParallelogramPolyomino(ClonableList, + metaclass=InheritComparisonClasscallMetaclass): r""" Parallelogram Polyominoes. @@ -1063,22 +1061,22 @@ def check(self): sage: pp = ParallelogramPolyomino([[0], [0]]) # indirect doctest Traceback (most recent call last): ... - ValueError: the lower or the upper path can't be equal to [0] + ValueError: the lower or the upper path can...t be equal to [0] sage: pp = ParallelogramPolyomino([[], [0]]) # indirect doctest Traceback (most recent call last): ... - ValueError: the lower or the upper path can't be equal to [] + ValueError: the lower or the upper path can...t be equal to [] sage: pp = ParallelogramPolyomino([[0], []]) # indirect doctest Traceback (most recent call last): ... - ValueError: the lower or the upper path can't be equal to [] + ValueError: the lower or the upper path can...t be equal to [] sage: pp = ParallelogramPolyomino([[], []]) # indirect doctest Traceback (most recent call last): ... - ValueError: the lower or the upper path can't be equal to [] + ValueError: the lower or the upper path can...t be equal to [] """ lower_path = self.lower_path() upper_path = self.upper_path() @@ -3832,7 +3830,7 @@ class ParallelogramPolyominoesFactory(SetFactory): sage: PPS Parallelogram polyominoes of size 4 - sage: sorted(list(PPS)) + sage: sorted(PPS) [[[0, 0, 0, 1], [1, 0, 0, 0]], [[0, 0, 1, 1], [1, 0, 1, 0]], [[0, 0, 1, 1], [1, 1, 0, 0]], @@ -3862,7 +3860,7 @@ def __call__(self, size=None, policy=None): sage: PPS = ParallelogramPolyominoes(size=4) sage: PPS Parallelogram polyominoes of size 4 - sage: sorted(list(PPS)) + sage: sorted(PPS) [[[0, 0, 0, 1], [1, 0, 0, 0]], [[0, 0, 1, 1], [1, 0, 1, 0]], [[0, 0, 1, 1], [1, 1, 0, 0]], @@ -3937,7 +3935,7 @@ class ParallelogramPolyominoes_size( sage: PPS = ParallelogramPolyominoes(4) sage: PPS Parallelogram polyominoes of size 4 - sage: sorted(list(PPS)) + sage: sorted(PPS) [[[0, 0, 0, 1], [1, 0, 0, 0]], [[0, 0, 1, 1], [1, 0, 1, 0]], [[0, 0, 1, 1], [1, 1, 0, 0]], diff --git a/src/sage/combinat/parking_functions.py b/src/sage/combinat/parking_functions.py index b79c49ca9e6..80e508022ae 100644 --- a/src/sage/combinat/parking_functions.py +++ b/src/sage/combinat/parking_functions.py @@ -62,7 +62,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from sage.rings.integer import Integer from sage.rings.all import QQ diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index e046474e857..98786ab28f4 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -258,7 +258,7 @@ sage: Partition(frobenius_coordinates=([6,1],[2,0])) [7, 3, 1] sage: all(mu == Partition(frobenius_coordinates=mu.frobenius_coordinates()) - ....: for n in range(30) for mu in Partitions(n)) + ....: for n in range(12) for mu in Partitions(n)) True We use the lexicographic ordering:: @@ -282,12 +282,11 @@ from __future__ import print_function, absolute_import from copy import copy -import six -from six.moves import range, zip from sage.libs.all import pari from sage.libs.flint.arith import number_of_partitions as flint_number_of_partitions +from sage.arith.misc import multinomial from sage.structure.global_options import GlobalOptions from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -319,12 +318,14 @@ from sage.combinat.partitions import ZS1_iterator, ZS1_iterator_nk from sage.combinat.integer_vector import IntegerVectors from sage.combinat.integer_lists import IntegerListsLex +from sage.combinat.combinat_cython import conjugate from sage.combinat.root_system.weyl_group import WeylGroup from sage.combinat.combinatorial_map import combinatorial_map from sage.groups.perm_gps.permgroup import PermutationGroup from sage.graphs.dot2tex_utils import have_dot2tex from sage.functions.other import binomial + class Partition(CombinatorialElement): r""" A partition `p` of a nonnegative integer `n` is a @@ -1054,8 +1055,6 @@ def __truediv__(self, p): return SkewPartition([self[:], p]) - __div__ = __truediv__ - def power(self, k): r""" Return the cycle type of the `k`-th power of any permutation @@ -1243,7 +1242,7 @@ def sign(self): REFERENCES: - - :wikipedia:`Zolotarev's_lemma` + - :wikipedia:`Zolotarev%27s_lemma` """ return (-1)**(self.size()-self.length()) @@ -2466,10 +2465,11 @@ def conjugate(self): * * """ - p = list(self) - if p == []: - return self - return Partition(conjugate(p)) + if not self: + par = Partitions_n(0) + return par.element_class(par, []) + par = Partitions_n(sum(self)) + return par.element_class(par, conjugate(self)) def suter_diagonal_slide(self, n, exp=1): r""" @@ -2804,12 +2804,14 @@ def top_garnir_tableau(self,e,cell): g=self.garnir_tableau(cell) # start with the Garnir tableau and modify - if e==0: return g # no more dominant tableau of the same residue + if e==0: + return g # no more dominant tableau of the same residue a=e*int((self[row]-col)/e) # number of cells in the e-bricks in row `row` b=e*int((col+1)/e) # number of cells in the e-bricks in row `row+1` - if a==0 or b==0: return g + if a==0 or b==0: + return g t=g.to_list() m=g[row+1][0] # smallest number in 0-Garnir belt @@ -3042,11 +3044,9 @@ def arm_lengths(self, flat=False): [2, 1, 0, 2, 1, 0] """ p = self - res = [[p[i]-(j+1) for j in range(p[i])] for i in range(len(p))] - if flat: - return sum(res, []) - else: - return res + if not flat: + return [[pi - (j + 1) for j in range(pi)] for pi in p] + return [pi - (j + 1) for pi in p for j in range(pi)] def arm_cells(self, i, j): r""" @@ -3142,11 +3142,11 @@ def leg_lengths(self, flat=False): """ p = self conj = p.conjugate() - res = [[conj[j]-(i+1) for j in range(p[i])] for i in range(len(p))] - if flat: - return sum(res, []) - else: - return res + if not flat: + return [[conj[j] - (i + 1) for j in range(pi)] + for i, pi in enumerate(p)] + return [conj[j] - (i + 1) for i, pi in enumerate(p) + for j in range(pi)] def leg_cells(self, i, j): r""" @@ -3670,8 +3670,8 @@ def centralizer_size(self, t=0, q=0): sage: Partition([2,2,2]).aut() 48 """ - size = prod(i ** mi * factorial(mi) - for i, mi in six.iteritems(self.to_exp_dict())) + size = prod(i**mi * factorial(mi) + for i, mi in self.to_exp_dict().items()) if t or q: size *= prod((ZZ.one() - q ** j) / (ZZ.one() - t ** j) for j in self) @@ -4349,7 +4349,7 @@ def is_core(self, k): :meth:`core`, :class:`Core` """ - return not k in self.hooks() + return k not in self.hooks() def k_interior(self, k): r""" @@ -5109,7 +5109,7 @@ def multinomial_with_partitions(sizes,path_counts): # if we know the total length alloted for each of the paths (sizes), and the number # of paths for each component. A multinomial picks the ordering of the components where # each step is taken. - return prod(path_counts) * factorial(sum(sizes)) / prod([factorial(_) for _ in sizes]) + return prod(path_counts) * multinomial(sizes) sizes = [larger_quotients[i].size()-smaller_quotients[i].size() for i in range(k)] path_counts = [larger_quotients[i].dimension(smaller_quotients[i]) for i in range(k)] @@ -6228,7 +6228,7 @@ def from_core_and_quotient(self, core, quotient): True """ from .partition_tuple import PartitionTuple, PartitionTuples - if not quotient in PartitionTuples(): + if quotient not in PartitionTuples(): raise ValueError('the quotient %s must be a tuple of partitions'%quotient) components = PartitionTuple(quotient).components() length = len(components) @@ -8797,31 +8797,6 @@ def number_of_partitions_length(n, k, algorithm='hybrid'): return ZZ(libgap.NrPartitions(ZZ(n), ZZ(k))) -########## -## Helper functions - -def conjugate(p): - """ - Return the conjugate partition associated to the partition ``p`` - as a list. - - EXAMPLES:: - - sage: from sage.combinat.partition import conjugate - sage: conjugate([2,2]) - [2, 2] - sage: conjugate([6,3,1]) - [3, 2, 2, 1, 1, 1] - """ - l = len(p) - if l == 0: - return [] - conj = [l] * p[-1] - for j in range(l - 1, 0, -1): - conj.extend([j] * (p[j - 1] - p[j])) - return conj - - ########## # trac 14225: Partitions() is frequently used, but only weakly cached. Hence, # establish a strong reference to it. diff --git a/src/sage/combinat/partition_algebra.py b/src/sage/combinat/partition_algebra.py index 6556b03ba8e..ce8a2288429 100644 --- a/src/sage/combinat/partition_algebra.py +++ b/src/sage/combinat/partition_algebra.py @@ -16,7 +16,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import -from six.moves import range from .combinat import catalan_number from .combinatorial_algebra import CombinatorialAlgebra, CombinatorialAlgebraElement diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index a78798746a1..e65ed33ef68 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -244,7 +244,7 @@ class of modules for the algebras, which are generalisations of the Specht """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Andrew Mathas # # This program is free software: you can redistribute it and/or modify @@ -252,11 +252,9 @@ class of modules for the algebras, which are generalisations of the Specht # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # https://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range - import itertools from .combinat import CombinatorialElement @@ -266,7 +264,7 @@ class of modules for the algebras, which are generalisations of the Specht from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.groups.perm_gps.permgroup import PermutationGroup -from sage.interfaces.all import gp +from sage.libs.pari.all import pari from sage.misc.cachefunc import cached_method from sage.rings.all import NN, ZZ, IntegerModRing from sage.rings.integer import Integer @@ -274,10 +272,11 @@ class of modules for the algebras, which are generalisations of the Specht from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation - -#-------------------------------------------------- +# ------------------------------------------------- # Partition tuple - element class -#-------------------------------------------------- +# ------------------------------------------------- + + class PartitionTuple(CombinatorialElement): r""" A tuple of :class:`Partition`. @@ -556,7 +555,7 @@ def _repr_list(self): sage: PartitionTuple(([2,1],[3,2],[1,1,1]))._repr_list() '([2, 1], [3, 2], [1, 1, 1])' """ - return '('+', '.join(nu._repr_() for nu in self)+')' + return '(' + ', '.join(nu._repr_() for nu in self) + ')' def _repr_exp_low(self): """ @@ -615,12 +614,11 @@ def _repr_compact_high(self): return '%s' % '|'.join(mu._repr_compact_high() for mu in self) # override default string representation which is str(self._list) - __str__=lambda self: self._repr_() - + __str__ = lambda self: self._repr_() def _latex_(self): r""" - Returns a LaTeX version of ``self``. + Return a LaTeX version of ``self``. For more on the latex options, see :meth:`Partitions.option`. @@ -688,7 +686,8 @@ def _latex_young_diagram(self): sage: mu = PartitionTuple([[2, 1],[1,1,1]])._latex_young_diagram() """ from sage.combinat.output import tex_from_array_tuple - return tex_from_array_tuple([ [["\\phantom{x}"]*row for row in mu] for mu in self._list ]) + return tex_from_array_tuple([[["\\phantom{x}"] * row for row in mu] + for mu in self._list]) def _latex_diagram(self): """ @@ -700,7 +699,8 @@ def _latex_diagram(self): """ entry = self.parent().options("latex_diagram_str") from sage.combinat.output import tex_from_array_tuple - return tex_from_array_tuple([ [[entry]*row for row in mu] for mu in self._list ], with_lines=False) + return tex_from_array_tuple([[[entry] * row for row in mu] + for mu in self._list], with_lines=False) def _latex_list(self): """ @@ -720,8 +720,10 @@ def _latex_exp_low(self): sage: mu = PartitionTuple([[2, 1],[1,1,1,1,1,1,1,1,1,1]])._latex_exp_low() """ - return '(%s)' % '|'.join(','.join('%s%s'%(a+1,'' if e==1 else '^{%s}'%e) - for (a,e) in enumerate(mu)) for mu in self.to_exp()) + txt = '|'.join(','.join('%s%s' % (a + 1, '' if e == 1 else '^{%s}' % e) + for a, e in enumerate(mu)) + for mu in self.to_exp()) + return '(' + txt + ')' def _latex_exp_high(self): """ @@ -731,9 +733,10 @@ def _latex_exp_high(self): sage: mu = PartitionTuple([[2, 1],[1,1,1,1,1,1,1,1,1,1]])._latex_exp_high() """ - return '(%s)' % '|'.join(','.join(['%s%s'%(a+1,'' if e==1 else '^{%s}'%e) - for (a,e) in enumerate(mu)][::-1]) for mu in self.to_exp()) - + txt = '|'.join(','.join(['%s%s' % (a + 1, '' if e == 1 else '^{%s}' % e) + for a, e in enumerate(mu)][::-1]) + for mu in self.to_exp()) + return '(' + txt + ')' def components(self): r""" @@ -760,8 +763,7 @@ def components(self): *** ** """ - return [ t for t in self ] - + return [t for t in self] def diagram(self): r""" @@ -786,7 +788,7 @@ def diagram(self): *** ** - * sage: PartitionTuples.options._reset() """ - col_len = [len(mu)>0 and mu[0] or 1 for mu in self] # columns per component + col_len = [mu and mu[0] or 1 for mu in self] # columns per component row_max = max(len(mu) for mu in self) # maximum row length # There should be a fancier list compression for this but I couldn't get # one to work in the cases where a component was the empty partition @@ -809,7 +811,6 @@ def diagram(self): ferrers_diagram = diagram - def pp(self): r""" Pretty prints this partition tuple. See :meth:`diagram`. @@ -824,7 +825,6 @@ def pp(self): """ print(self.diagram()) - def size(self): """ Return the size of a partition tuple. @@ -945,7 +945,7 @@ def cells(self): def content(self, k,r,c, multicharge): r""" - Returns the content of the cell. + Return the content of the cell. Let `m_k =` ``multicharge[k]``, then the content of a cell is `m_k + c - r`. @@ -1004,8 +1004,10 @@ def content_tableau(self,multicharge): 2 """ from sage.combinat.tableau_tuple import TableauTuple - return TableauTuple([[[multicharge[k]-r+c for c in range(self[k][r])] - for r in range(len(self[k]))] for k in range(len(self))]) + return TableauTuple([[[multicharge[k] - r + c + for c in range(self[k][r])] + for r in range(len(self[k]))] + for k in range(len(self))]) def conjugate(self): """ @@ -1018,7 +1020,6 @@ def conjugate(self): sage: PartitionTuple([[2,1],[1],[1,1,1]]).conjugate() ([3], [1], [2, 1]) - """ return PartitionTuple([nu.conjugate() for nu in self[::-1]]) @@ -1056,22 +1057,25 @@ def dominates(self, mu): except ValueError: raise ValueError('%s must be a PartitionTuple' % mu) - if mu==self: return True - level=0 - ssum=0 # sum of successive rows in self - musum=0 # sum of successive rows in self + if mu == self: + return True + level = 0 + ssum = 0 # sum of successive rows in self + musum = 0 # sum of successive rows in self while levelssum: return False + if musum>ssum: + return False row+=1 if rowssum: return False + if musum>ssum: + return False level+=1 return True @@ -1247,12 +1251,14 @@ def top_garnir_tableau(self,e,cell): g=self.garnir_tableau(cell) - if e==0: return # no more dominant tableau of the same residue + if e==0: + return # no more dominant tableau of the same residue a=e*int((self[comp][row]-col)/e) # number of cells in the e-bricks in row `row` b=e*int((col+1)/e) # number of cells in the e-bricks in row `row+1` - if a==0 or b==0: return self.garnir_tableau(cell) + if a==0 or b==0: + return self.garnir_tableau(cell) t=g.to_list() m=t[comp][row+1][0] # smallest number of 0-Garnir belt @@ -1327,7 +1333,7 @@ def leg_length(self, k,r,c): def contains(self, mu): r""" - Returns ``True`` if this partition tuple contains `\mu`. + Return ``True`` if this partition tuple contains `\mu`. If `\lambda=(\lambda^{(1)}, \ldots, \lambda^{(l)})` and `\mu=(\mu^{(1)}, \ldots, \mu^{(m)})` are two partition tuples then @@ -1379,7 +1385,7 @@ def to_exp(self, k=0): def removable_cells(self): """ - Returns a list of the removable cells of this partition tuple. + Return a list of the removable cells of this partition tuple. All indices are of the form ``(k, r, c)``, where ``r`` is the row-index, ``c`` is the column index and ``k`` is the component. @@ -1415,7 +1421,7 @@ def addable_cells(self): outside_corners = addable_cells # for compatibility with partitions - def add_cell(self, k,r,c): + def add_cell(self, k, r, c): r""" Return the partition tuple obtained by adding a cell in row ``r``, column ``c``, and component ``k``. @@ -1426,17 +1432,18 @@ def add_cell(self, k,r,c): sage: PartitionTuple([[1,1],[4,3],[2,1,1]]).add_cell(0,0,1) ([2, 1], [4, 3], [2, 1, 1]) - """ - if (k,r,c) in self.addable_cells(): # an addable cell - mu=self.to_list() - if c==0: mu[k].append(1) - else: mu[k][r]+=1 + if (k, r, c) in self.addable_cells(): # an addable cell + mu = self.to_list() + if c == 0: + mu[k].append(1) + else: + mu[k][r] += 1 return PartitionTuple(mu) else: - raise ValueError("%s is not an addable cell"%((k,r,c),)) + raise ValueError("%s is not an addable cell" % ((k, r, c),)) - def remove_cell(self, k,r,c): + def remove_cell(self, k, r, c): """ Return the partition tuple obtained by removing a cell in row ``r``, column ``c``, and component ``k``. @@ -1447,14 +1454,13 @@ def remove_cell(self, k,r,c): sage: PartitionTuple([[1,1],[4,3],[2,1,1]]).remove_cell(0,1,0) ([1], [4, 3], [2, 1, 1]) - """ - if (k,r,c) in self.removable_cells(): # a removable cell - mu=self.to_list() - mu[k][r]-=1 + if (k, r, c) in self.removable_cells(): # a removable cell + mu = self.to_list() + mu[k][r] -= 1 return PartitionTuple(mu) else: - raise ValueError("%s is not a removable cell"%((k,r,c),)) + raise ValueError("%s is not a removable cell" % ((k, r, c),)) def to_list(self): r""" @@ -1470,7 +1476,7 @@ def to_list(self): sage: all(mu==PartitionTuple(mu.to_list()) for mu in PartitionTuples(4,4)) True """ - return [ mu.to_list() for mu in self] + return [mu.to_list() for mu in self] def young_subgroup(self): """ @@ -1482,14 +1488,14 @@ def young_subgroup(self): sage: PartitionTuple([[2,1],[4,2],[1]]).young_subgroup() Permutation Group with generators [(), (8,9), (6,7), (5,6), (4,5), (1,2)] """ - gens=[] - m=0 + gens = [] + m = 0 for comp in self: for row in comp: - gens.extend([(c,c+1) for c in range(m+1,m+row)]) - m+=row - gens.append(list(range(1,self.size()+1))) # to ensure we get a subgroup of Sym_n - return PermutationGroup( gens ) + gens.extend([(c, c+1) for c in range(m+1, m+row)]) + m += row + gens.append(list(range(1, self.size()+1))) # to ensure we get a subgroup of Sym_n + return PermutationGroup(gens) def young_subgroup_generators(self): """ @@ -1501,12 +1507,12 @@ def young_subgroup_generators(self): sage: PartitionTuple([[2,1],[4,2],[1]]).young_subgroup_generators() [1, 4, 5, 6, 8] """ - gens=[] - m=0 + gens = [] + m = 0 for comp in self: for row in comp: - gens.extend([c for c in range(m+1,m+row)]) - m+=row + gens.extend([c for c in range(m + 1, m + row)]) + m += row return gens @cached_method @@ -1759,11 +1765,13 @@ def defect(self, e, multicharge): return (sum(beta.get(r, 0) for r in multicharge) - sum(beta[r]**2 - beta[r] * beta.get(Ie(r+1), 0) for r in beta)) -#-------------------------------------------------- +# ------------------------------------------------- # Partition tuples - parent classes -#-------------------------------------------------- +# ------------------------------------------------- + + class PartitionTuples(UniqueRepresentation, Parent): - """ + r""" Class of all partition tuples. For more information about partition tuples, see :class:`PartitionTuple`. @@ -1777,8 +1785,16 @@ class PartitionTuples(UniqueRepresentation, Parent): - ``size`` -- the total number of cells - - ``regular`` -- the highest multiplicity an entry may have in a - component plus `1` + - ``regular`` -- a positive integer or a tuple of non-negative + integers; if an integer, the highest multiplicity an entry may + have in a component plus `1` + + If a level `k` is specified and ``regular`` is a tuple of integers + `\ell_1, \ldots, \ell_k`, then this specifies partition tuples `\mu` + such that `\mu_i` is `\ell_i`-regular, where `0` here + represents `\infty`-regular partitions (equivalently, partitions + without restrictions). If ``regular`` is an integer `\ell`, then + we set `\ell_i = \ell` for all `i`. TESTS:: @@ -1788,6 +1804,10 @@ class PartitionTuples(UniqueRepresentation, Parent): True sage: ( [] ) in PartitionTuples() True + sage: PartitionTuples(level=1, regular=(0,)) + Partitions + sage: PartitionTuples(level=1, size=3, regular=(0,)) + Partitions of the integer 3 Check that :trac:`14145` has been fixed:: @@ -1810,15 +1830,28 @@ def __classcall_private__(klass, level=None, size=None, regular=None): Partition tuples of size 3 sage: PartitionTuples(3,8) Partition tuples of level 3 and size 8 + sage: PartitionTuples(level=3, regular=(0,2,4)) + (0, 2, 4)-Regular partition tuples of level 3 + sage: PartitionTuples(level=1,regular=(4,)) is PartitionTuples(level=1, regular=4) + True """ - # sanity testing - if level is not None and (not isinstance(level,(int,Integer)) or level<1): + if level is not None and (not isinstance(level, (int, Integer)) or level < 1): raise ValueError('the level must be a positive integer') - if size is not None and (not isinstance(size,(int,Integer)) or size<0): + if size is not None and (not isinstance(size, (int, Integer)) or size < 0): raise ValueError('the size must be a non-negative integer') + if isinstance(regular, (list, tuple)): + if level is None: + raise ValueError("When no level is specified, regular must be " + "a positive integer") + if len(regular) != level: + raise ValueError("regular must be a list of length {}, got {}".format( + level, regular)) + if regular == 0: + raise ValueError("regular must be a positive integer or a tuple " + "of non-negative integers") if level is None: if size is None: if regular is None: @@ -1830,16 +1863,23 @@ def __classcall_private__(klass, level=None, size=None, regular=None): return RegularPartitionTuples_size(size, regular) elif level == 1: + if isinstance(regular, (list, tuple)): + regular = regular[0] if size is None: - if regular is None: + if regular is None or regular == 0: return _Partitions return RegularPartitions_all(regular) - if regular is None: + if regular is None or regular == 0: return Partitions_n(size) return RegularPartitions_n(size, regular) # Higher level + if regular is not None: + if not isinstance(regular, (list, tuple)): + regular = (regular,) * level + else: + regular = tuple(regular) if size is None: if regular is None: return PartitionTuples_level(level) @@ -1849,11 +1889,11 @@ def __classcall_private__(klass, level=None, size=None, regular=None): return RegularPartitionTuples_level_size(level, size, regular) Element = PartitionTuple - options=Partitions.options + options = Partitions.options # default for level - _level=None - _size=None + _level = None + _size = None def _element_constructor_(self, mu): r""" @@ -2019,7 +2059,7 @@ def _an_element_(self): sage: PartitionTuples().an_element() ([1, 1, 1, 1], [2, 1, 1], [3, 1], [4]) """ - return PartitionTuple( ([1,1,1,1],[2,1,1],[3,1],[4]) ) + return PartitionTuple(([1, 1, 1, 1], [2, 1, 1], [3, 1], [4])) class PartitionTuples_all(PartitionTuples): @@ -2099,8 +2139,7 @@ class PartitionTuples_level(PartitionTuples): Class of partition tuples of a fixed level, but summing to an arbitrary integer. """ - - def __init__(self, level): + def __init__(self, level, category=None): r""" Initializes this class. @@ -2112,10 +2151,12 @@ def __init__(self, level): Partition tuples of level 6 sage: TestSuite( PartitionTuples(level=4) ).run() """ - if not level in NN: + if level not in NN: raise ValueError('level must be a non-negative integer') - super(PartitionTuples_level, self).__init__(category=InfiniteEnumeratedSets()) - self._level=level + if category is None: + category = InfiniteEnumeratedSets() + super(PartitionTuples_level, self).__init__(category=category) + self._level = level def _repr_(self): """ @@ -2195,7 +2236,7 @@ def _an_element_(self): sage: PartitionTuples(level=4).an_element() ([], [1], [2], [3]) """ - return self.element_class(self, tuple([l] for l in range(self.level()) )) + return self.element_class(self, tuple([l] for l in range(self.level()))) class PartitionTuples_size(PartitionTuples): @@ -2204,7 +2245,7 @@ class PartitionTuples_size(PartitionTuples): """ def __init__(self, size): r""" - Initializes this class. + Initialize this class. EXAMPLES:: @@ -2215,7 +2256,7 @@ def __init__(self, size): sage: TestSuite( PartitionTuples(size=6) ).run() """ - if not size in NN: + if size not in NN: raise ValueError('size must be a non-negative integer') super(PartitionTuples_size, self).__init__(category=InfiniteEnumeratedSets()) self._size=size @@ -2300,7 +2341,7 @@ def _an_element_(self): sage: PartitionTuples(size=4).an_element() ([1], [1], [1], [1]) """ - return self.element_class(self, tuple([1] for l in range(self._size) )) + return self.element_class(self, tuple([1] for l in range(self._size))) class PartitionTuples_level_size(PartitionTuples): @@ -2393,7 +2434,6 @@ def __iter__(self): for cp in itertools.product(*[p[i] for i in iv]): yield self._element_constructor_(cp) - def _an_element_(self): """ Return a generic element. @@ -2403,9 +2443,10 @@ def _an_element_(self): sage: PartitionTuples(level=4,size=4).an_element() ([1], [], [], [3]) """ - mu=[[] for l in range(self._level)] + mu = [[] for l in range(self._level)] if self._size > 0: - if self._level == 1: mu=[self._size-1,1] + if self._level == 1: + mu=[self._size-1,1] else: mu[0]=[1] mu[-1]=[self._size-1] @@ -2413,9 +2454,9 @@ def _an_element_(self): def cardinality(self): r""" - Returns the number of ``level``-tuples of partitions of size ``n``. + Return the number of ``level``-tuples of partitions of size ``n``. - Wraps a pari function call. + Wraps a pari function call using :pari:`eta`. EXAMPLES:: @@ -2440,7 +2481,8 @@ def cardinality(self): These answers were checked against Gap4 (the last of which takes an awful long time for gap to compute). """ - return ZZ(gp.eval('polcoeff((1/eta(x+O(x^%s)))^%s, %s, x)'%(self.size()+1,self.level(), self.size()))) + eta = pari(f'Ser(x,x,{self.size()})').eta() + return ZZ((1 / eta**self.level()).polcoef(self.size(), pari('x'))) def __setstate__(self, state): r""" @@ -2463,7 +2505,8 @@ def __setstate__(self, state): super(PartitionTuples, self).__setstate__(state) ############################################################################### -## Regular partition tuples +# Regular partition tuples + class RegularPartitionTuples(PartitionTuples): r""" @@ -2513,7 +2556,7 @@ def __contains__(self, mu): return max(mu.to_exp() + [0]) < self._ell if isinstance(mu, PartitionTuple): return all(max(nu.to_exp() + [0]) < self._ell for nu in mu) - if len(mu) == 0: + if not mu: return True if mu in _Partitions: return all(mu.count(i) < self._ell for i in set(mu) if i > 0) @@ -2539,6 +2582,7 @@ def _an_element_(self): elt = RegularPartitionTuples_level_size(lvl, size, self._ell).an_element() return self.element_class(self, list(elt)) + class RegularPartitionTuples_all(RegularPartitionTuples): r""" Class of `\ell`-regular partition tuples. @@ -2599,27 +2643,85 @@ def __iter__(self): yield self.element_class(self, list(mu)) -class RegularPartitionTuples_level(RegularPartitionTuples): +class RegularPartitionTuples_level(PartitionTuples_level): r""" - Class of `\ell`-regular partition tuples with a fixed level. + Regular Partition tuples of a fixed level. + + INPUT: + + - ``level`` -- a non-negative Integer; the level + - ``regular`` -- a positive integer or a tuple of non-negative + integers; if an integer, the highest multiplicity an entry may + have in a component plus `1` with `0` representing `\infty`-regular + (equivalently, partitions without restrictions) + + ``regular`` is a tuple of integers `(\ell_1, \ldots, \ell_k)` that + specifies partition tuples `\mu` such that `\mu_i` is `\ell_i`-regular. + If ``regular`` is an integer `\ell`, then we set `\ell_i = \ell` for + all `i`. + + EXAMPLES:: + + sage: RPT = PartitionTuples(level=4, regular=(2,3,0,2)) + sage: RPT[:24] + [([], [], [], []), + ([1], [], [], []), + ([], [1], [], []), + ([], [], [1], []), + ([], [], [], [1]), + ([2], [], [], []), + ([1], [1], [], []), + ([1], [], [1], []), + ([1], [], [], [1]), + ([], [2], [], []), + ([], [1, 1], [], []), + ([], [1], [1], []), + ([], [1], [], [1]), + ([], [], [2], []), + ([], [], [1, 1], []), + ([], [], [1], [1]), + ([], [], [], [2]), + ([3], [], [], []), + ([2, 1], [], [], []), + ([2], [1], [], []), + ([2], [], [1], []), + ([2], [], [], [1]), + ([1], [2], [], []), + ([1], [1, 1], [], [])] + sage: [[1,1],[3],[5,5,5],[7,2]] in RPT + False + sage: [[3,1],[3],[5,5,5],[7,2]] in RPT + True + sage: [[3,1],[3],[5,5,5]] in RPT + False """ def __init__(self, level, regular): r""" Initialize ``self``. - EXAMPLES:: + TESTS:: + sage: RPT = PartitionTuples(level=2, regular=(0,0)) + sage: RPT.category() + Category of infinite enumerated sets sage: RPT = PartitionTuples(level=4, regular=3) sage: TestSuite(RPT).run() """ - if level not in ZZ or level <= 0: - raise ValueError('level must be a positive integer') - if regular > 1: + if not level in NN: + raise ValueError('level must be a non-negative integer') + if not isinstance(regular, tuple): + # This should not happen if called from RegularPartitionTuples + regular = (regular,) * level + if any (r != 1 for r in regular): category = InfiniteEnumeratedSets() else: category = FiniteEnumeratedSets() - RegularPartitionTuples.__init__(self, regular, category=category) - self._level = level + if any (r not in NN for r in regular): + raise ValueError('regular must be a tuple of non-negative integers') + if len(regular) != level: + raise ValueError("regular must be a tuple with length {}".format(level)) + PartitionTuples_level.__init__(self, level, category=category) + self._ell = regular def _repr_(self): """ @@ -2629,8 +2731,14 @@ def _repr_(self): sage: PartitionTuples(level=4, regular=3) 3-Regular partition tuples of level 4 + sage: PartitionTuples(level=4, regular=(2,3,0,2)) + (2, 3, 0, 2)-Regular partition tuples of level 4 """ - return '{}-Regular partition tuples of level {}'.format(self._ell, self._level) + if self._ell[1:] == self._ell[:-1]: + return '{}-Regular partition tuples of level {}'.format(self._ell[0], + self._level) + return '{}-Regular partition tuples of level {}'.format(self._ell, + self._level) def __contains__(self, mu): r""" @@ -2647,11 +2755,51 @@ def __contains__(self, mu): False sage: [4, 3, 2] in RPT False + + sage: RPT = PartitionTuples(level=3, regular=(2,1,4)) + sage: [[4], [2], [5]] in RPT + False + sage: [[4], [], [5]] in RPT + True + sage: [[4,3], [], [5]] in RPT + True + sage: [[4,4], [], [5]] in RPT + False + sage: [[4,3], [5]] in RPT + False + sage: [5, 4, 3] in RPT + False + sage: [] in RPT + False + sage: [[], [], []] in RPT + True + sage: [[], [], [], [2]] in RPT + False + + sage: from sage.combinat.partition_tuple import RegularPartitionTuples_level + sage: RPT = RegularPartitionTuples_level(1, (3,)); RPT + 3-Regular partition tuples of level 1 + sage: [[2,2]] in RPT + True + sage: [[2,2,2]] in RPT + False """ if self._level == 1: - if mu[0] in ZZ: - return mu in RegularPartitions_all(self._ell) - return RegularPartitionTuples.__contains__(self, mu) and self._level == len(mu) + try: + if mu[0] in ZZ: + return mu in RegularPartitions_all(self._ell[0]) + except (TypeError, ValueError): + return False + return mu[0] in RegularPartitions_all(self._ell[0]) + if mu not in PartitionTuples_level(self._level): + return False + if isinstance(mu, Partition): # it is level 1 + return False + if isinstance(mu, PartitionTuple): + return all(max(p.to_exp() + [0]) < ell for p, ell in zip(mu, self._ell) + if ell > 0) + return all(p in RegularPartitions_all(ell) for p, ell in zip(mu, self._ell) + if ell > 0) def __iter__(self): r""" @@ -2660,6 +2808,31 @@ def __iter__(self): EXAMPLES:: + sage: PartitionTuples(level=3, regular=(2,1,4))[:24] + [([], [], []), + ([1], [], []), + ([], [], [1]), + ([2], [], []), + ([1], [], [1]), + ([], [], [2]), + ([], [], [1, 1]), + ([3], [], []), + ([2, 1], [], []), + ([2], [], [1]), + ([1], [], [2]), + ([1], [], [1, 1]), + ([], [], [3]), + ([], [], [2, 1]), + ([], [], [1, 1, 1]), + ([4], [], []), + ([3, 1], [], []), + ([3], [], [1]), + ([2, 1], [], [1]), + ([2], [], [2]), + ([2], [], [1, 1]), + ([1], [], [3]), + ([1], [], [2, 1]), + ([1], [], [1, 1, 1])] sage: PartitionTuples(level=4, regular=2)[:20] [([], [], [], []), ([1], [], [], []), @@ -2686,6 +2859,7 @@ def __iter__(self): for mu in RegularPartitionTuples_level_size(self._level, size, self._ell): yield self.element_class(self, list(mu)) + class RegularPartitionTuples_size(RegularPartitionTuples): r""" Class of `\ell`-regular partition tuples with a fixed size. @@ -2743,9 +2917,10 @@ def __contains__(self, mu): sage: [4, 3, 2] in RPT True """ - return ( (mu in RegularPartitions_all(self._ell) and self._size == sum(mu)) - or (RegularPartitionTuples.__contains__(self, mu) and self._size == sum(map(sum,mu))) - ) + return ((mu in RegularPartitions_all(self._ell) + and self._size == sum(mu)) + or (RegularPartitionTuples.__contains__(self, mu) + and self._size == sum(map(sum, mu)))) def __iter__(self): r""" @@ -2771,16 +2946,58 @@ def __iter__(self): yield self.element_class(self, list(mu)) -class RegularPartitionTuples_level_size(RegularPartitionTuples): +class RegularPartitionTuples_level_size(PartitionTuples_level_size): r""" - Class of `\ell`-regular partition tuples with a fixed level and a fixed - size. + Class of `\ell`-regular partition tuples with a fixed level and + a fixed size. + + INPUT: + + - ``level`` -- a non-negative Integer; the level + - ``size`` -- a non-negative Integer; the size + - ``regular`` -- a positive integer or a tuple of non-negative + integers; if an integer, the highest multiplicity an entry may + have in a component plus `1` with `0` representing `\infty`-regular + (equivalently, partitions without restrictions) + + ``regular`` is a tuple of integers `(\ell_1, \ldots, \ell_k)` that + specifies partition tuples `\mu` such that `\mu_i` is `\ell_i`-regular. + If ``regular`` is an integer `\ell`, then we set `\ell_i = \ell` for + all `i`. + + EXAMPLES:: + + sage: PartitionTuples(level=3, size=7, regular=(2,1,3))[0:24] + [([7], [], []), + ([6, 1], [], []), + ([5, 2], [], []), + ([4, 3], [], []), + ([4, 2, 1], [], []), + ([6], [], [1]), + ([5, 1], [], [1]), + ([4, 2], [], [1]), + ([3, 2, 1], [], [1]), + ([5], [], [2]), + ([5], [], [1, 1]), + ([4, 1], [], [2]), + ([4, 1], [], [1, 1]), + ([3, 2], [], [2]), + ([3, 2], [], [1, 1]), + ([4], [], [3]), + ([4], [], [2, 1]), + ([3, 1], [], [3]), + ([3, 1], [], [2, 1]), + ([3], [], [4]), + ([3], [], [3, 1]), + ([3], [], [2, 2]), + ([3], [], [2, 1, 1]), + ([2, 1], [], [4])] """ def __init__(self, level, size, regular): r""" Initialize ``self``. - EXAMPLES:: + TESTS:: sage: RPT = PartitionTuples(4,2,3) sage: TestSuite(RPT).run() @@ -2789,9 +3006,15 @@ def __init__(self, level, size, regular): raise ValueError('size must be a non-negative integer') if not (level in ZZ and level > 0): raise ValueError('level must be a positive integer') - RegularPartitionTuples.__init__(self, regular, category=FiniteEnumeratedSets()) - self._level = level - self._size = size + if not isinstance(regular, tuple): + #This should not happen if called from RegularPartitionTuples + regular = (regular,)*level + if len(regular) != level: + raise ValueError('regular must be a list with length {}'.format(level)) + if any (i not in NN for i in regular): + raise ValueError('regular must be a list of non-negative integers') + PartitionTuples_level_size.__init__(self, level, size) + self._ell = regular def _repr_(self): """ @@ -2799,12 +3022,18 @@ def _repr_(self): EXAMPLES:: + sage: PartitionTuples(level=3, size=7, regular=(2,1,4)) + (2, 1, 4)-Regular partition tuples of level 3 and size 7 sage: PartitionTuples(4,2,3) 3-Regular partition tuples of level 4 and size 2 sage: PartitionTuples(size=2,level=4,regular=3) 3-Regular partition tuples of level 4 and size 2 """ - return '{}-Regular partition tuples of level {} and size {}'.format(self._ell, self._level, self._size) + if self._ell[1:] == self._ell[:-1]: + return '{}-Regular partition tuples of level {} and size {}'.format( + self._ell[0], self._level, self._size) + return '{}-Regular partition tuples of level {} and size {}'.format( + self._ell, self._level, self._size) def __contains__(self, mu): r""" @@ -2812,6 +3041,17 @@ def __contains__(self, mu): TESTS:: + sage: RPT = PartitionTuples(level=3, size=7, regular=(2,1,4)) + sage: RPT + (2, 1, 4)-Regular partition tuples of level 3 and size 7 + sage: [[3,1],[],[3]] in RPT + True + sage: [[3],[1],[3]] in RPT + False + sage: [[3,2],[],[3]] in RPT + False + sage: [[3,3],[],[1]] in RPT + False sage: RPT = PartitionTuples(4,3,2) sage: [[], [], [2], [1]] in RPT True @@ -2823,11 +3063,9 @@ def __contains__(self, mu): sage: [4, 3, 2] in RPT False """ - if self._level == 1 and mu[0] in ZZ: - return mu in RegularPartitions_all(self._ell) and self._size == sum(mu) - return (RegularPartitionTuples.__contains__(self, mu) - and self._level == len(mu) - and self._size == sum(map(sum,mu))) + if mu not in RegularPartitionTuples_level(self._level, self._ell): + return False + return self._size == sum(map(sum, mu)) def __iter__(self): r""" @@ -2851,9 +3089,10 @@ def __iter__(self): ([], [], [3]), ([], [], [2, 1])] """ - p = [RegularPartitions_n(i, self._ell) for i in range(self._size+1)] for iv in IntegerVectors(self._size, self._level): - for cp in itertools.product(*[p[i] for i in iv]): + p = [RegularPartitions_n(v, ell) if ell > 0 else Partitions_n(v) + for v,ell in zip(iv,self._ell)] + for cp in itertools.product(*[p[i] for i in range(self._level)]): yield self._element_constructor_(cp) def _an_element_(self): @@ -2868,8 +3107,9 @@ def _an_element_(self): mu = [[] for l in range(self._level)] if self._size > 0: if self._level == 1: - mu = [self._size-1,1] + mu = [[self._size-1,1]] else: mu[0] = [1] mu[-1] = [self._size-1] return self.element_class(self, mu) + diff --git a/src/sage/combinat/path_tableaux/__init__.py b/src/sage/combinat/path_tableaux/__init__.py new file mode 100644 index 00000000000..62d6b68c4dc --- /dev/null +++ b/src/sage/combinat/path_tableaux/__init__.py @@ -0,0 +1,10 @@ +r""" +Path Tableaux +============= + +- :ref:`sage.combinat.path_tableaux.path_tableau` +- :ref:`sage.combinat.path_tableaux.dyck_path` +- :ref:`sage.combinat.path_tableaux.frieze` + +""" + diff --git a/src/sage/combinat/path_tableaux/all.py b/src/sage/combinat/path_tableaux/all.py new file mode 100644 index 00000000000..9ae4469b28d --- /dev/null +++ b/src/sage/combinat/path_tableaux/all.py @@ -0,0 +1,3 @@ +r""" +PathTableaux +""" diff --git a/src/sage/combinat/path_tableaux/catalog.py b/src/sage/combinat/path_tableaux/catalog.py new file mode 100644 index 00000000000..c68f5afb19b --- /dev/null +++ b/src/sage/combinat/path_tableaux/catalog.py @@ -0,0 +1,25 @@ +r""" +Catalog of Path Tableaux + +The ``path_tableaux`` object may be used to access examples of various path +tableau objects currently implemented in Sage. Using tab-completion on this +object is an easy way to discover and quickly create the path tableaux that +are available (as listed here). + +Let ```` indicate pressing the tab key. So begin by typing +``path_tableaux.`` to the see the currently implemented path tableaux. + +- :class:`~sage.combinat.path_tableaux.path_tableau.CylindricalDiagram` +- :class:`~sage.combinat.path_tableaux.dyck_path.DyckPath` +- :class:`~sage.combinat.path_tableaux.dyck_path.DyckPaths` +- :class:`~sage.combinat.path_tableaux.frieze.FriezePattern` +- :class:`~sage.combinat.path_tableaux.frieze.FriezePatterns` +""" + +from sage.misc.lazy_import import lazy_import + +lazy_import('sage.combinat.path_tableaux.path_tableau', ['CylindricalDiagram']) +lazy_import('sage.combinat.path_tableaux.dyck_path', ['DyckPath', 'DyckPaths']) +lazy_import('sage.combinat.path_tableaux.frieze', ['FriezePattern','FriezePatterns']) + +del lazy_import diff --git a/src/sage/combinat/path_tableaux/dyck_path.py b/src/sage/combinat/path_tableaux/dyck_path.py new file mode 100644 index 00000000000..29fcf32796a --- /dev/null +++ b/src/sage/combinat/path_tableaux/dyck_path.py @@ -0,0 +1,379 @@ +r""" +Dyck Paths + +This is an implementation of the abstract base class +:class:`sage.combinat.path_tableaux.path_tableau.PathTableau`. +This is the simplest implementation of a path tableau and is included +to provide a convenient test case and for pedagogical purposes. + +In this implementation we have sequences of nonnegative integers. These +are required to be the heights Dyck words (except that we do not require +the sequence to start or end at height zero). These are in bijection +with skew standard tableaux with at most two rows. Sequences which start +and end at height zero are in bijection with noncrossing perfect matchings. + +AUTHORS: + +- Bruce Westbury (2018): initial version +""" + +#***************************************************************************** +# Copyright (C) 2018 Bruce Westbury , +# +# 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.combinat.path_tableaux.path_tableau import PathTableau, PathTableaux +from sage.combinat.combinatorial_map import combinatorial_map +from sage.combinat.dyck_word import DyckWord +from sage.combinat.perfect_matching import PerfectMatching +from sage.combinat.skew_tableau import SkewTableau +from sage.combinat.tableau import Tableau, StandardTableau +from sage.rings.integer import Integer + +############################################################################### + +class DyckPath(PathTableau): + r""" + An instance is the sequence of nonnegative + integers given by the heights of a Dyck word. + + INPUT: + + * a sequence of nonnegative integers + * a two row standard skew tableau + * a Dyck word + * a noncrossing perfect matching + + EXAMPLES:: + + sage: path_tableaux.DyckPath([0,1,2,1,0]) + [0, 1, 2, 1, 0] + + sage: w = DyckWord([1,1,0,0]) + sage: path_tableaux.DyckPath(w) + [0, 1, 2, 1, 0] + + sage: p = PerfectMatching([(1,2), (3,4)]) + sage: path_tableaux.DyckPath(p) + [0, 1, 0, 1, 0] + + sage: t = Tableau([[1,2,4],[3,5,6]]) + sage: path_tableaux.DyckPath(t) + [0, 1, 2, 1, 2, 1, 0] + + sage: st = SkewTableau([[None, 1,4],[2,3]]) + sage: path_tableaux.DyckPath(st) + [1, 2, 1, 0, 1] + + Here we illustrate the slogan that promotion = rotation:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t.to_perfect_matching() + [(0, 5), (1, 4), (2, 3)] + + sage: t = t.promotion() + sage: t.to_perfect_matching() + [(0, 3), (1, 2), (4, 5)] + + sage: t = t.promotion() + sage: t.to_perfect_matching() + [(0, 1), (2, 5), (3, 4)] + + sage: t = t.promotion() + sage: t.to_perfect_matching() + [(0, 5), (1, 4), (2, 3)] + """ + @staticmethod + def __classcall_private__(cls, ot): + r""" + This ensures that a tableau is only ever constructed as an + ``element_class`` call of an appropriate parent. + + TESTS:: + + sage: t = path_tableaux.DyckPath([0,1,2,1,0]) + + sage: t.parent() + + """ + return DyckPaths()(ot) + + def __init__(self, parent, ot, check=True): + r""" + Initialize a Dyck path. + + TESTS:: + + sage: D = path_tableaux.DyckPath(Tableau([[1,2], [3,4]])) + sage: TestSuite(D).run() + + sage: D = path_tableaux.DyckPath(PerfectMatching([(1,4), (2,3), (5,6)])) + sage: TestSuite(D).run() + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: TestSuite(t).run() + + sage: path_tableaux.DyckPath(PerfectMatching([(1, 3), (2, 4), (5, 6)])) + Traceback (most recent call last): + ... + ValueError: the perfect matching must be non crossing + sage: path_tableaux.DyckPath(Tableau([[1,2,5],[3,5,6]])) + Traceback (most recent call last): + ... + ValueError: the tableau must be standard + sage: path_tableaux.DyckPath(Tableau([[1,2,4],[3,5,6],[7]])) + Traceback (most recent call last): + ... + ValueError: the tableau must have at most two rows + sage: path_tableaux.DyckPath(SkewTableau([[None, 1,4],[2,3],[5]])) + Traceback (most recent call last): + ... + ValueError: the skew tableau must have at most two rows + sage: path_tableaux.DyckPath([0,1,2.5,1,0]) + Traceback (most recent call last): + ... + ValueError: [0, 1, 2.50000000000000, 1, 0] is not a sequence of integers + sage: path_tableaux.DyckPath(Partition([3,2,1])) + Traceback (most recent call last): + ... + ValueError: invalid input [3, 2, 1] + """ + w = None + + if isinstance(ot, DyckWord): + w = ot.heights() + + elif isinstance(ot, PerfectMatching): + if ot.is_noncrossing(): + u = [1]*ot.size() + for a in ot.arcs(): + u[a[1]-1] = 0 + w = DyckWord(u).heights() + else: + raise ValueError("the perfect matching must be non crossing") + + elif isinstance(ot, Tableau): + if len(ot) > 2: + raise ValueError("the tableau must have at most two rows") + if ot.is_standard(): + u = [1] * ot.size() + for i in ot[1]: + u[i-1] = 0 + w = DyckWord(u).heights() + else: + raise ValueError("the tableau must be standard") + + elif isinstance(ot, SkewTableau): + if len(ot) > 2: + raise ValueError("the skew tableau must have at most two rows") + # The check that ot is standard is not implemented + c = ot.to_chain() + w = [0]*len(c) + for i,a in enumerate(c): + if len(a) == 1: + w[i] = a[0] + else: + w[i] = a[0] - a[1] + + elif isinstance(ot, (list,tuple)): + try: + w = tuple([Integer(a) for a in ot]) + except TypeError: + raise ValueError("%s is not a sequence of integers" % ot) + + if w is None: + raise ValueError("invalid input %s" % ot) + + PathTableau.__init__(self, parent, w, check=check) + + def check(self): + """ + Checks that ``self`` is a valid path. + + TESTS:: + + sage: path_tableaux.DyckPath([0,1,0,-1,0]) # indirect doctest + Traceback (most recent call last): + ... + ValueError: [0, 1, 0, -1, 0] has a negative entry + + sage: path_tableaux.DyckPath([0,1,3,1,0]) # indirect doctest + Traceback (most recent call last): + ... + ValueError: [0, 1, 3, 1, 0] is not a Dyck path + """ + n = len(self) + if any(a < 0 for a in self): + raise ValueError( "%s has a negative entry" % (str(self)) ) + for i in range(n-1): + if abs(self[i+1]-self[i]) != 1: + raise ValueError( "%s is not a Dyck path" % str(self) ) + + def local_rule(self,i): + r""" + This has input a list of objects. This method first takes + the list of objects of length three consisting of the `(i-1)`-st, + `i`-th and `(i+1)`-term and applies the rule. It then replaces + the `i`-th object by the object returned by the rule. + + EXAMPLES:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t.local_rule(3) + [0, 1, 2, 1, 2, 1, 0] + + TESTS:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t.local_rule(0) + Traceback (most recent call last): + ... + ValueError: 0 is not a valid integer + sage: t.local_rule(5) + [0, 1, 2, 3, 2, 1, 0] + sage: t.local_rule(6) + Traceback (most recent call last): + ... + ValueError: 6 is not a valid integer + """ + def _rule(x): + """ + This is the rule on a sequence of three letters. + """ + return abs(x[0]-x[1]+x[2]) + + if not (i > 0 and i < len(self)-1): + raise ValueError("%d is not a valid integer" % i) + + with self.clone() as result: + result[i] = _rule(self[i-1:i+2]) + + return result + + def is_skew(self): + r""" + Return ``True`` if ``self`` is skew and ``False`` if not. + + EXAMPLES:: + + sage: path_tableaux.DyckPath([0,1,2,1]).is_skew() + False + + sage: path_tableaux.DyckPath([1,0,1,2,1]).is_skew() + True + """ + return self[0] != 0 + + @combinatorial_map(name='to Dyck word') + def to_DyckWord(self): + r""" + Converts ``self`` to a Dyck word. + + EXAMPLES:: + + sage: c = path_tableaux.DyckPath([0,1,2,1,0]) + sage: c.to_DyckWord() + [1, 1, 0, 0] + """ + return DyckWord(heights_sequence=list(self)) + + def descents(self): + r""" + Return the descent set of ``self``. + + EXAMPLES:: + + sage: path_tableaux.DyckPath([0,1,2,1,2,1,0,1,0]).descents() + {3, 6} + """ + result = set() + + for i in range(1,len(self)-1): + if self[i] < self[i-1] and self[i] < self[i+1]: + result.add(i) + + return result + + def to_word(self): + r""" + Return the word in the alphabet `\{0,1\}` associated to ``self``. + + EXAMPLES:: + + sage: path_tableaux.DyckPath([1,0,1,2,1]).to_word() + [0, 1, 1, 0] + """ + return [(self[i+1] - self[i] + 1) // 2 for i in range(self.size()-1)] + + def to_perfect_matching(self): + r""" + Return the perfect matching associated to ``self``. + + EXAMPLES:: + + sage: path_tableaux.DyckPath([0,1,2,1,2,1,0,1,0]).to_perfect_matching() + [(0, 5), (1, 2), (3, 4), (6, 7)] + + TESTS:: + + sage: path_tableaux.DyckPath([1,2,1,2,1,0,1]).to_perfect_matching() + Traceback (most recent call last): + ... + ValueError: [1, 2, 1, 2, 1, 0, 1] does not start at 0 + """ + if self.is_skew(): + raise ValueError( "%s does not start at 0" % (str(self)) ) + w = self.to_word() + y = DyckWord(w) + pairs = set() + for i, a in enumerate(y): + c = y.associated_parenthesis(i) + if i < c: + pairs.add((i,c)) + return PerfectMatching(pairs) + + def to_tableau(self): + r""" + Return the skew tableau associated to ``self``. + + EXAMPLES:: + + sage: T = path_tableaux.DyckPath([0,1,2,3,2,3]) + sage: T.to_tableau() + [[1, 2, 3, 5], [4]] + + sage: U = path_tableaux.DyckPath([2,3,2,3]) + sage: U.to_tableau() + [[None, None, 1, 3], [2]] + """ + w = self.to_word() + top = [i + 1 for i, a in enumerate(w) if a == 1] + bot = [i + 1 for i, a in enumerate(w) if a == 0] + if self.is_skew(): + return SkewTableau([[None]*self[0]+top, bot]) + else: + return StandardTableau([top, bot]) + +class DyckPaths(PathTableaux): + """ + The parent class for DyckPath. + """ + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: path_tableaux.DyckPaths()._an_element_() + [0, 1, 2, 1, 0] + """ + return DyckPath([0,1,2,1,0]) + + Element = DyckPath + diff --git a/src/sage/combinat/path_tableaux/frieze.py b/src/sage/combinat/path_tableaux/frieze.py new file mode 100644 index 00000000000..108cc46fe41 --- /dev/null +++ b/src/sage/combinat/path_tableaux/frieze.py @@ -0,0 +1,460 @@ +r""" +Frieze Patterns + +This implements the original frieze patterns due to Conway and Coxeter. +Such a frieze pattern is considered as a sequence of nonnegative +integers following [CoCo1]_ and [CoCo2]_ using +:class:`sage.combinat.path_tableaux.path_tableau`. + +AUTHORS: + +- Bruce Westbury (2019): initial version +""" +#***************************************************************************** +# Copyright (C) 2019 Bruce Westbury , +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from six import add_metaclass +from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass +from sage.structure.parent import Parent +from sage.categories.sets_cat import Sets +from sage.combinat.path_tableaux.path_tableau import PathTableau, PathTableaux, CylindricalDiagram +from sage.categories.fields import Fields +from sage.rings.all import QQ, ZZ + +############################################################################### + +@add_metaclass(InheritComparisonClasscallMetaclass) +class FriezePattern(PathTableau): + """ + A frieze pattern. + + We encode a frieze pattern as a sequence in a fixed ground field. + + EXAMPLES:: + + sage: t = path_tableaux.FriezePattern([1,2,1,2,3,1]) + sage: path_tableaux.CylindricalDiagram(t) + [0, 1, 2, 1, 2, 3, 1, 0] + [ , 0, 1, 1, 3, 5, 2, 1, 0] + [ , , 0, 1, 4, 7, 3, 2, 1, 0] + [ , , , 0, 1, 2, 1, 1, 1, 1, 0] + [ , , , , 0, 1, 1, 2, 3, 4, 1, 0] + [ , , , , , 0, 1, 3, 5, 7, 2, 1, 0] + [ , , , , , , 0, 1, 2, 3, 1, 1, 1, 0] + [ , , , , , , , 0, 1, 2, 1, 2, 3, 1, 0] + + sage: TestSuite(t).run() + + sage: t = path_tableaux.FriezePattern([1,2,7,5,3,7,4,1]) + sage: path_tableaux.CylindricalDiagram(t) + [0, 1, 2, 7, 5, 3, 7, 4, 1, 0] + [ , 0, 1, 4, 3, 2, 5, 3, 1, 1, 0] + [ , , 0, 1, 1, 1, 3, 2, 1, 2, 1, 0] + [ , , , 0, 1, 2, 7, 5, 3, 7, 4, 1, 0] + [ , , , , 0, 1, 4, 3, 2, 5, 3, 1, 1, 0] + [ , , , , , 0, 1, 1, 1, 3, 2, 1, 2, 1, 0] + [ , , , , , , 0, 1, 2, 7, 5, 3, 7, 4, 1, 0] + [ , , , , , , , 0, 1, 4, 3, 2, 5, 3, 1, 1, 0] + [ , , , , , , , , 0, 1, 1, 1, 3, 2, 1, 2, 1, 0] + [ , , , , , , , , , 0, 1, 2, 7, 5, 3, 7, 4, 1, 0] + + sage: TestSuite(t).run() + + sage: t = path_tableaux.FriezePattern([1,3,4,5,1]) + sage: path_tableaux.CylindricalDiagram(t) + [ 0, 1, 3, 4, 5, 1, 0] + [ , 0, 1, 5/3, 7/3, 2/3, 1, 0] + [ , , 0, 1, 2, 1, 3, 1, 0] + [ , , , 0, 1, 1, 4, 5/3, 1, 0] + [ , , , , 0, 1, 5, 7/3, 2, 1, 0] + [ , , , , , 0, 1, 2/3, 1, 1, 1, 0] + [ , , , , , , 0, 1, 3, 4, 5, 1, 0] + + sage: TestSuite(t).run() + + This constructs the examples from [HJ18]_:: + + sage: K. = NumberField(x^2-3) + sage: t = path_tableaux.FriezePattern([1,sqrt3,2,sqrt3,1,1], field=K) + sage: path_tableaux.CylindricalDiagram(t) + [ 0, 1, sqrt3, 2, sqrt3, 1, 1, 0] + [ , 0, 1, sqrt3, 2, sqrt3, sqrt3 + 1, 1, 0] + [ , , 0, 1, sqrt3, 2, sqrt3 + 2, sqrt3, 1, 0] + [ , , , 0, 1, sqrt3, sqrt3 + 2, 2, sqrt3, 1, 0] + [ , , , , 0, 1, sqrt3 + 1, sqrt3, 2, sqrt3, 1, 0] + [ , , , , , 0, 1, 1, sqrt3, 2, sqrt3, 1, 0] + [ , , , , , , 0, 1, sqrt3 + 1, sqrt3 + 2, sqrt3 + 2, sqrt3 + 1, 1, 0] + [ , , , , , , , 0, 1, sqrt3, 2, sqrt3, 1, 1, 0] + + sage: TestSuite(t).run() + + sage: K. = NumberField(x^2-2) + sage: t = path_tableaux.FriezePattern([1,sqrt2,1,sqrt2,3,2*sqrt2,5,3*sqrt2,1], field=K) + sage: path_tableaux.CylindricalDiagram(t) + [ 0, 1, sqrt2, 1, sqrt2, 3, 2*sqrt2, 5, 3*sqrt2, 1, 0] + [ , 0, 1, sqrt2, 3, 5*sqrt2, 7, 9*sqrt2, 11, 2*sqrt2, 1, 0] + [ , , 0, 1, 2*sqrt2, 7, 5*sqrt2, 13, 8*sqrt2, 3, sqrt2, 1, 0] + [ , , , 0, 1, 2*sqrt2, 3, 4*sqrt2, 5, sqrt2, 1, sqrt2, 1, 0] + [ , , , , 0, 1, sqrt2, 3, 2*sqrt2, 1, sqrt2, 3, 2*sqrt2, 1, 0] + [ , , , , , 0, 1, 2*sqrt2, 3, sqrt2, 3, 5*sqrt2, 7, 2*sqrt2, 1, 0] + [ , , , , , , 0, 1, sqrt2, 1, 2*sqrt2, 7, 5*sqrt2, 3, sqrt2, 1, 0] + [ , , , , , , , 0, 1, sqrt2, 5, 9*sqrt2, 13, 4*sqrt2, 3, 2*sqrt2, 1, 0] + [ , , , , , , , , 0, 1, 3*sqrt2, 11, 8*sqrt2, 5, 2*sqrt2, 3, sqrt2, 1, 0] + [ , , , , , , , , , 0, 1, 2*sqrt2, 3, sqrt2, 1, sqrt2, 1, sqrt2, 1, 0] + [ , , , , , , , , , , 0, 1, sqrt2, 1, sqrt2, 3, 2*sqrt2, 5, 3*sqrt2, 1, 0] + + sage: TestSuite(t).run() + """ + @staticmethod + def __classcall_private__(cls, fp, field=QQ): + """ + This is the preprocessing for creating friezes. + + INPUT: + + - ``field`` -- a sequence of elements of the field + + EXAMPLES:: + + sage: path_tableaux.FriezePattern([1,2,1,2,3,1]) + [1, 2, 1, 2, 3, 1] + + TESTS:: + + sage: path_tableaux.FriezePattern(2) + Traceback (most recent call last): + ... + ValueError: invalid input 2 + + sage: K. = NumberField(x^2-3) + sage: t = path_tableaux.FriezePattern([1,sqrt3,2,sqrt3,1,1]) + Traceback (most recent call last): + ... + ValueError: [1, sqrt3, 2, sqrt3, 1, 1] is not a sequence in the field Rational Field + + sage: path_tableaux.FriezePattern([1,2,1,2,3,1],field=Integers()) + Traceback (most recent call last): + ... + ValueError: Integer Ring must be a field + """ + if field not in Fields: + raise ValueError(f"{field} must be a field") + + if isinstance(fp, (list, tuple)): + try: + fp = [field(a) for a in fp] + except TypeError: + raise ValueError(f"{fp} is not a sequence in the field {field}") + else: + raise ValueError(f"invalid input {fp}") + + fp.insert(0, field(0)) + fp.append(field(0)) + return FriezePatterns(field)(tuple(fp)) + + def check(self): + """ + Check that ``self`` is a valid frieze pattern. + + TESTS:: + + sage: path_tableaux.FriezePattern([1,2,1,2,3,1]) # indirect test + [1, 2, 1, 2, 3, 1] + """ + # Nothing to check + pass + + def _repr_(self): + """ + Return the string representation of ``self``. + + This removes the leading and trailing zero. + + TESTS:: + + sage: t = path_tableaux.FriezePattern([1,2,1,2,3,1]) + sage: repr(t) == t._repr_() # indirect test + True + """ + return repr(self[1:-1]) + + def local_rule(self, i): + r""" + Return the `i`-th local rule on ``self``. + + This interprets ``self`` as a list of objects. This method first takes + the list of objects of length three consisting of the `(i-1)`-st, + `i`-th and `(i+1)`-term and applies the rule. It then replaces + the `i`-th object by the object returned by the rule. + + EXAMPLES:: + + sage: t = path_tableaux.FriezePattern([1,2,1,2,3,1]) + sage: t.local_rule(3) + [1, 2, 5, 2, 3, 1] + + sage: t = path_tableaux.FriezePattern([1,2,1,2,3,1]) + sage: t.local_rule(0) + Traceback (most recent call last): + ... + ValueError: 0 is not a valid integer + """ + def _rule(x): + """ + This is the rule on a sequence of three scalars. + """ + return (x[0]*x[2]+1) / x[1] + + if not (i > 0 and i < len(self) - 1): + raise ValueError(f"{i} is not a valid integer") + + with self.clone() as result: + result[i] = _rule(self[i-1:i+2]) + + return result + + def is_skew(self): + r""" + Return ``True`` if ``self`` is skew and ``False`` if not. + + EXAMPLES:: + + sage: path_tableaux.FriezePattern([1,2,1,2,3,1]).is_skew() + False + + sage: path_tableaux.FriezePattern([2,2,1,2,3,1]).is_skew() + True + """ + return self[1] != 1 + + def width(self): + r""" + Return the width of ``self``. + + If the first and last terms of ``self`` are 1 then this is the + length of ``self`` plus two and otherwise is undefined. + + EXAMPLES:: + + sage: path_tableaux.FriezePattern([1,2,1,2,3,1]).width() + 8 + + sage: path_tableaux.FriezePattern([1,2,1,2,3,4]).width() is None + True + """ + if self[1] == 1 and self[-2] == 1: + return len(self) + else: + return None + + def is_positive(self): + r""" + Return ``True`` if all elements of ``self`` are positive. + + This implies that all entries of ``CylindricalDiagram(self)`` are positive. + + .. WARNING:: + + There are orders on all fields. These may not be ordered fields + as they may not be compatible with the field operations. This + method is intended to be used with ordered fields only. + + EXAMPLES:: + + sage: path_tableaux.FriezePattern([1,2,7,5,3,7,4,1]).is_positive() + True + + sage: path_tableaux.FriezePattern([1,-3,4,5,1]).is_positive() + False + + sage: K. = NumberField(x^2-3) + sage: path_tableaux.FriezePattern([1,sqrt3,1],K).is_positive() + True + """ + return all(a > 0 for a in self[1:-1]) + + def is_integral(self): + r""" + Return ``True`` if all entries of the frieze pattern are positive integers. + + EXAMPLES:: + + sage: path_tableaux.FriezePattern([1,2,7,5,3,7,4,1]).is_integral() + True + + sage: path_tableaux.FriezePattern([1,3,4,5,1]).is_integral() + False + """ + n = len(self) + cd = CylindricalDiagram(self).diagram + return all(all(k in ZZ for k in a[i+1:n+i-2]) for i, a in enumerate(cd)) + + def triangulation(self): + r""" + Plot a regular polygon with some diagonals. + + If ``self`` is positive and integral then this will be a triangulation. + + .. PLOT:: + :width: 600 px + + G = path_tableaux.FriezePattern([1,2,7,5,3,7,4,1]).triangulation() + p = graphics_array(G, 7, 6) + sphinx_plot(p) + + EXAMPLES:: + + sage: path_tableaux.FriezePattern([1,2,7,5,3,7,4,1]).triangulation() + Graphics object consisting of 25 graphics primitives + + sage: path_tableaux.FriezePattern([1,2,1/7,5,3]).triangulation() + Graphics object consisting of 12 graphics primitives + + sage: K. = NumberField(x^2-2) + sage: path_tableaux.FriezePattern([1,sqrt2,1,sqrt2,3,2*sqrt2,5,3*sqrt2,1], field=K).triangulation() + Graphics object consisting of 24 graphics primitives + """ + n = len(self)-1 + cd = CylindricalDiagram(self).diagram + from sage.plot.plot import Graphics + from sage.plot.line import line + from sage.plot.text import text + from sage.functions.trig import sin, cos + from sage.all import pi + G = Graphics() + G.set_aspect_ratio(1.0) + + vt = [(cos(2*theta*pi/(n)), sin(2*theta*pi/(n))) for theta in range(n+1)] + for i, p in enumerate(vt): + G += text(str(i), [1.05*p[0],1.05*p[1]]) + + for i, r in enumerate(cd): + for j, a in enumerate(r[:n]): + if a == 1: + G += line([vt[i],vt[j]]) + + G.axes(False) + return G + + def plot(self, model='UHP'): + r""" + Plot the frieze as an ideal hyperbolic polygon. + + This is only defined up to isometry of the hyperbolic plane. + + We are identifying the boundary of the hyperbolic plane with the + real projective line. + + The option ``model`` must be one of + + * ``'UHP'``, for the upper half plane model + * ``'PD'``, for the Poincare disk model + * ``'KM'``, for the Klein model + + The hyperboloid model is not an option as this does not implement + boundary points. + + This can be omitted in which case it is taken to be UHP. + + EXAMPLES:: + + sage: t = path_tableaux.FriezePattern([1,2,7,5,3,7,4,1]) + sage: t.plot() + Graphics object consisting of 18 graphics primitives + + sage: t.plot(model='UHP') + Graphics object consisting of 18 graphics primitives + + sage: t.plot(model='PD') + Traceback (most recent call last): + ... + TypeError: '>' not supported between instances of 'NotANumber' and 'Pi' + sage: t.plot(model='KM') + Graphics object consisting of 18 graphics primitives + """ + from sage.geometry.hyperbolic_space.hyperbolic_interface import HyperbolicPlane + from sage.plot.plot import Graphics + models = { + 'UHP': HyperbolicPlane().UHP(), + 'PD': HyperbolicPlane().PD(), + 'KM': HyperbolicPlane().KM(), + } + if model not in models: + raise ValueError(f"{model} must be one of ``UHP``, ``PD``, ``KM``") + M = models[model] + + U = HyperbolicPlane().UHP() + cd = CylindricalDiagram(self).diagram + num = cd[0][:-1] + den = cd[1][2:] + vt = [M(U.get_point(x / (x+y))) for x,y in zip(num, den)] + gd = [M.get_geodesic(vt[i-1], vt[i]) for i in range(len(vt))] + return sum([a.plot() for a in gd], Graphics()).plot() + + def change_ring(self, R): + r""" + Return ``self`` as a frieze pattern with coefficients in ``R`` + assuming there is a canonical coerce map from the base ring of ``self`` + to ``R``. + + EXAMPLES:: + + sage: path_tableaux.FriezePattern([1,2,7,5,3,7,4,1]).change_ring(RealField()) + [0.000000000000000, 1.00000000000000, ... 4.00000000000000, 1.00000000000000, 0.000000000000000] + + sage: path_tableaux.FriezePattern([1,2,7,5,3,7,4,1]).change_ring(GF(7)) + Traceback (most recent call last): + ... + TypeError: no base extension defined + """ + if R.has_coerce_map_from(self.parent().base_ring()): + return FriezePattern(list(self), field=R) + else: + raise TypeError("no base extension defined") + +class FriezePatterns(PathTableaux): + """ + The parent class for path_tableaux.FriezePattern. + + EXAMPLES:: + + sage: P = path_tableaux.FriezePatterns(QQ) + sage: fp = P((1, 1, 1)) + sage: fp + [1] + sage: path_tableaux.CylindricalDiagram(fp) + [1, 1, 1] + [ , 1, 2, 1] + [ , , 1, 1, 1] + """ + def __init__(self, field): + """ + Initializes the class of all FriezePatterns + + TESTS:: + + sage: P = path_tableaux.FriezePatterns(QQ) + sage: TestSuite(P).run() + """ + Parent.__init__(self, base=field, category=Sets()) + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: path_tableaux.FriezePatterns(QQ)._an_element_() + [1, 1, 1] + """ + return FriezePattern((1,1,1)) + + Element = FriezePattern + diff --git a/src/sage/combinat/path_tableaux/path_tableau.py b/src/sage/combinat/path_tableaux/path_tableau.py new file mode 100644 index 00000000000..40deced9c6f --- /dev/null +++ b/src/sage/combinat/path_tableaux/path_tableau.py @@ -0,0 +1,725 @@ +r""" +Path Tableaux + +This is an abstract base class for using local rules to construct +rectification and the action of the cactus group [Wes2017]_. + +This is a construction of the Henriques-Kamnitzer construction of +the action of the cactus group on tensor powers of a crystal. This is +also a generalisation of the Fomin growth rules, which are a version of +the operations on standard tableaux which were previously constructed +using jeu-de-taquin. + +The basic operations are rectification, evacuation and promotion. +Rectification of standard skew tableaux agrees with the rectification +by jeu-de-taquin as does evacuation. Promotion agrees with promotion +by jeu-de-taquin on rectangular tableaux but in general they are different. + +REFERENCES: + +- [Wes2017]_ + +AUTHORS: + +- Bruce Westbury (2018): initial version +""" + +# **************************************************************************** +# Copyright (C) 2018 Bruce Westbury , +# +# 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.inherit_comparison import InheritComparisonClasscallMetaclass +from sage.misc.abstract_method import abstract_method +from sage.categories.sets_cat import Sets +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent +#from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_graded +from sage.structure.sage_object import SageObject +from sage.structure.list_clone import ClonableArray +from sage.misc.latex import latex +#from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +#from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets + + +class PathTableau(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): + r""" + This is the abstract base class for a path tableau. + """ + @abstract_method + def local_rule(self,i): + r""" + This is the abstract local rule defined in any coboundary category. + + This has input a list of objects. This method first takes + the list of objects of length three consisting of the `(i-1)`-st, + `i`-th and `(i+1)`-term and applies the rule. It then replaces + the `i`-th object by the object returned by the rule. + + EXAMPLES:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t.local_rule(3) + [0, 1, 2, 1, 2, 1, 0] + """ + + ################################# Book Keeping ############################ + + def size(self): + r""" + Return the size or length of ``self``. + + EXAMPLES:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t.size() + 7 + """ + return len(self) + + def initial_shape(self): + r""" + Return the initial shape of ``self``. + + EXAMPLES:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t.initial_shape() + 0 + """ + return self[0] + + def final_shape(self): + r""" + Return the final shape of ``self``. + + EXAMPLES:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t.final_shape() + 0 + """ + return self[-1] + + ############################# Jeu de taquin ############################### + + def promotion(self): + r""" + Return the promotion operator applied to ``self``. + + EXAMPLES:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t.promotion() + [0, 1, 2, 1, 0, 1, 0] + """ + with self.clone() as result: + for i in range(1,len(result)-1): + result = result.local_rule(i) + + return result + + def evacuation(self): + r""" + Return the evacuation operator applied to ``self``. + + EXAMPLES:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t.evacuation() + [0, 1, 2, 3, 2, 1, 0] + """ + if self.size() < 3: + return self + + L = list(self) + result = [] + P = self.parent() + for i in range(len(self)): + L = list(P(L).promotion()) + result.append( L.pop() ) + result.reverse() + return P(result) + + def commutor(self, other, verbose=False): + r""" + Return the commutor of ``self`` with ``other``. + + If ``verbose=True`` then the function will print + the rectangle. + + EXAMPLES:: + + sage: t1 = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t2 = path_tableaux.DyckPath([0,1,2,1,0]) + sage: t1.commutor(t2) + ([0, 1, 2, 1, 0], [0, 1, 2, 3, 2, 1, 0]) + sage: t1.commutor(t2,verbose=True) + [0, 1, 2, 1, 0] + [1, 2, 3, 2, 1] + [2, 3, 4, 3, 2] + [3, 4, 5, 4, 3] + [2, 3, 4, 3, 2] + [1, 2, 3, 2, 1] + [0, 1, 2, 1, 0] + ([0, 1, 2, 1, 0], [0, 1, 2, 3, 2, 1, 0]) + + TESTS:: + + sage: t1 = path_tableaux.DyckPath([]) + sage: t2 = path_tableaux.DyckPath([0,1,2,1,0]) + sage: t1.commutor(t2) + Traceback (most recent call last): + ... + ValueError: this requires nonempty lists + sage: t1 = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t2 = path_tableaux.DyckPath([]) + sage: t1.commutor(t2) + Traceback (most recent call last): + ... + ValueError: this requires nonempty lists + sage: t1 = path_tableaux.DyckPath([0,1,2,3,2,1]) + sage: t2 = path_tableaux.DyckPath([0,1,2,1,0]) + sage: t1.commutor(t2) + Traceback (most recent call last): + ... + ValueError: [0, 1, 2, 3, 2, 1], [0, 1, 2, 1, 0] is not a composable pair + """ + n = self.size() + m = len(other) + if n == 0 or m == 0: + raise ValueError("this requires nonempty lists") + if n == 1 or m == 1: + return (other, self) + + P = self.parent() + + row = list(other) + col = list(self) + if col[-1] != row[0]: + raise ValueError("%s, %s is not a composable pair" % (self,other)) + + path = P(col + row[1:]) + + for i in range(1,n): + if verbose: + print(path[n-i:n+m-i]) + for j in range(m-1): + path = path.local_rule(n+j-i) + if verbose: + print(path[:m]) + + return (P(path[:m]), P(path[m-1:])) + + def cactus(self,i,j): + r""" + Return the action of the generator `s_{i,j}` of the cactus + group on ``self``. + + INPUT: + + ``i`` -- a positive integer + ``j`` -- a positive integer weakly greater than ``i`` + + EXAMPLES:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t.cactus(1,5) + [0, 1, 0, 1, 2, 1, 0] + + sage: t.cactus(1,6) + [0, 1, 2, 1, 0, 1, 0] + + sage: t.cactus(1,7) == t.evacuation() + True + sage: t.cactus(1,7).cactus(1,6) == t.promotion() + True + + TESTS:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t.cactus(1,8) + Traceback (most recent call last): + ... + ValueError: integers out of bounds + sage: t.cactus(0,3) + Traceback (most recent call last): + ... + ValueError: integers out of bounds + """ + if not 0 < i <= j <= self.size(): + raise ValueError("integers out of bounds") + + if i == j: + return self + + if i == 1: + h = list(self)[:j] + t = list(self)[j:] + T = self.parent()(h) + L = list(T.evacuation()) + t + return self.parent()(L) + + return self.cactus(1,j).cactus(1,j-i+1).cactus(1,j) + + ########################### Visualisation and checking #################### + + def _test_involution_rule(self, **options): + """ + Check that the local rule gives an involution. + + TESTS:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t._test_involution_rule() + """ + tester = self._tester(**options) + for i in range(self.size()-2): + tester.assertEqual(self.local_rule(i+1).local_rule(i + 1), self) + + def _test_involution_cactus(self, **options): + """ + Check that the cactus group generators are involutions. + + TESTS:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t._test_involution_cactus() + """ + tester = self._tester(**options) + for i in range(2, self.size()+1): + tester.assertEqual(self.cactus(1,i).cactus(1,i), self) + + def _test_promotion(self, **options): + """ + Check that promotion can be expressed in terms of the cactus generators. + + TESTS:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t._test_promotion() + """ + tester = self._tester(**options) + n = self.size() - 1 + tester.assertEqual(self.cactus(1,n-1).cactus(1,n).promotion(), self) + + def _test_commutation(self, **options): + """ + Check the commutation relations in the presentation of the cactus group. + + TESTS:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t._test_commutation() + """ + from itertools import combinations + tester = self._tester(**options) + + n = self.size() + if n < 5: + return + for i,j,r,s in combinations(range(1,n+1), 4): + lhs = self.cactus(i, j).cactus(r, s) + rhs = self.cactus(r, s).cactus(i, j) + tester.assertEqual(lhs, rhs) + + def _test_coboundary(self, **options): + """ + Check the coboundary relations in the presentation of the cactus group. + + TESTS:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t._test_coboundary() + """ + from itertools import combinations + tester = self._tester(**options) + + n = self.size() + if n < 4: + return + for i,j,r,s in combinations(range(1,n+3), 4): + lhs = self.cactus(i, s-2).cactus(j-1, r-1) + rhs = self.cactus(i+s-r-1, i+s-j-1).cactus(i, s-2) + tester.assertEqual(lhs, rhs) + + def orbit(self): + r""" + Return the orbit of ``self`` under the action of the cactus group. + + EXAMPLES:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: t.orbit() + {[0, 1, 0, 1, 0, 1, 0], + [0, 1, 0, 1, 2, 1, 0], + [0, 1, 2, 1, 0, 1, 0], + [0, 1, 2, 1, 2, 1, 0], + [0, 1, 2, 3, 2, 1, 0]} + """ + orb = set([]) + rec = set([self]) + while rec: + new = set([]) + for a in rec: + for i in range(2, self.size()): + b = a.cactus(1, i) + if (b not in orb) and (b not in rec): + new.add(b) + orb = orb.union(rec) + rec = new.copy() + + return orb + + def dual_equivalence_graph(self): + r""" + Return the graph with vertices the orbit of ``self`` + and edges given by the action of the cactus group generators. + + In most implementations the generators `s_{i,i+1}` will act + as the identity operators. The usual dual equivalence graphs + are given by replacing the label `i,i+2` by `i` and removing + edges with other labels. + + EXAMPLES:: + + sage: s = path_tableaux.DyckPath([0,1,2,3,2,3,2,1,0]) + sage: s.dual_equivalence_graph().adjacency_matrix() + [0 1 1 1 0 1 0 1 1 0 0 0 0 0] + [1 0 1 1 1 1 1 0 1 0 0 1 1 0] + [1 1 0 1 1 1 0 1 0 1 1 1 0 0] + [1 1 1 0 1 0 1 1 1 1 0 1 1 0] + [0 1 1 1 0 0 1 0 0 1 1 0 1 1] + [1 1 1 0 0 0 1 1 1 1 1 0 1 0] + [0 1 0 1 1 1 0 1 0 1 1 1 0 1] + [1 0 1 1 0 1 1 0 1 1 1 1 1 0] + [1 1 0 1 0 1 0 1 0 1 0 1 1 0] + [0 0 1 1 1 1 1 1 1 0 0 1 1 1] + [0 0 1 0 1 1 1 1 0 0 0 1 1 1] + [0 1 1 1 0 0 1 1 1 1 1 0 1 1] + [0 1 0 1 1 1 0 1 1 1 1 1 0 1] + [0 0 0 0 1 0 1 0 0 1 1 1 1 0] + sage: s = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: sorted(s.dual_equivalence_graph().edges()) + [([0, 1, 0, 1, 0, 1, 0], [0, 1, 0, 1, 2, 1, 0], '4,7'), + ([0, 1, 0, 1, 0, 1, 0], [0, 1, 2, 1, 0, 1, 0], '2,5'), + ([0, 1, 0, 1, 0, 1, 0], [0, 1, 2, 1, 2, 1, 0], '2,7'), + ([0, 1, 0, 1, 2, 1, 0], [0, 1, 2, 1, 0, 1, 0], '2,6'), + ([0, 1, 0, 1, 2, 1, 0], [0, 1, 2, 1, 2, 1, 0], '1,4'), + ([0, 1, 0, 1, 2, 1, 0], [0, 1, 2, 3, 2, 1, 0], '2,7'), + ([0, 1, 2, 1, 0, 1, 0], [0, 1, 2, 1, 2, 1, 0], '4,7'), + ([0, 1, 2, 1, 0, 1, 0], [0, 1, 2, 3, 2, 1, 0], '3,7'), + ([0, 1, 2, 1, 2, 1, 0], [0, 1, 2, 3, 2, 1, 0], '3,6')] + """ + from sage.graphs.graph import Graph + from itertools import combinations + + G = Graph() + orb = self.orbit() + + for a in orb: + for i,j in combinations(range(1,self.size()+1),2): + b = a.cactus(i,j) + if a != b: + G.add_edge(a,b,"%d,%d" % (i,j)) + return G + +class PathTableaux(UniqueRepresentation,Parent): + """ + The abstract parent class for PathTableau. + """ + def __init__(self): + """ + Initialize ``self``. + + TESTS:: + + sage: t = path_tableaux.DyckPaths() + sage: TestSuite(t).run() + + sage: f = path_tableaux.FriezePatterns(QQ) + sage: TestSuite(f).run() + """ + Parent.__init__(self, category=Sets()) + + def _element_constructor_(self, *args, **kwds): + r""" + Construct an object as an element of ``self``, if possible. + + TESTS:: + + sage: path_tableaux.DyckPath([0,1,2,1,0]) # indirect doctest + [0, 1, 2, 1, 0] + """ + return self.element_class(self, *args, **kwds) + +class CylindricalDiagram(SageObject): + r""" + Cylindrical growth diagrams. + + EXAMPLES:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: path_tableaux.CylindricalDiagram(t) + [0, 1, 2, 3, 2, 1, 0] + [ , 0, 1, 2, 1, 0, 1, 0] + [ , , 0, 1, 0, 1, 2, 1, 0] + [ , , , 0, 1, 2, 3, 2, 1, 0] + [ , , , , 0, 1, 2, 1, 0, 1, 0] + [ , , , , , 0, 1, 0, 1, 2, 1, 0] + [ , , , , , , 0, 1, 2, 3, 2, 1, 0] + """ + def __init__(self, T): + """ + Initialize ``self`` from the + :class:`~sage.combinat.path_tableaux.path_tableau.PathTableau` + object ``T``. + + TESTS:: + + sage: T = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: D = path_tableaux.CylindricalDiagram(T) + sage: TestSuite(D).run() + + sage: path_tableaux.CylindricalDiagram(2) + Traceback (most recent call last): + ... + ValueError: 2 must be a path tableau + """ + if not isinstance(T, PathTableau): + raise ValueError('{0} must be a path tableau'.format(str(T))) + n = len(T) + result = [[None]*(2*n-1)] * n + for i in range(n): + result[i] = [""]*i + list(T) + T = T.promotion() + + self.path_tableau = T + self.diagram = result + + def _repr_(self): + r""" + Return a string representation of ``self`` + + TESTS:: + + sage: cd = path_tableaux.CylindricalDiagram(path_tableaux.DyckPath([0,1,2,1,2,1,0])) + sage: repr(cd) == cd._repr_() # indirect test + True + + sage: cd = path_tableaux.CylindricalDiagram(path_tableaux.FriezePattern([1,3,4,5,1])) + sage: repr(cd) == cd._repr_() # indirect test + True + + sage: print(path_tableaux.DyckPath([0,1,2,1,2,1,0])) # indirect test + [0, 1, 2, 1, 2, 1, 0] + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: path_tableaux.CylindricalDiagram(t) + [0, 1, 2, 3, 2, 1, 0] + [ , 0, 1, 2, 1, 0, 1, 0] + [ , , 0, 1, 0, 1, 2, 1, 0] + [ , , , 0, 1, 2, 3, 2, 1, 0] + [ , , , , 0, 1, 2, 1, 0, 1, 0] + [ , , , , , 0, 1, 0, 1, 2, 1, 0] + [ , , , , , , 0, 1, 2, 3, 2, 1, 0] + """ + data = [[str(x) for x in row] for row in self.diagram] + if not data[0]: + data[0] = [''] # Put sometime there + max_width = max(max(len(x) for x in row) for row in data if row) + return '\n'.join('[' + ', '.join(' '*(max_width-len(x)) + x for x in row) + + ']' for row in data) + + def __eq__(self, other): + """ + Check equality. + + EXAMPLES:: + + sage: t1 = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: T1 = path_tableaux.CylindricalDiagram(t1) + sage: t2 = path_tableaux.DyckPath([0,1,2,1,2,1,0]) + sage: T2 = path_tableaux.CylindricalDiagram(t2) + sage: T1 == T2 + False + sage: T1 == path_tableaux.CylindricalDiagram(t1) + True + """ + return isinstance(other, CylindricalDiagram) and self.diagram == other.diagram + + def __ne__(self, other): + """ + Check inequality. + + EXAMPLES:: + + sage: t1 = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: T1 = path_tableaux.CylindricalDiagram(t1) + sage: t2 = path_tableaux.DyckPath([0,1,2,1,2,1,0]) + sage: T2 = path_tableaux.CylindricalDiagram(t2) + sage: T1 != T2 + True + sage: T1 != path_tableaux.CylindricalDiagram(t1) + False + """ + return not (self == other) + + def _latex_(self): + r""" + Return a `\LaTeX` representation of ``self`` + + EXAMPLES:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: latex(path_tableaux.CylindricalDiagram(t)) + \begin{array}{ccccccccccccc} + 0 & 1 & 2 & 3 & 2 & 1 & 0\\ + & 0 & 1 & 2 & 1 & 0 & 1 & 0\\ + & & 0 & 1 & 0 & 1 & 2 & 1 & 0\\ + & & & 0 & 1 & 2 & 3 & 2 & 1 & 0\\ + & & & & 0 & 1 & 2 & 1 & 0 & 1 & 0\\ + & & & & & 0 & 1 & 0 & 1 & 2 & 1 & 0\\ + & & & & & & 0 & 1 & 2 & 3 & 2 & 1 & 0 + \end{array} + + sage: t = path_tableaux.FriezePattern([1,3,4,5,1]) + sage: latex(path_tableaux.CylindricalDiagram(t)) + \begin{array}{ccccccccccccc} + 0 & 1 & 3 & 4 & 5 & 1 & 0\\ + & 0 & 1 & \frac{5}{3} & \frac{7}{3} & \frac{2}{3} & 1 & 0\\ + & & 0 & 1 & 2 & 1 & 3 & 1 & 0\\ + & & & 0 & 1 & 1 & 4 & \frac{5}{3} & 1 & 0\\ + & & & & 0 & 1 & 5 & \frac{7}{3} & 2 & 1 & 0\\ + & & & & & 0 & 1 & \frac{2}{3} & 1 & 1 & 1 & 0\\ + & & & & & & 0 & 1 & 3 & 4 & 5 & 1 & 0 + \end{array} + + """ + D = self.diagram + m = len(D[-1]) + result = "\\begin{array}{"+"c"*m + "}\n" + result += "\\\\ \n".join( " & ".join(latex(a) for a in x) for x in D ) + result += "\n \\end{array}\n" + return result + + def __len__(self): + r""" + Return the length of ``self``. + + TESTS:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: len(path_tableaux.CylindricalDiagram(t)) + 7 + """ + return len(self.diagram) + + def _ascii_art_(self): + r""" + Return an ascii art representation of ``self`` + + TESTS:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: ascii_art(path_tableaux.CylindricalDiagram(t)) + 0 1 2 3 2 1 0 + 0 1 2 1 0 1 0 + 0 1 0 1 2 1 0 + 0 1 2 3 2 1 0 + 0 1 2 1 0 1 0 + 0 1 0 1 2 1 0 + 0 1 2 3 2 1 0 + + sage: t = path_tableaux.FriezePattern([1,3,4,5,1]) + sage: ascii_art(path_tableaux.CylindricalDiagram(t)) + 0 1 3 4 5 1 0 + 0 1 5/3 7/3 2/3 1 0 + 0 1 2 1 3 1 0 + 0 1 1 4 5/3 1 0 + 0 1 5 7/3 2 1 0 + 0 1 2/3 1 1 1 0 + 0 1 3 4 5 1 0 + """ + from sage.typeset.ascii_art import ascii_art + from sage.misc.misc_c import prod + data = [[ascii_art(x) for x in row] for row in self.diagram] + if not data[0]: + data[0] = [ascii_art('')] # Put sometime there + max_width = max(max(len(x) for x in row) for row in data if row) + return prod((sum((ascii_art(' '*(max_width-len(x)+1)) + x for x in row), ascii_art('')) + for row in data), ascii_art('')) + + def _unicode_art_(self): + r""" + Return a unicode art representation of ``self`` + + TESTS:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: unicode_art(path_tableaux.CylindricalDiagram(t)) + 0 1 2 3 2 1 0 + 0 1 2 1 0 1 0 + 0 1 0 1 2 1 0 + 0 1 2 3 2 1 0 + 0 1 2 1 0 1 0 + 0 1 0 1 2 1 0 + 0 1 2 3 2 1 0 + + sage: t = path_tableaux.FriezePattern([1,3,4,5,1]) + sage: unicode_art(path_tableaux.CylindricalDiagram(t)) + 0 1 3 4 5 1 0 + 0 1 5/3 7/3 2/3 1 0 + 0 1 2 1 3 1 0 + 0 1 1 4 5/3 1 0 + 0 1 5 7/3 2 1 0 + 0 1 2/3 1 1 1 0 + 0 1 3 4 5 1 0 + """ + from sage.typeset.unicode_art import unicode_art + from sage.misc.misc_c import prod + data = [[unicode_art(x) for x in row] for row in self.diagram] + if not data[0]: + data[0] = [unicode_art('')] # Put sometime there + max_width = max(max(len(x) for x in row) for row in data if row) + return prod((sum((unicode_art(' '*(max_width-len(x)+1)) + x for x in row), unicode_art('')) + for row in data), unicode_art('')) + + def pp(self): + r""" + A pretty print utility method. + + EXAMPLES:: + + sage: t = path_tableaux.DyckPath([0,1,2,3,2,1,0]) + sage: path_tableaux.CylindricalDiagram(t).pp() + 0 1 2 3 2 1 0 + 0 1 2 1 0 1 0 + 0 1 0 1 2 1 0 + 0 1 2 3 2 1 0 + 0 1 2 1 0 1 0 + 0 1 0 1 2 1 0 + 0 1 2 3 2 1 0 + + sage: t = path_tableaux.FriezePattern([1,3,4,5,1]) + sage: path_tableaux.CylindricalDiagram(t).pp() + 0 1 3 4 5 1 0 + 0 1 5/3 7/3 2/3 1 0 + 0 1 2 1 3 1 0 + 0 1 1 4 5/3 1 0 + 0 1 5 7/3 2 1 0 + 0 1 2/3 1 1 1 0 + 0 1 3 4 5 1 0 + """ + data = [[str(x) for x in row] for row in self.diagram] + if not data[0]: + data[0] = [''] # Put sometime there + max_width = max(max(len(x) for x in row) for row in data if row) + print('\n'.join(' '.join(' '*(max_width-len(x)) + x for x in row) + for row in data)) + diff --git a/src/sage/combinat/perfect_matching.py b/src/sage/combinat/perfect_matching.py index 16924dca039..8e65e9c9cc5 100644 --- a/src/sage/combinat/perfect_matching.py +++ b/src/sage/combinat/perfect_matching.py @@ -54,7 +54,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import division, print_function -from six.moves import range from sage.misc.cachefunc import cached_method from sage.rings.integer import Integer diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index a68d6734f81..5c222e6aa1e 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -233,9 +233,6 @@ #***************************************************************************** from __future__ import print_function, absolute_import -from builtins import zip -from six.moves import range - from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets @@ -244,7 +241,7 @@ from sage.categories.finite_permutation_groups import FinitePermutationGroups from sage.structure.list_clone import ClonableArray from sage.structure.global_options import GlobalOptions -from sage.interfaces.all import gap +from sage.libs.gap.libgap import libgap from sage.rings.all import ZZ, Integer, PolynomialRing from sage.arith.all import factorial, multinomial from sage.matrix.matrix_space import MatrixSpace @@ -415,9 +412,6 @@ class Permutation(CombinatorialElement): [] sage: Permutation( [[], []] ) [] - - .. automethod:: Permutation.left_action_product - .. automethod:: Permutation.right_action_product """ @staticmethod def __classcall_private__(cls, l, check_input = True): @@ -666,11 +660,11 @@ def _latex_(self): redword = self.reduced_word() if not redword: return self.parent().options.latex_empty_str - return " ".join("%s_{%s}"%(let, i) for i in redword) + return " ".join("%s_{%s}" % (let, i) for i in redword) if display == "twoline": - return "\\begin{pmatrix} %s \\\\ %s \\end{pmatrix}"%( - " & ".join("%s"%i for i in range(1, len(self._list)+1)), - " & ".join("%s"%i for i in self._list)) + return "\\begin{pmatrix} %s \\\\ %s \\end{pmatrix}" % ( + " & ".join("%s" % i for i in range(1, len(self._list)+1)), + " & ".join("%s" % i for i in self._list)) if display == "list": return repr(self._list) if display == "cycle": @@ -1883,7 +1877,7 @@ def _icondition(self, i): :meth:`ishift`, :meth:`iswitch` """ if i not in range(2, len(self)): - raise ValueError("i (= %s) must be between 2 and n-1"%i) + raise ValueError("i (= %s) must be between 2 and n-1" % i) pos_i = self.index(i) pos_ip1 = self.index(i+1) pos_im1 = self.index(i-1) @@ -5310,7 +5304,7 @@ def __classcall_private__(cls, n=None, k=None, **kwargs): if k is None: return Permutations_mset(n) else: - return Permutations_msetk(n,k) + return Permutations_msetk(n, k) elif 'descents' in kwargs: #Descent positions specified if isinstance(kwargs['descents'], tuple): @@ -5518,6 +5512,7 @@ def random_element(self): """ return sample(range(1, self.n+1), self._k) + class Permutations_mset(Permutations): r""" Permutations of a multiset `M`. @@ -5669,6 +5664,8 @@ def __iter__(self): def cardinality(self): """ + Return the cardinality of the set. + EXAMPLES:: sage: Permutations([1,2,2]).cardinality() @@ -5684,6 +5681,228 @@ def cardinality(self): return ZZ(multinomial(d.values())) + def rank(self, p): + r""" + Return the rank of ``p`` in lexicographic order. + + INPUT: + + - ``p`` -- a permutation of `M` + + ALGORITHM: + + The algorithm uses the recurrence from the solution to exercise 4 in + [Knu2011]_, Section 7.2.1.2: + + .. MATH:: + + \mathrm{rank}(p_1 \ldots p_n) = + \mathrm{rank}(p_2 \ldots p_n) + + \frac{1}{n} \genfrac{(}{)}{0pt}{0}{n}{n_1, \ldots, n_t} + \sum_{j=1}^t n_j \left[ x_j < p_1 \right], + + where `x_j, n_j` are the distinct elements of `p` with their + multiplicities, `n` is the sum of `n_1, \ldots, n_t`, + `\genfrac{(}{)}{0pt}{1}{n}{n_1, \ldots, n_t}` is the multinomial + coefficient `\frac{n!}{n_1! \ldots n_t!}`, and + `\sum_{j=1}^t n_j \left[ x_j < p_1 \right]` means "the number of + elements to the right of the first element that are less than the first + element". + + EXAMPLES:: + + sage: mset = [1, 1, 2, 3, 4, 5, 5, 6, 9] + sage: p = Permutations(mset) + sage: p.rank(list(sorted(mset))) + 0 + sage: p.rank(list(reversed(sorted(mset)))) == p.cardinality() - 1 + True + sage: p.rank([3, 1, 4, 1, 5, 9, 2, 6, 5]) + 30991 + + TESTS:: + + sage: from sage.combinat.permutation import Permutations_mset + sage: p = Permutations_mset([]) + sage: p.rank([]) + 0 + sage: p = Permutations_mset([1, 1, 2, 3, 4, 5, 5, 6, 9]) + sage: p.rank([1, 2, 3, 4, 5, 6, 9]) + Traceback (most recent call last): + ... + ValueError: Invalid permutation + + Try with greater-than-unity multiplicity in the least and greatest + elements:: + + sage: mset = list(range(10)) * 3 + sage: p = Permutations_mset(mset) + sage: p.rank(list(sorted(mset))) + 0 + sage: p.rank(list(reversed(sorted(mset)))) + 4386797336285844479999999 + sage: p.cardinality() + 4386797336285844480000000 + + Should match ``StandardPermutations_n`` when `M` is the set + `\{1, 2, \ldots, n\}`:: + + sage: ps = Permutations(4) + sage: pm = Permutations_mset(list(range(1, 5))) + sage: ps.rank([2, 3, 1, 4]) == pm.rank([2, 3, 1, 4]) + True + """ + self(p).check() + m = {} + r = 0 + for n in range(1, len(p)+1): + # ``p1`` is the first element of ``p[-n:]`` (i.e., the last ``n`` + # elements of ``p``). ``m`` represents the multiset of ``p[-n:]`` in + # the form element→count. + p1 = p[-n] + m[p1] = m.get(p1, 0) + 1 + r += multinomial(m.values()) * sum(nj for xj, nj in m.items() if xj < p1) // n + return r + + def unrank(self, r): + r""" + Return the permutation of `M` having lexicographic rank ``r``. + + INPUT: + + - ``r`` -- an integer between ``0`` and ``self.cardinality()-1`` + inclusive + + ALGORITHM: + + The algorithm is adapted from the solution to exercise 4 in [Knu2011]_, + Section 7.2.1.2. + + EXAMPLES:: + + sage: mset = [1, 1, 2, 3, 4, 5, 5, 6, 9] + sage: p = Permutations(mset) + sage: p.unrank(30991) + [3, 1, 4, 1, 5, 9, 2, 6, 5] + sage: p.rank(p.unrank(10)) + 10 + sage: p.unrank(0) == list(sorted(mset)) + True + sage: p.unrank(p.cardinality()-1) == list(reversed(sorted(mset))) + True + + TESTS:: + + sage: from sage.combinat.permutation import Permutations_mset + sage: p = Permutations_mset([]) + sage: p.unrank(0) + [] + sage: p.unrank(1) + Traceback (most recent call last): + ... + ValueError: r must be between 0 and 0 inclusive + sage: p = Permutations_mset([1, 1, 2, 3, 4, 5, 5, 6, 9]) + sage: p.unrank(-1) + Traceback (most recent call last): + ... + ValueError: r must be between 0 and 90719 inclusive + sage: p.unrank(p.cardinality()) + Traceback (most recent call last): + ... + ValueError: r must be between 0 and 90719 inclusive + + Try with a cardinality that exceeds the precise integer range of a + float:: + + sage: mset = list(range(10)) * 3 + sage: p = Permutations_mset(mset) + sage: p.unrank(p.rank(mset)) == mset + True + sage: p.unrank(p.cardinality()-1) == list(reversed(sorted(mset))) + True + + Exhaustive check of roundtrip and lexicographic order for a single + multiset:: + + sage: p = Permutations_mset([2, 2, 3, 3, 3, 5, 5, 5, 5, 5]) + sage: prev = None + sage: for rank, perm in enumerate(p): + ....: perm = tuple(perm) + ....: assert p.rank(perm) == rank, (rank, perm, p.rank(perm)) + ....: assert tuple(p.unrank(rank)) == perm, (rank, perm, p.unrank(rank)) + ....: assert prev is None or prev < perm + ....: prev = perm + + Should match ``StandardPermutations_n`` when `M` is the set + `\{1, 2, \ldots, n\}`:: + + sage: ps = Permutations(4) + sage: pm = Permutations_mset(list(range(1, 5))) + sage: ps.unrank(5) == pm.unrank(5) + True + """ + range_error = ValueError("r must be between %d and %d inclusive" % (0, self.cardinality()-1)) + if r < 0: + raise range_error + + # ``mset`` is a working copy of the remaining multiset, initially set to + # ``self.mset`` and always kept sorted. ``m`` represents ``mset`` in + # element→count form. One element is removed from both representations + # of the multiset in each loop iteration. + mset = list(sorted(self.mset)) + m = {} + for x in mset: + m[x] = m.get(x, 0) + 1 + + from bisect import bisect_left + + # We will build up ``p`` from left to right. For each element ``pi``, we + # use the residual rank to compute how many elements to the right of + # position ``i`` have a value less than ``pi``. We use that count of + # elements to index what remains of ``mset`` to get the value of ``pi``. + p = [] + while mset: + n = len(mset) + mult = multinomial(m.values()) + # Now ``r = ci * mult / n + rank(p[i+1:])``, where ``ci`` is the + # number of elements remaining in ``mset`` that are less than + # ``pi``. We know ``r`` and ``mult / n``, but not ``rank(p[i+1:])`` + # nor ``ci``, except that we know both are integers. + ti = r * n // mult + if ti >= n: + raise range_error + # ``ti`` is an upper bound for ``ci``. The true value is + # ``ci = (r - rank(p[i+1:])) * n / mult``, but we do not know + # ``rank(p[i+1:])`` yet. When ``pi`` has multiplicity 1, ``ti = ci`` + # exactly, because in that case ``rank(p[i+1:]) < mult / n``. When + # ``pi`` has multiplicity greater than 1, ``ti`` is equal to the + # number of elements that are less than ``pi``, *plus* some fraction + # of those that are equal to ``pi``. We need to search left from + # ``ti`` to find the lowest-indexed element equal to ``mset[ti]``, + # to find ``ci`` which counts only elements that are strictly less + # than ``pi``. We do a binary search in ``mset`` to the left of + # position ``ti`` to find the leftmost index whose value is equal to + # ``mset[ti]``. We do not have to look more places to the left than + # the multiplicity of ``pi`` (``m[mset[ti]]``). + ci = bisect_left(mset, mset[ti], max(0, ti + 1 - m[mset[ti]]), ti + 1) + # Now that we know ``ci``, we can set ``r = rank(p[i+1:])`` for the + # next iteration. + r -= ci * mult // n + # Because ``mset`` is sorted, the element that is greater than + # ``ci`` other elements is at index ``ci``. Add that element to the + # permutation and remove it from the multiset for the next + # iteration. + pi = mset.pop(ci) + m[pi] -= 1 + if m[pi] == 0: + del m[pi] + p.append(pi) + + if r > 0: + raise range_error + + return p + class Permutations_set(Permutations): """ @@ -5791,6 +6010,8 @@ def __iter__(self): def cardinality(self): """ + Return the cardinality of the set. + EXAMPLES:: sage: Permutations([1,2,3]).cardinality() @@ -5855,7 +6076,8 @@ def __contains__(self, x): sage: [2,1] in p True """ - if len(x) != self._k: return False + if len(x) != self._k: + return False s = list(self.mset) for i in x: if i in s: @@ -5871,7 +6093,18 @@ def _repr_(self): sage: Permutations([1,2,2],2) Permutations of the multi-set [1, 2, 2] of length 2 """ - return "Permutations of the multi-set %s of length %s"%(list(self.mset), self._k) + return "Permutations of the multi-set %s of length %s" % (list(self.mset), self._k) + + def cardinality(self): + """ + Return the cardinality of the set. + + EXAMPLES:: + + sage: Permutations([1,2,2],2).cardinality() + 3 + """ + return ZZ.sum(1 for z in self) def __iter__(self): """ @@ -5883,10 +6116,11 @@ def __iter__(self): mset = self.mset lmset = list(mset) mset_list = [lmset.index(x) for x in lmset] - indices = eval(gap.eval('Arrangements(%s,%s)'%(mset_list, self._k))) + indices = libgap.Arrangements(mset_list, self._k).sage() for ktuple in indices: yield self.element_class(self, [lmset[x] for x in ktuple]) + class Permutations_setk(Permutations_set): """ Length-`k` partial permutations of an arbitrary given finite set. @@ -6397,7 +6631,7 @@ def rank(self, p=None): return self.n - 1 if p in self: return Permutation(p).rank() - raise ValueError("x not in self") + raise ValueError("p not in self") def random_element(self): """ diff --git a/src/sage/combinat/plane_partition.py b/src/sage/combinat/plane_partition.py index 24815ac122b..c61717fd833 100644 --- a/src/sage/combinat/plane_partition.py +++ b/src/sage/combinat/plane_partition.py @@ -23,8 +23,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range -from six import add_metaclass from sage.structure.list_clone import ClonableArray from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass @@ -37,8 +35,8 @@ from sage.combinat.tableau import Tableau -@add_metaclass(InheritComparisonClasscallMetaclass) -class PlanePartition(ClonableArray): +class PlanePartition(ClonableArray, + metaclass=InheritComparisonClasscallMetaclass): r""" A plane partition. diff --git a/src/sage/combinat/posets/__init__.py b/src/sage/combinat/posets/__init__.py index a2cd43889e5..01e0e411a96 100644 --- a/src/sage/combinat/posets/__init__.py +++ b/src/sage/combinat/posets/__init__.py @@ -12,7 +12,8 @@ - :ref:`sage.combinat.posets.lattices` - :ref:`sage.combinat.posets.linear_extensions` - +- :ref:`sage.combinat.posets.d_complete` +- :ref:`sage.combinat.posets.forest` - :ref:`sage.combinat.posets.incidence_algebras` - :ref:`sage.combinat.posets.cartesian_product` @@ -29,5 +30,5 @@ :class:`~sage.categories.finite_posets.FinitePosets`, :class:`~sage.categories.lattice_posets.LatticePosets` and :class:`~sage.categories.finite_lattice_posets.FiniteLatticePosets`. +""" - """ diff --git a/src/sage/combinat/posets/d_complete.py b/src/sage/combinat/posets/d_complete.py new file mode 100644 index 00000000000..ef4e85f8324 --- /dev/null +++ b/src/sage/combinat/posets/d_complete.py @@ -0,0 +1,155 @@ +r""" +D-Complete Posets + +AUTHORS: + +- Stefan Grosser (06-2020): initial implementation +""" + +# **************************************************************************** +# Copyright (C) 2020 Stefan Grosser +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.misc.lazy_attribute import lazy_attribute +from .linear_extensions import LinearExtensionsOfPosetWithHooks +from .lattices import FiniteJoinSemilattice +from collections import deque + +class DCompletePoset(FiniteJoinSemilattice): + r""" + A d-complete poset. + + D-complete posets are a class of posets introduced by Proctor + in [Proc1999]_. It includes common families such as shapes, shifted + shapes, and rooted forests. Proctor showed in [PDynk1999]_ that + d-complete posets have decompositions in *irreducible* posets, + and showed in [Proc2014]_ that d-complete posets admit a hook-length + formula (see :wikipedia:`Hook_length_formula`). A complete proof of + the hook-length formula can be found in [KY2019]_. + + EXAMPLES:: + + sage: from sage.combinat.posets.poset_examples import Posets + sage: P = Posets.DoubleTailedDiamond(2) + sage: TestSuite(P).run() + """ + _lin_ext_type = LinearExtensionsOfPosetWithHooks + _desc = "Finite d-complete poset" + + @lazy_attribute + def _hooks(self): + r""" + The hook lengths of the elements of the d-complete poset. + + See [KY2019]_ for the definition of hook lengths for d-complete posets. + + TESTS:: + + sage: from sage.combinat.posets.d_complete import DCompletePoset + sage: P = DCompletePoset(DiGraph({0: [1, 2], 1: [3], 2: [3], 3: []})) + sage: P._hooks + {0: 1, 1: 2, 2: 2, 3: 3} + sage: from sage.combinat.posets.poset_examples import Posets + sage: P = DCompletePoset(Posets.YoungDiagramPoset(Partition([3,2,1]))._hasse_diagram.reverse()) + sage: P._hooks + {0: 5, 1: 3, 2: 1, 3: 3, 4: 1, 5: 1} + """ + hooks = {} + + min_diamond = {} # Maps max of double-tailed diamond to min of double-tailed diamond + max_diamond = {} # Maps min of double-tailed diamond to max of double-tailed diamond + + H = self._hasse_diagram + + diamonds, _ = H.diamonds() # Tuples of four elements that are diamonds + + diamond_index = {} # Map max elmt of double tailed diamond to index of diamond + + # Find all the double-tailed diamonds and map the mins and maxes + for index, d in enumerate(diamonds): + min_diamond[d[3]] = d[0] + max_diamond[d[0]] = d[3] + diamond_index[d[3]] = index + + min_elmt = d[0] + max_elmt = d[3] + + while True: + potential_min = H.neighbors_in(min_elmt) + potential_max = H.neighbors_out(max_elmt) + + # Check if any of these make a longer double tailed diamond + found_diamond = False + for (mn, mx) in [(i,j) for i in potential_min for j in potential_max]: + if len(H.neighbors_in(mx)) != 1: + continue + if len(H.all_paths(mn, mx)) == 2: + # Success + min_elmt = mn + max_elmt = mx + + min_diamond[mx] = mn + max_diamond[mn] = mx + diamond_index[mx] = index + found_diamond = True + break + if not found_diamond: + break + # Compute the hooks + queue = deque(H.sources()) + enqueued = set() + while queue: + elmt = queue.popleft() + if elmt not in diamond_index: + hooks[elmt] = H.order_ideal_cardinality([elmt]) + else: + diamond = diamonds[diamond_index[elmt]] + side1 = diamond[1] + side2 = diamond[2] + hooks[elmt] = hooks[side1] + hooks[side2] - hooks[min_diamond[elmt]] + enqueued.add(elmt) + + for c in H.neighbors_out(elmt): + if c not in enqueued: + queue.append(c) + enqueued.add(c) + + poset_hooks = {self._vertex_to_element(key): value for (key, value) in hooks.items()} + return poset_hooks + + def get_hook(self, elmt): + r""" + Return the hook length of the element ``elmt``. + + EXAMPLES:: + + sage: from sage.combinat.posets.d_complete import DCompletePoset + sage: P = DCompletePoset(DiGraph({0: [1], 1: [2]})) + sage: P.get_hook(1) + 2 + """ + return self._hooks[elmt] + + def get_hooks(self): + r""" + Return all the hook lengths as a dictionary. + + EXAMPLES:: + + sage: from sage.combinat.posets.d_complete import DCompletePoset + sage: P = DCompletePoset(DiGraph({0: [1, 2], 1: [3], 2: [3], 3: []})) + sage: P.get_hooks() + {0: 1, 1: 2, 2: 2, 3: 3} + sage: from sage.combinat.posets.poset_examples import Posets + sage: P = DCompletePoset(Posets.YoungDiagramPoset(Partition([3,2,1]))._hasse_diagram.reverse()) + sage: P.get_hooks() + {0: 5, 1: 3, 2: 1, 3: 3, 4: 1, 5: 1} + """ + return dict(self._hooks) + diff --git a/src/sage/combinat/posets/forest.py b/src/sage/combinat/posets/forest.py new file mode 100644 index 00000000000..41e29cd5f9a --- /dev/null +++ b/src/sage/combinat/posets/forest.py @@ -0,0 +1,29 @@ +r""" +Forest Posets + +AUTHORS: + +- Stefan Grosser (06-2020): initial implementation +""" + +# **************************************************************************** +# Copyright (C) 2020 Stefan Grosser +# +# 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.combinat.posets.posets import FinitePoset +from sage.combinat.posets.linear_extensions import LinearExtensionsOfForest + +class ForestPoset(FinitePoset): + r""" + A forest poset is a poset where the underlying Hasse diagram and is + directed acyclic graph. + """ + _lin_ext_type = LinearExtensionsOfForest + _desc = 'Finite forest poset' + diff --git a/src/sage/combinat/posets/hasse_cython.pyx b/src/sage/combinat/posets/hasse_cython.pyx new file mode 100644 index 00000000000..4d4ba3abe3c --- /dev/null +++ b/src/sage/combinat/posets/hasse_cython.pyx @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +r""" +Some fast computations for finite posets +""" +# **************************************************************************** +# Copyright (C) 2020 Frédéric Chapoton +# +# 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 cysignals.signals cimport sig_check +from cysignals.memory cimport sig_malloc, sig_free + +from sage.libs.flint.fmpz cimport * +from sage.libs.flint.fmpz_mat cimport * + +from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense + +from sage.rings.integer_ring import ZZ +from sage.matrix.matrix_space import MatrixSpace + + +cpdef Matrix_integer_dense moebius_matrix_fast(list positions): + """ + Compute the Möbius matrix of a poset by a specific triangular inversion. + + INPUT: + + a list of sets describing the poset, as given by the + lazy attribute ``_leq_storage`` of Hasse diagrams. + + OUTPUT: + + a dense matrix + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_cython import moebius_matrix_fast + sage: D = [{0,1},{1}] + sage: moebius_matrix_fast(D) + [ 1 -1] + [ 0 1] + sage: P = posets.TamariLattice(5) + sage: H = P._hasse_diagram + sage: D = H._leq_storage + sage: moebius_matrix_fast(D) + 42 x 42 dense matrix over Integer Ring (...) + """ + cdef Matrix_integer_dense A + cdef Py_ssize_t n = len(positions) + cdef Py_ssize_t i + cdef int j, k + A = Matrix_integer_dense.__new__(Matrix_integer_dense, + MatrixSpace(ZZ, n, n), None, None, None) + fmpz_mat_one(A._matrix) + cdef Py_ssize_t *pos_lens = sig_malloc(n*sizeof(Py_ssize_t)) + cdef int **pos_array = sig_malloc(n*sizeof(int*)) + cdef Py_ssize_t jind, kind + for i in range(n): + pos_lens[i] = len(positions[i]) + pos_array[i] = sig_malloc(pos_lens[i]*sizeof(int)) + for jind,j in enumerate(positions[i]): + pos_array[i][jind] = j + + for i in range(n - 1, -1, -1): + sig_check() + for jind in range(pos_lens[i]): + j = pos_array[i][jind] + if j != i: + for kind in range(pos_lens[j]): + k = pos_array[j][kind] + fmpz_sub(fmpz_mat_entry(A._matrix, i, k), + fmpz_mat_entry(A._matrix, i, k), + fmpz_mat_entry(A._matrix, j, k)) + for i in range(n): + sig_free(pos_array[i]) + sig_free(pos_array) + sig_free(pos_lens) + return A + + +cpdef Matrix_integer_dense coxeter_matrix_fast(list positions): + """ + Compute the Coxeter matrix of a poset by a specific algorithm. + + INPUT: + + a list of sets describing the poset, as given by the + lazy attribute ``_leq_storage`` of Hasse diagrams. + + OUTPUT: + + a dense matrix + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_cython import coxeter_matrix_fast + sage: D = [{0,1},{1}] + sage: coxeter_matrix_fast(D) + [ 0 -1] + [ 1 -1] + sage: P = posets.TamariLattice(5) + sage: H = P._hasse_diagram + sage: D = H._leq_storage + sage: coxeter_matrix_fast(D) + 42 x 42 dense matrix over Integer Ring (...) + """ + cdef Matrix_integer_dense A + cdef Py_ssize_t n = len(positions) + cdef Py_ssize_t i + cdef int j, k + A = Matrix_integer_dense.__new__(Matrix_integer_dense, + MatrixSpace(ZZ, n, n), None, None, None) + fmpz_mat_one(A._matrix) + cdef Py_ssize_t *pos_lens = sig_malloc(n*sizeof(Py_ssize_t)) + cdef int **pos_array = sig_malloc(n*sizeof(int*)) + cdef Py_ssize_t jind, kind + for i in range(n): + pos_lens[i] = len(positions[i]) + pos_array[i] = sig_malloc(pos_lens[i]*sizeof(int)) + for jind,j in enumerate(positions[i]): + pos_array[i][jind] = j + + for i in range(n - 1, -1, -1): + sig_check() + for jind in range(pos_lens[i]): + j = pos_array[i][jind] + if j != i: + for kind in range(pos_lens[j]): + k = pos_array[j][kind] + fmpz_sub(fmpz_mat_entry(A._matrix, k, i), + fmpz_mat_entry(A._matrix, k, i), + fmpz_mat_entry(A._matrix, k, j)) + for i in range(n): + sig_check() + for jind in range(pos_lens[i]): + j = pos_array[i][jind] + if j != i: + for k in range(n): + fmpz_add(fmpz_mat_entry(A._matrix, i, k), + fmpz_mat_entry(A._matrix, i, k), + fmpz_mat_entry(A._matrix, j, k)) + for i in range(n): + sig_free(pos_array[i]) + sig_free(pos_array) + sig_free(pos_lens) + fmpz_mat_scalar_mul_si(A._matrix, A._matrix, -1) + return A + diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 334a1a0985d..7160f629f6c 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -15,18 +15,18 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from __future__ import print_function - -from six.moves import range - from sage.graphs.digraph import DiGraph from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ from sage.rings.finite_rings.finite_field_constructor import GF from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method +from sage.functions.other import binomial from sage.misc.rest_index_of_methods import gen_rest_table_index +from sage.combinat.posets.hasse_cython import (moebius_matrix_fast, + coxeter_matrix_fast) +from collections import deque class LatticeError(ValueError): """ @@ -101,7 +101,7 @@ def linear_extension(self): r""" Return a linear extension - TESTS:: + EXAMPLES:: sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: H = HasseDiagram({0:[1,2],1:[3],2:[3],3:[]}) @@ -115,7 +115,7 @@ def linear_extensions(self): r""" Return an iterator over all linear extensions. - TESTS:: + EXAMPLES:: sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: H = HasseDiagram({0:[1,2],1:[3],2:[3],3:[]}) @@ -146,7 +146,7 @@ def greedy_linear_extensions_iterator(self): [0, 1, 2, 3, 4] [0, 2, 3, 1, 4] - TESTS: + TESTS:: sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: list(HasseDiagram({}).greedy_linear_extensions_iterator()) @@ -163,17 +163,16 @@ def greedy_rec(H, linext): S = [] if linext: - S = [x for x in H.neighbors_out(linext[-1]) - if all(low in linext for low in H.neighbors_in(x))] + S = [x for x in H.neighbor_out_iterator(linext[-1]) + if all(low in linext for low in H.neighbor_in_iterator(x))] if not S: S_ = set(self).difference(set(linext)) S = [x for x in S_ - if not any(low in S_ for low in self.neighbors_in(x))] + if not any(low in S_ + for low in self.neighbor_in_iterator(x))] for e in S: - # Python3-todo: use yield from - for tmp in greedy_rec(H, linext + [e]): - yield tmp + yield from greedy_rec(H, linext + [e]) return greedy_rec(self, []) @@ -229,16 +228,14 @@ def supergreedy_rec(H, linext): if not k: # Start from new minimal element S = [x for x in self.sources() if x not in linext] else: - S = [x for x in self.neighbors_out(linext[k - 1]) + S = [x for x in self.neighbor_out_iterator(linext[k - 1]) if x not in linext and all(low in linext - for low in self.neighbors_in(x))] + for low in self.neighbor_in_iterator(x))] k -= 1 for e in S: - # Python3-todo: use yield from - for tmp in supergreedy_rec(H, linext + [e]): - yield tmp + yield from supergreedy_rec(H, linext + [e]) return supergreedy_rec(self, []) @@ -246,7 +243,7 @@ def is_linear_extension(self, lin_ext=None): r""" Test if an ordering is a linear extension. - TESTS:: + EXAMPLES:: sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: H = HasseDiagram({0:[1,2],1:[3],2:[3],3:[]}) @@ -256,35 +253,29 @@ def is_linear_extension(self, lin_ext=None): False """ if lin_ext is None or lin_ext == list(range(len(self))): - for x, y in self.cover_relations_iterator(): - if not x < y: - return False - return True + return all(x < y for x, y in self.cover_relations_iterator()) else: - for x, y in self.cover_relations_iterator(): - if not lin_ext.index(x) < lin_ext.index(y): - return False - return True + return all(lin_ext.index(x) < lin_ext.index(y) + for x, y in self.cover_relations_iterator()) def cover_relations_iterator(self): r""" Iterate over cover relations. - TESTS:: + EXAMPLES:: sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: H = HasseDiagram({0:[2,3], 1:[3,4], 2:[5], 3:[5], 4:[5]}) sage: list(H.cover_relations_iterator()) [(0, 2), (0, 3), (1, 3), (1, 4), (2, 5), (3, 5), (4, 5)] """ - for u, v, l in self.edge_iterator(): - yield (u, v) + yield from self.edge_iterator(labels=False) def cover_relations(self): r""" Return the list of cover relations. - TESTS:: + EXAMPLES:: sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: H = HasseDiagram({0:[2,3], 1:[3,4], 2:[5], 3:[5], 4:[5]}) @@ -295,15 +286,15 @@ def cover_relations(self): def is_lequal(self, i, j): """ - Return True if i is less than or equal to j in the poset, and - False otherwise. + Return ``True`` if i is less than or equal to j in the poset, and + ``False`` otherwise. .. note:: If the :meth:`lequal_matrix` has been computed, then this method is redefined to use the cached data (see :meth:`_alternate_is_lequal`). - TESTS:: + EXAMPLES:: sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: H = HasseDiagram({0:[2], 1:[2], 2:[3], 3:[4], 4:[]}) @@ -323,10 +314,10 @@ def is_lequal(self, i, j): def is_less_than(self, x, y): r""" - Return True if ``x`` is less than or equal to ``y`` in the - poset, and False otherwise. + Return ``True`` if ``x`` is less than but not equal to ``y`` in the + poset, and ``False`` otherwise. - TESTS:: + EXAMPLES:: sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: H = HasseDiagram({0:[2], 1:[2], 2:[3], 3:[4], 4:[]}) @@ -344,8 +335,7 @@ def is_less_than(self, x, y): """ if x == y: return False - else: - return self.is_lequal(x, y) + return self.is_lequal(x, y) def is_gequal(self, x, y): r""" @@ -445,7 +435,7 @@ def bottom(self): def has_bottom(self): """ - Return True if the poset has a unique minimal element. + Return ``True`` if the poset has a unique minimal element. EXAMPLES:: @@ -494,8 +484,8 @@ def has_top(self): def is_bounded(self): """ - Return True if the poset contains a unique maximal element and a - unique minimal element, and False otherwise. + Return ``True`` if the poset contains a unique maximal element and a + unique minimal element, and ``False`` otherwise. EXAMPLES:: @@ -510,7 +500,7 @@ def is_bounded(self): def is_chain(self): """ - Return True if the poset is totally ordered, and False otherwise. + Return ``True`` if the poset is totally ordered, and ``False`` otherwise. EXAMPLES:: @@ -558,6 +548,10 @@ def dual(self): """ Return a poset that is dual to the given poset. + This means that it has the same elements but opposite order. + The elements are renumbered to ensure that ``range(n)`` + is a linear extension. + EXAMPLES:: sage: P = posets.IntegerPartitions(4) @@ -597,13 +591,11 @@ def _precompute_intervals(self): True """ n = self.order() - - v_up = [frozenset(self.depth_first_search(v)) for v in range(n)] + v_up = (frozenset(self.depth_first_search(v)) for v in range(n)) v_down = [frozenset(self.depth_first_search(v, neighbors=self.neighbors_in)) for v in range(n)] self._intervals = [[sorted(up.intersection(down)) for down in v_down] for up in v_up] - self.interval = self._alternate_interval def _alternate_interval(self, x, y): @@ -626,15 +618,15 @@ def _alternate_interval(self, x, y): sage: P._hasse_diagram._precompute_intervals() sage: P.interval(1, 7) # Uses this function [1, 3, 5, 7] - """ return self._intervals[x][y] def interval(self, x, y): r""" Return a list of the elements `z` of ``self`` such that - `x \leq z \leq y`. The order is that induced by the - ordering in ``self.linear_extension``. + `x \leq z \leq y`. + + The order is that induced by the ordering in ``self.linear_extension``. INPUT: @@ -659,9 +651,9 @@ def interval(self, x, y): def open_interval(self, x, y): """ - Return a list of the elements `z` of ``self`` such that - `x < z < y`. The order is that induced by the ordering in - ``self.linear_extension``. + Return a list of the elements `z` of ``self`` such that `x < z < y`. + + The order is that induced by the ordering in ``self.linear_extension``. EXAMPLES:: @@ -675,7 +667,7 @@ def open_interval(self, x, y): [] """ ci = self.interval(x, y) - if len(ci) == 0: + if not ci: return [] else: return ci[1:-1] @@ -782,12 +774,12 @@ def _rank(self): # look at the neighbors of y and set the ranks; # then look at the neighbors of the neighbors ... y = queue.pop() - for x in self.neighbors_out(y): + for x in self.neighbor_out_iterator(y): if rank[x] is None: rank[x] = rank[y] + 1 queue.add(x) component.add(x) - for x in self.neighbors_in(y): + for x in self.neighbor_in_iterator(y): if rank[x] is None: rank[x] = rank[y] - 1 queue.add(x) @@ -830,7 +822,7 @@ def rank(self, element=None): def is_ranked(self): r""" - Return True if the poset is ranked, and False otherwise. + Return ``True`` if the poset is ranked, and ``False`` otherwise. A poset is *ranked* if it admits a rank function. For more information about the rank function, see :meth:`~rank_function` @@ -849,7 +841,7 @@ def is_ranked(self): def covers(self, x, y): """ - Return True if y covers x and False otherwise. + Return ``True`` if y covers x and ``False`` otherwise. EXAMPLES:: @@ -954,31 +946,29 @@ def moebius_function(self, i, j): # dumb algorithm self._moebius_function_values[(i, j)] = -sum(self.moebius_function(i, k) for k in ci[:-1]) return self._moebius_function_values[(i, j)] - def moebius_function_matrix(self, algorithm='recursive'): + def moebius_function_matrix(self, algorithm='cython'): r""" Return the matrix of the Möbius function of this poset. - This returns the sparse matrix over `\ZZ` whose ``(x, y)`` entry + This returns the matrix over `\ZZ` whose ``(x, y)`` entry is the value of the Möbius function of ``self`` evaluated on - ``x`` and ``y``, and redefines :meth:`moebius_function` to use - it. + ``x`` and ``y``, and redefines :meth:`moebius_function` to use it. INPUT: - - ``algorithm`` -- optional, ``'recursive'`` (default) or ``'matrix'`` + - ``algorithm`` -- optional, ``'recursive'``, ``'matrix'`` + or ``'cython'`` (default) - This uses either the recursive formula or one matrix inversion. + This uses either the recursive formula, a generic matrix inversion + or a specific matrix inversion coded in Cython. - .. NOTE:: + OUTPUT: - The result is cached in :meth:`_moebius_function_matrix`. + a dense matrix for the algorithm ``cython``, a sparse matrix otherwise - .. TODO:: + .. NOTE:: - Try to make a specific multimodular matrix inversion - algorithm for this kind of sparse triangular matrices - where the non-zero entries of the inverse are in known - positions. + The result is cached in :meth:`_moebius_function_matrix`. .. SEEALSO:: :meth:`lequal_matrix`, :meth:`coxeter_transformation` @@ -1007,12 +997,23 @@ def moebius_function_matrix(self, algorithm='recursive'): True sage: H = posets.TamariLattice(3)._hasse_diagram - sage: H.moebius_function_matrix('matrix') + sage: M = H.moebius_function_matrix('matrix'); M [ 1 -1 -1 0 1] [ 0 1 0 0 -1] [ 0 0 1 -1 0] [ 0 0 0 1 -1] [ 0 0 0 0 1] + sage: _ = H.__dict__.pop('_moebius_function_matrix') + sage: H.moebius_function_matrix('cython') == M + True + sage: _ = H.__dict__.pop('_moebius_function_matrix') + sage: H.moebius_function_matrix('recursive') == M + True + sage: _ = H.__dict__.pop('_moebius_function_matrix') + sage: H.moebius_function_matrix('banana') + Traceback (most recent call last): + ... + ValueError: unknown algorithm """ if not hasattr(self, '_moebius_function_matrix'): if algorithm == 'recursive': @@ -1030,8 +1031,12 @@ def moebius_function_matrix(self, algorithm='recursive'): for j in available if k in greater_than[j]) M = matrix(ZZ, n, n, m, sparse=True) - else: + elif algorithm == "matrix": M = self.lequal_matrix().inverse_of_unit() + elif algorithm == "cython": + M = moebius_matrix_fast(self._leq_storage) + else: + raise ValueError("unknown algorithm") self._moebius_function_matrix = M self._moebius_function_matrix.set_immutable() self.moebius_function = self._moebius_function_from_matrix @@ -1059,30 +1064,51 @@ def _moebius_function_from_matrix(self, i, j): return self._moebius_function_matrix[i, j] @cached_method - def coxeter_transformation(self): + def coxeter_transformation(self, algorithm='cython'): r""" Return the matrix of the Auslander-Reiten translation acting on the Grothendieck group of the derived category of modules on the poset, in the basis of simple modules. + INPUT: + + - ``algorithm`` -- optional, ``'cython'`` (default) or ``'matrix'`` + + This uses either a specific matrix code in Cython, or generic matrices. + .. SEEALSO:: :meth:`lequal_matrix`, :meth:`moebius_function_matrix` EXAMPLES:: - sage: M = posets.PentagonPoset()._hasse_diagram.coxeter_transformation(); M + sage: P = posets.PentagonPoset()._hasse_diagram + sage: M = P.coxeter_transformation(); M [ 0 0 0 0 -1] [ 0 0 0 1 -1] [ 0 1 0 0 -1] [-1 1 1 0 -1] [-1 1 0 1 -1] + sage: P.__dict__['coxeter_transformation'].clear_cache() + sage: P.coxeter_transformation(algorithm="matrix") == M + True TESTS:: - sage: M = posets.PentagonPoset()._hasse_diagram.coxeter_transformation() + sage: P = posets.PentagonPoset()._hasse_diagram + sage: M = P.coxeter_transformation() sage: M**8 == 1 True + sage: P.__dict__['coxeter_transformation'].clear_cache() + sage: P.coxeter_transformation(algorithm="banana") + Traceback (most recent call last): + ... + ValueError: unknown algorithm """ - return - self.lequal_matrix() * self.moebius_function_matrix().transpose() + if algorithm == 'matrix': + return - self.lequal_matrix() * self.moebius_function_matrix().transpose() + elif algorithm == 'cython': + return coxeter_matrix_fast(self._leq_storage) + else: + raise ValueError("unknown algorithm") def order_filter(self, elements): r""" @@ -1097,7 +1123,7 @@ def order_filter(self, elements): sage: H.order_filter([3,8]) [3, 7, 8, 9, 10, 11, 12, 13, 14, 15] """ - return sorted(list(self.depth_first_search(elements))) + return sorted(self.depth_first_search(elements)) def principal_order_filter(self, i): """ @@ -1124,8 +1150,34 @@ def order_ideal(self, elements): sage: H.order_ideal([7,10]) [0, 1, 2, 3, 4, 5, 6, 7, 8, 10] """ - return sorted(list( - self.depth_first_search(elements, neighbors=self.neighbors_in))) + return sorted(self.depth_first_search(elements, + neighbors=self.neighbors_in)) + + def order_ideal_cardinality(self, elements): + r""" + Return the cardinality of the order ideal generated by ``elements``. + + `I` is an order ideal if, for any `x` in `I` and `y` such that + `y \le x`, then `y` is in `I`. + + EXAMPLES:: + + sage: H = posets.BooleanLattice(4)._hasse_diagram + sage: H.order_ideal_cardinality([7,10]) + 10 + """ + seen = set() + q = deque(elements) + size = 0 + while q: + v = q.popleft() + if v in seen: + continue + size += 1 + seen.add(v) + q.extend(self.neighbors_in(v)) + + return size def principal_order_ideal(self, i): """ @@ -1156,7 +1208,7 @@ def _leq_storage(self): greater_than = [set([i]) for i in range(n)] for i in range(n - 1, -1, -1): gt = greater_than[i] - for j in self.neighbors_out(i): + for j in self.neighbor_out_iterator(i): gt = gt.union(greater_than[j]) greater_than[i] = gt @@ -1224,12 +1276,9 @@ def _leq_matrix(self): Integer Ring """ n = self.order() - one = ZZ.one() greater_than = self._leq_storage - D = {(i, j): one for i in range(n) for j in greater_than[i]} - M = matrix(ZZ, n, n, D, sparse=True) - M.set_immutable() - return M + D = {(i, j): 1 for i in range(n) for j in greater_than[i]} + return matrix(ZZ, n, n, D, sparse=True, immutable=True) def lequal_matrix(self, boolean=False): r""" @@ -1430,8 +1479,10 @@ def _meet(self): def meet_matrix(self): r""" - Return the matrix of meets of ``self``. The ``(x,y)``-entry of - this matrix is the meet of ``x`` and ``y`` in ``self``. + Return the matrix of meets of ``self``. + + The ``(x,y)``-entry of this matrix is the meet of ``x`` and + ``y`` in ``self``. This algorithm is modelled after the algorithm of Freese-Jezek-Nation (p217). It can also be found on page 140 of [Gec81]_. @@ -1963,13 +2014,12 @@ def recursive_fit(orthocomplements, unbinded): new_unbinded = unbinded[1:] # Remove next_to_fit new_unbinded.remove(e) - for i_want_python3_yield_from in recursive_fit(new_binded, new_unbinded): - yield i_want_python3_yield_from + yield from recursive_fit(new_binded, new_unbinded) start = [None] * n # A little optimization for e in range(n): - if len(comps[e]) == 0: # Not any possible orthocomplement + if not comps[e]: # Not any possible orthocomplement return if len(comps[e]) == 1: # Do not re-fit this every time e_ = comps[e][0] @@ -1984,8 +2034,7 @@ def recursive_fit(orthocomplements, unbinded): start[e_] = e start_unbinded = [e for e in range(n) if start[e] is None] - for i_want_python3_yield_from in recursive_fit(start, start_unbinded): - yield i_want_python3_yield_from + yield from recursive_fit(start, start_unbinded) def find_nonsemimodular_pair(self, upper): """ @@ -2102,7 +2151,7 @@ def are_incomparable(self, i, j): INPUT: - - ``i``, ``j`` -- vertices of this Hasse diagram + - ``i``, ``j`` -- vertices of this Hasse diagram EXAMPLES:: @@ -2142,7 +2191,7 @@ def antichains(self, element_class=list): INPUT: - - ``element_class`` -- (default:list) an iterable type + - ``element_class`` -- (default:list) an iterable type EXAMPLES:: @@ -2162,8 +2211,6 @@ def antichains(self, element_class=list): sage: TestSuite(A).run() - TESTS:: - sage: A = Poset()._hasse_diagram.antichains() sage: list(A) [[]] @@ -2211,7 +2258,7 @@ def chains(self, element_class=list, exclude=None): [[], [0], [0, 1], [0, 2], [1], [2]] The ``element_class`` keyword determines how the chains are - being returned: + being returned:: sage: P = Poset({1: [2, 3], 2: [4]}) sage: list(P._hasse_diagram.chains(element_class=tuple)) @@ -2219,7 +2266,7 @@ def chains(self, element_class=list, exclude=None): sage: list(P._hasse_diagram.chains()) [[], [0], [0, 1], [0, 1, 2], [0, 2], [0, 3], [1], [1, 2], [2], [3]] - (Note that taking the Hasse diagram has renamed the vertices.) + (Note that taking the Hasse diagram has renamed the vertices.) :: sage: list(P._hasse_diagram.chains(element_class=tuple, exclude=[0])) [(), (1,), (1, 2), (2,), (3,)] @@ -2235,6 +2282,75 @@ def chains(self, element_class=list, exclude=None): self.are_comparable, element_class=element_class) + def diamonds(self): + r""" + Return the list of diamonds of ``self``. + + A diamond is the following subgraph of the Hasse diagram:: + + z + / \ + x y + \ / + w + + Thus each edge represents a cover relation in the Hasse diagram. + We represent his as the tuple `(w, x, y, z)`. + + OUTPUT: + + A tuple with + + - a list of all diamonds in the Hasse Diagram, + - a boolean checking that every `w,x,y` that form a ``V``, there is a + unique element `z`, which completes the diamond. + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: H = HasseDiagram({0: [1,2], 1: [3], 2: [3], 3: []}) + sage: H.diamonds() + ([(0, 1, 2, 3)], True) + + sage: P = posets.YoungDiagramPoset(Partition([3, 2, 2])) + sage: H = P._hasse_diagram + sage: H.diamonds() + ([(0, 1, 3, 4), (3, 4, 5, 6)], False) + """ + diamonds = [] + all_diamonds_completed = True + for w in self.vertices(): + covers = self.neighbors_out(w) + for i, x in enumerate(covers): + for y in covers[i+1:]: + zs = self.common_upper_covers([x, y]) + if len(zs) != 1: + all_diamonds_completed = False + for z in zs: + diamonds.append((w, x, y, z)) + return (diamonds, all_diamonds_completed) + + def common_upper_covers(self, vertices): + r""" + Return the list of all common upper covers of ``vertices``. + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: H = HasseDiagram({0: [1,2], 1: [3], 2: [3], 3: []}) + sage: H.common_upper_covers([1, 2]) + [3] + + sage: from sage.combinat.posets.poset_examples import Posets + sage: H = Posets.YoungDiagramPoset(Partition([3, 2, 2]))._hasse_diagram + sage: H.common_upper_covers([4, 5]) + [6] + """ + covers = set(self.neighbors_out(vertices.pop())) + for v in vertices: + covers = covers.intersection(self.neighbors_out(v)) + return list(covers) + def _trivial_nonregular_congruence(self): """ Return a pair of elements giving "trivial" non-regular congruence. @@ -2308,7 +2424,6 @@ def sublattices_iterator(self, elms, min_e): sage: next(it) {0} """ - # Python3-note: "yield from" would be simpler. yield elms for e in range(min_e, self.cardinality()): if e in elms: @@ -2326,8 +2441,7 @@ def sublattices_iterator(self, elms, min_e): gens.add(self._join[x, g]) current_set.add(g) else: - for x in self.sublattices_iterator(current_set, e + 1): - yield x + yield from self.sublattices_iterator(current_set, e + 1) def maximal_sublattices(self): """ @@ -2495,10 +2609,10 @@ def kappa_dual(self, a): if self.in_degree(uc) == 1: return uc lt_a = set(self.depth_first_search(a, neighbors=self.neighbors_in)) - tmp = list(self.depth_first_search(uc, neighbors=lambda v: [v_ for v_ in self.neighbors_in(v) if v_ not in lt_a])) + tmp = list(self.depth_first_search(uc, neighbors=lambda v: [v_ for v_ in self.neighbor_in_iterator(v) if v_ not in lt_a])) result = None for e in tmp: - if all(x not in tmp for x in self.neighbors_in(e)): + if all(x not in tmp for x in self.neighbor_in_iterator(e)): if result: return None result = e @@ -2735,10 +2849,10 @@ def kappa(self, a): if self.out_degree(lc) == 1: return lc gt_a = set(self.depth_first_search(a)) - tmp = list(self.depth_first_search(lc, neighbors=lambda v: [v_ for v_ in self.neighbors_out(v) if v_ not in gt_a])) + tmp = list(self.depth_first_search(lc, neighbors=lambda v: [v_ for v_ in self.neighbor_out_iterator(v) if v_ not in gt_a])) result = None for e in tmp: - if all(x not in tmp for x in self.neighbors_out(e)): + if all(x not in tmp for x in self.neighbor_out_iterator(e)): if result: return None result = e @@ -3171,5 +3285,163 @@ def is_congruence_normal(self): return True + @staticmethod + def _glue_spectra(a_spec, b_spec, orientation): + r""" + Return the `a`-spectrum of a poset by merging ``a_spec`` and ``b_spec``. + + ``a_spec`` and ``b_spec`` are the `a`-spectrum and `b`-spectrum of two different + posets (see :meth:`atkinson` for the definition of `a`-spectrum). + + The orientation determines whether `a < b` or `b < a` in the combined poset. + + This is a helper method for :meth:`atkinson`. + + INPUT: + + - ``a_spec`` -- list; the `a`-spectrum of a poset `P` + + - ``b_spec`` -- list; the `b`-spectrum of a poset `Q` + + - ``orientation`` -- boolean; ``True`` if `a < b`, ``False`` otherwise + + OUTPUT: + + The `a`-spectrum (or `b`-spectrum, depending on orientation), + returned as a list, of the poset which is a disjoint union + of `P` and `Q`, together with the additional + covering relation `a < b`. + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: Pdata = [0, 1, 2, 0] + sage: Qdata = [1, 1, 0] + sage: HasseDiagram._glue_spectra(Pdata, Qdata, True) + [0, 20, 28, 18, 0, 0, 0] + + sage: Pdata = [0, 0, 2] + sage: Qdata = [0, 1] + sage: HasseDiagram._glue_spectra(Pdata, Qdata, False) + [0, 0, 0, 0, 8] + """ + new_a_spec = [] + + if not orientation: + a_spec, b_spec = b_spec, a_spec + + p = len(a_spec) + q = len(b_spec) + + for r in range(1, p+q+1): + new_a_spec.append(0) + for i in range(max(1, r-q), min(p, r) + 1): + k_val = binomial(r-1, i-1) * binomial(p+q-r, p-i) + if orientation: + inner_sum = sum(b_spec[j-1] for j in range(r-i + 1, len(b_spec) + 1)) + else: + inner_sum = sum(b_spec[j-1] for j in range(1, r-i + 1)) + new_a_spec[-1] = new_a_spec[-1] + (a_spec[i-1] * k_val * inner_sum) + + return new_a_spec + + def _split(self, a, b): + r""" + Return the two connected components obtained by deleting the covering + relation `a < b` from a Hasse diagram that is a tree. + + This is a helper method for :meth:`FinitePoset.atkinson`. + + INPUT: + + - ``a`` -- an element of the poset + - ``b`` -- an element of the poset which covers ``a`` + + OUTPUT: + + A list containing two posets which are the connected components + of this poset after deleting the covering relation `a < b`. + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: H = HasseDiagram({0: [1, 2], 1: [], 2: []}) + sage: H._split(0, 1) + [Hasse diagram of a poset containing 2 elements, Hasse diagram of a poset containing 1 elements] + + sage: H = HasseDiagram({0: [1], 1: [2], 2: [3], 3: [4], 4: []}) + sage: H._split(1, 2) + [Hasse diagram of a poset containing 2 elements, Hasse diagram of a poset containing 3 elements] + + TESTS:: + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: H = HasseDiagram({0: [1,2,3], 1: [4, 5], 2: [4, 6], 3: [5, 6], 4: [7], 5: [7], 6: [7], 7: []}) + sage: H._split(0, 1) + Traceback (most recent call last): + ... + ValueError: wrong number of connected components after the covering relation is deleted + + sage: H = HasseDiagram({0: [1], 1: [], 2: []}) + sage: H._split(0, 1) + Traceback (most recent call last): + ... + ValueError: wrong number of connected components after the covering relation is deleted + """ + split_hasse = self.copy(self) + split_hasse.delete_edge(a,b) + components = split_hasse.connected_components_subgraphs() + if not len(components) == 2: + raise ValueError("wrong number of connected components after the covering relation is deleted") + + c1, c2 = components + if a in c2: + c1, c2 = c2, c1 + + return [c1, c2] + + def _spectrum_of_tree(self, a): + r""" + Return the `a`-spectrum of a poset whose underlying graph is a tree. + + This is a helper method for :meth:`FinitePoset.atkinson`. + + INPUT: + + - ``a`` -- an element of the poset + + OUTPUT: + + The `a`-spectrum of this poset, returned as a list. + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: H = HasseDiagram({0: [2], 1: [2], 2: [3, 4], 3: [], 4: []}) + sage: H._spectrum_of_tree(0) + [2, 2, 0, 0, 0] + + sage: H = HasseDiagram({0: [2], 1: [2], 2: [3, 4], 3: [], 4: []}) + sage: H._spectrum_of_tree(2) + [0, 0, 4, 0, 0] + + sage: H = HasseDiagram({0: [2], 1: [2], 2: [3, 4], 3: [], 4: []}) + sage: H._spectrum_of_tree(3) + [0, 0, 0, 2, 2] + """ + upper_covers = self.neighbors_out(a) + lower_covers = self.neighbors_in(a) + if not upper_covers and not lower_covers: + return [1] + if upper_covers: + b = upper_covers[0] + orientation = True + else: + (a, b) = (lower_covers[0], a) + orientation = False + P, Q = self._split(a, b) + a_spec = P._spectrum_of_tree(a) + b_spec = Q._spectrum_of_tree(b) + return HasseDiagram._glue_spectra(a_spec, b_spec, orientation) + __doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(HasseDiagram)) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 66d08b4f3e7..f9d892e00e5 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -146,8 +146,6 @@ # # https://www.gnu.org/licenses/ # ***************************************************************************** -from six.moves import range -from six import iteritems from sage.categories.finite_lattice_posets import FiniteLatticePosets from sage.combinat.posets.posets import Poset, FinitePoset @@ -228,26 +226,7 @@ class FiniteMeetSemilattice(FinitePoset): sage: TestSuite(M).run() """ Element = MeetSemilatticeElement - - def _repr_(self): - r""" - TESTS:: - - sage: M = MeetSemilattice([[1,2],[3],[3]]) - sage: M._repr_() - 'Finite meet-semilattice containing 3 elements' - - :: - - sage: P = Poset([[1,2],[3],[3]]) - sage: M = MeetSemilattice(P) - sage: M._repr_() - 'Finite meet-semilattice containing 3 elements' - """ - s = "Finite meet-semilattice containing %s elements" % self._hasse_diagram.order() - if self._with_linear_extension: - s += " with distinguished linear extension" - return s + _desc = 'Finite meet-semilattice' def meet_matrix(self): """ @@ -568,26 +547,7 @@ class FiniteJoinSemilattice(FinitePoset): """ Element = JoinSemilatticeElement - - def _repr_(self): - r""" - TESTS:: - - sage: J = JoinSemilattice([[1,2],[3],[3]]) - sage: J._repr_() - 'Finite join-semilattice containing 3 elements' - - :: - - sage: P = Poset([[1,2],[3],[3]]) - sage: J = JoinSemilattice(P) - sage: J._repr_() - 'Finite join-semilattice containing 3 elements' - """ - s = "Finite join-semilattice containing %s elements" % self._hasse_diagram.order() - if self._with_linear_extension: - s += " with distinguished linear extension" - return s + _desc = 'Finite join-semilattice' def join_matrix(self): """ @@ -1830,7 +1790,7 @@ def is_relatively_complemented(self, certificate=False): for e1 in range(n - 1): C = Counter(flatten([H.neighbors_out(e2) for e2 in H.neighbors_out(e1)])) - for e3, c in iteritems(C): + for e3, c in C.items(): if c == 1 and len(H.closed_interval(e1, e3)) == 3: if not certificate: return False diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index 1ca301006e6..60dbf6ff4a4 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -6,6 +6,8 @@ - :class:`LinearExtensionOfPoset` - :class:`LinearExtensionsOfPoset` +- :class:`LinearExtensionsOfPosetWithHooks` +- :class:`LinearExtensionsOfForest` Classes and methods ------------------- @@ -25,8 +27,6 @@ # http://www.gnu.org/licenses/ #**************************************************************************** from __future__ import print_function -from six.moves import range -from six import add_metaclass from sage.rings.rational_field import QQ from sage.structure.unique_representation import UniqueRepresentation @@ -36,10 +36,12 @@ from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.graphs.dot2tex_utils import have_dot2tex from sage.structure.list_clone import ClonableArray +from sage.misc.misc_c import prod +from sage.functions.other import factorial -@add_metaclass(InheritComparisonClasscallMetaclass) -class LinearExtensionOfPoset(ClonableArray): +class LinearExtensionOfPoset(ClonableArray, + metaclass=InheritComparisonClasscallMetaclass): r""" A linear extension of a finite poset `P` of size `n` is a total ordering `\pi := \pi_0 \pi_1 \ldots \pi_{n-1}` of its elements @@ -417,7 +419,6 @@ class LinearExtensionsOfPoset(UniqueRepresentation, Parent): .. SEEALSO:: - :meth:`sage.combinat.posets.posets.FinitePoset.linear_extensions` - - :class:`sage.graphs.linearextensions.LinearExtensions` EXAMPLES:: @@ -845,3 +846,51 @@ def _element_constructor_(self, lst, check=True): return self.element_class(self, lst, check) Element = LinearExtensionOfPoset + +class LinearExtensionsOfPosetWithHooks(LinearExtensionsOfPoset): + r""" + Linear extensions such that the poset has well-defined + hook lengths (i.e., d-complete). + """ + def cardinality(self): + r""" + Count the number of linear extensions using a hook-length formula. + + EXAMPLES:: + + sage: from sage.combinat.posets.poset_examples import Posets + sage: P = Posets.YoungDiagramPoset(Partition([3,2]), dual=True) + sage: P.linear_extensions().cardinality() + 5 + """ + num_elmts = self._poset.cardinality() + + if num_elmts == 0: + return 1 + + hooks = self._poset.get_hooks() + hook_product = prod(hooks.values()) + return factorial(num_elmts) // hook_product + +class LinearExtensionsOfForest(LinearExtensionsOfPoset): + r""" + Linear extensions such that the poset is a forest. + """ + def cardinality(self): + r""" + Use Atkinson's algorithm to compute the number of linear extensions. + + EXAMPLES:: + + sage: from sage.combinat.posets.forest import ForestPoset + sage: from sage.combinat.posets.poset_examples import Posets + sage: P = Poset({0: [2], 1: [2], 2: [3, 4], 3: [], 4: []}) + sage: P.linear_extensions().cardinality() + 4 + + sage: Q = Poset({0: [1], 1: [2, 3], 2: [], 3: [], 4: [5, 6], 5: [], 6: []}) + sage: Q.linear_extensions().cardinality() + 140 + """ + return sum(self.atkinson(self._elements[0])) + diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index fa92da00b37..ccb831771aa 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -1,5 +1,5 @@ r""" -A catalog of posets and lattices. +Catalog of posets and lattices Some common posets can be accessed through the ``posets.`` object:: @@ -81,12 +81,12 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from six import add_metaclass, string_types from sage.misc.classcall_metaclass import ClasscallMetaclass import sage.categories.posets from sage.combinat.permutation import Permutations, Permutation, to_standard from sage.combinat.posets.posets import Poset, FinitePoset, FinitePosets_n +from sage.combinat.posets.d_complete import DCompletePoset from sage.combinat.posets.lattices import (LatticePoset, MeetSemilattice, JoinSemilattice, FiniteLatticePoset) from sage.categories.finite_posets import FinitePosets @@ -95,8 +95,7 @@ from sage.rings.integer import Integer -@add_metaclass(ClasscallMetaclass) -class Posets(object): +class Posets(metaclass=ClasscallMetaclass): r""" A collection of posets and lattices. @@ -849,7 +848,7 @@ def RandomLattice(n, p, properties=None): D.relabel([i-1 for i in Permutations(n).random_element()]) return LatticePoset(D, cover_relations=True) - if isinstance(properties, string_types): + if isinstance(properties, str): properties = set([properties]) else: properties = set(properties) @@ -933,7 +932,7 @@ def covers(x): @staticmethod def SSTPoset(s, f=None): """ - The poset on semistandard tableaux of shape ``s`` and largest + The lattice poset on semistandard tableaux of shape ``s`` and largest entry ``f`` that is ordered by componentwise comparison of the entries. @@ -945,16 +944,18 @@ def SSTPoset(s, f=None): argument. If no maximal number is given, it will use the number of cells in the shape. - NOTE: This is a basic implementation and most certainly - not the most efficient. + .. NOTE:: + + This is a basic implementation and most certainly + not the most efficient. EXAMPLES:: sage: posets.SSTPoset([2,1]) - Finite poset containing 8 elements + Finite lattice containing 8 elements sage: posets.SSTPoset([2,1],4) - Finite poset containing 20 elements + Finite lattice containing 20 elements sage: posets.SSTPoset([2,1],2).cover_relations() [[[[1, 1], [2]], [[1, 2], [2]]]] @@ -968,22 +969,12 @@ def SSTPoset(s, f=None): from sage.combinat.tableau import SemistandardTableaux def tableaux_is_less_than(a, b): - atstring = [] - btstring = [] - for i in a: - atstring += i - for i in b: - btstring += i - for i in range(len(atstring)): - if atstring[i] > btstring[i]: - return False - return True + return all(ix <= iy for x, y in zip(a, b) for ix, iy in zip(x, y)) + if f is None: - f=0 - for i in s: - f += i + f = sum(i for i in s) E = SemistandardTableaux(s, max_entry=f) - return Poset((E, tableaux_is_less_than)) + return LatticePoset((E, tableaux_is_less_than)) @staticmethod def StandardExample(n, facade=None): @@ -1384,31 +1375,50 @@ def UpDownPoset(n, m=1): return Poset((range(n), covers), cover_relations=True) @staticmethod - def YoungDiagramPoset(lam): + def YoungDiagramPoset(lam, dual=False): """ Return the poset of cells in the Young diagram of a partition. INPUT: - ``lam`` -- a partition + - ``dual`` -- (default: ``False``) determines the orientation + of the poset; if ``True``, then it is a join semilattice, + otherwise it is a meet semilattice EXAMPLES:: - sage: P = posets.YoungDiagramPoset(Partition([2,2])); P + sage: P = posets.YoungDiagramPoset(Partition([2, 2])); P Finite meet-semilattice containing 4 elements sage: sorted(P.cover_relations()) [[(0, 0), (0, 1)], [(0, 0), (1, 0)], [(0, 1), (1, 1)], [(1, 0), (1, 1)]] + + sage: posets.YoungDiagramPoset([3, 2], dual=True) + Finite join-semilattice containing 5 elements """ - def cell_leq(a, b): - """ - Nested function that returns `True` if the cell `a` is - to the left or above - the cell `b` in the (English) Young diagram. - """ - return ((a[0] == b[0] - 1 and a[1] == b[1]) or - (a[1] == b[1] - 1 and a[0] == b[0])) - return MeetSemilattice((lam.cells(), cell_leq), cover_relations=True) + from sage.combinat.partition import Partition + lam = Partition(lam) + if dual: + def cell_geq(a, b): + """ + Nested function that returns `True` if the cell `a` is + to the right or below + the cell `b` in the (English) Young diagram. + """ + return ((a[0] == b[0] + 1 and a[1] == b[1]) or + (a[1] == b[1] + 1 and a[0] == b[0])) + return JoinSemilattice((lam.cells(), cell_geq), cover_relations=True) + else: + def cell_leq(a, b): + """ + Nested function that returns `True` if the cell `a` is + to the left or above + the cell `b` in the (English) Young diagram. + """ + return ((a[0] == b[0] - 1 and a[1] == b[1]) or + (a[1] == b[1] - 1 and a[0] == b[0])) + return MeetSemilattice((lam.cells(), cell_leq), cover_relations=True) @staticmethod def YoungsLattice(n): @@ -1542,6 +1552,35 @@ def YoungFibonacci(n): D.relabel(lambda v: Word(v), inplace=True) return FiniteMeetSemilattice(hasse_diagram=D, category=FinitePosets()) + @staticmethod + def DoubleTailedDiamond(n): + r""" + Return a double-tailed diamond of `2n + 2` elements. + + INPUT: + + - ``n`` -- a positive integer + + EXAMPLES:: + + sage: P = posets.DoubleTailedDiamond(2); P + Finite d-complete poset containing 6 elements + sage: P.cover_relations() + [[1, 2], [2, 3], [2, 4], [3, 5], [4, 5], [5, 6]] + """ + try: + n = Integer(n) + except TypeError: + raise TypeError("number of elements must be an integer, not {}".format(n)) + if n <= 0: + raise ValueError("number of elements must be nonnegative, not {}".format(n)) + + edges = [(i, i+1) for i in range(1, n)] + edges.extend([(n, n+1), (n, n+2), (n+1, n+3), (n+2, n+3)]) + edges.extend([(i, i+1) for i in range(n+3, 2*n+2)]) + p = DiGraph([list(range(1, 2*n + 3)), edges]) + return DCompletePoset(p) + @staticmethod def PermutationPattern(n): r""" diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index deac1eff88b..9e04daddfdd 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -256,6 +256,8 @@ :meth:`~FinitePoset.rank` | Return the rank of an element, or the rank of the poset. :meth:`~FinitePoset.rank_function` | Return a rank function of the poset, if it exists. :meth:`~FinitePoset.unwrap` | Unwraps an element of this poset. + :meth:`~FinitePoset.atkinson` | Return the `a`-spectrum of a poset whose undirected Hasse diagram is a forest. + :meth:`~FinitePoset.spectrum` | Return the `a`-spectrum of this poset. Classes and functions --------------------- @@ -273,14 +275,12 @@ # **************************************************************************** from __future__ import division, print_function, absolute_import -from six.moves import range, builtins -from six import iteritems - from copy import copy, deepcopy from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc_c import prod from sage.functions.other import floor +from sage.functions.other import binomial from sage.categories.category import Category from sage.categories.sets_cat import Sets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -299,6 +299,7 @@ from sage.combinat.posets.elements import PosetElement from sage.combinat.combinatorial_map import combinatorial_map from sage.combinat.subset import Subsets +from .linear_extensions import LinearExtensionsOfPoset def Poset(data=None, element_labels=None, cover_relations=False, linear_extension=False, category=None, facade=None, key=None): @@ -719,7 +720,7 @@ def Poset(data=None, element_labels=None, cover_relations=False, linear_extensio vertices = sorted(set(x for item in data for x in item)) if len(vertices) != len(data): # by default, assuming vertices are the range 0..n - vertices = list(range(len(data))) + vertices = range(len(data)) D = DiGraph({v: [u for u in cov if u != v] for v, cov in zip(vertices, data)}, format="dict_of_lists") @@ -920,6 +921,8 @@ class contains. For example, for this class, ``FinitePoset``, sage: Q == P True """ + _lin_ext_type = LinearExtensionsOfPoset + _desc = 'Finite poset' # The parsing of the construction data (like a list of cover relations) # into a :class:`DiGraph` is done in :func:`Poset`. @@ -1369,8 +1372,12 @@ def _repr_(self): sage: P5 = Poset(partitions_of_five) sage: P5._repr_() 'Finite poset containing 7 elements' + + sage: M = MeetSemilattice([[1,2],[3],[3]]) + sage: M._repr_() + 'Finite meet-semilattice containing 3 elements' """ - s = "Finite poset containing %s elements" % self._hasse_diagram.order() + s = "%s containing %s elements" % (self._desc, self._hasse_diagram.order()) if self._with_linear_extension: s += " with distinguished linear extension" return s @@ -1556,7 +1563,7 @@ def linear_extension(self, linear_extension=None, check=True): @cached_method def linear_extensions(self, facade=False): """ - Returns the enumerated set of all the linear extensions of this poset + Return the enumerated set of all the linear extensions of this poset. INPUT: @@ -1627,12 +1634,155 @@ def linear_extensions(self, facade=False): sage: list(D.linear_extensions()) [[0, 1, 2, 3, 4], [0, 2, 1, 3, 4], [0, 2, 1, 4, 3], [0, 2, 4, 1, 3], [0, 1, 2, 4, 3]] """ - from .linear_extensions import LinearExtensionsOfPoset - return LinearExtensionsOfPoset(self, facade = facade) + return self._lin_ext_type(self, facade = facade) + + def spectrum(self, a): + r""" + Return the `a`-spectrum of this poset. + + The `a`-spectrum in a poset `P` is the list of integers whose + `i`-th position contains the number of linear extensions of `P` + that have `a` in the `i`-th location. + + INPUT: + + - ``a`` -- an element of this poset + + OUTPUT: + + The `a`-spectrum of this poset, returned as a list. + + EXAMPLES:: + + sage: P = posets.ChainPoset(5) + sage: P.spectrum(2) + [0, 0, 1, 0, 0] + + sage: P = posets.BooleanLattice(3) + sage: P.spectrum(5) + [0, 0, 0, 4, 12, 16, 16, 0] + + sage: P = posets.YoungDiagramPoset(Partition([3,2,1])) + sage: P.spectrum((0,1)) + [0, 8, 6, 2, 0, 0] + + sage: P = posets.AntichainPoset(4) + sage: P.spectrum(3) + [6, 6, 6, 6] + + TESTS:: + + sage: P = posets.ChainPoset(5) + sage: P.spectrum(6) + Traceback (most recent call last): + ... + ValueError: input element is not in poset + """ + if a not in self: + raise ValueError("input element is not in poset") + + a_spec = [0] * len(self) + for L in self.linear_extensions(): + idx = L.index(a) + a_spec[idx] += 1 + + return a_spec + + def atkinson(self, a): + r""" + Return the `a`-spectrum of a poset whose Hasse diagram is + cycle-free as an undirected graph. + + Given an element `a` in a poset `P`, the `a`-spectrum is the list of + integers whose `i`-th term contains the number of linear extensions of + `P` with element `a` located in the i-th position. + + INPUT: + + - ``self`` -- a poset whose Hasse diagram is a forest + + - ``a`` -- an element of the poset + + OUTPUT: + + The `a`-spectrum of this poset, returned as a list. + + EXAMPLES:: + + sage: P = Poset({0: [2], 1: [2], 2: [3, 4], 3: [], 4: []}) + sage: P.atkinson(0) + [2, 2, 0, 0, 0] + + sage: P = Poset({0: [1], 1: [2, 3], 2: [], 3: [], 4: [5, 6], 5: [], 6: []}) + sage: P.atkinson(5) + [0, 10, 18, 24, 28, 30, 30] + + sage: P = posets.AntichainPoset(10) + sage: P.atkinson(0) + [362880, 362880, 362880, 362880, 362880, 362880, 362880, 362880, 362880, 362880] + + TESTS:: + + sage: P = posets.ChainPoset(5) + sage: P.atkinson(6) + Traceback (most recent call last): + ... + ValueError: input element is not in poset + + sage: P = posets.BooleanLattice(2) + sage: P.atkinson(1) + Traceback (most recent call last): + ... + ValueError: this poset is not a forest + + .. NOTE:: + + This function is the implementation of the algorithm from [At1990]_. + """ + if a not in self: + raise ValueError("input element is not in poset") + + if not self._hasse_diagram.to_undirected().is_forest(): + raise ValueError("this poset is not a forest") + + n = self.cardinality() + + # Compute the component of this poset containing `a` and its complement + components = self.connected_components() + remainder_poset = Poset() + + + for X in components: + if a in X: + main = X + else: + remainder_poset = remainder_poset.disjoint_union(X) + + hasse_a = main._element_to_vertex(a) + a_spec = main._hasse_diagram._spectrum_of_tree(hasse_a) + + if not remainder_poset.cardinality(): + return a_spec + + b = remainder_poset.an_element() + + b_spec = remainder_poset.atkinson(b) + n_lin_exts = sum(b_spec) + + new_a_spec = [] + k = main.cardinality() + + # Compute number of shuffles of linear extensions of the two posets + for r in range(1, n+1): + new_a_spec.append(0) + for i in range(max(1, r-n+k), min(r,k) + 1): + k_val = binomial(r-1, i-1) * binomial(n - r, k - i) + new_a_spec[-1] += k_val * a_spec[i-1] * n_lin_exts + return new_a_spec def is_linear_extension(self, l): """ - Returns whether ``l`` is a linear extension of ``self`` + Return whether ``l`` is a linear extension of ``self``. INPUT: @@ -2086,6 +2236,144 @@ def relations(self): """ return list(self.relations_iterator()) + def diamonds(self): + r""" + Return the list of diamonds of ``self``. + + A diamond is the following subgraph of the Hasse diagram:: + + z + / \ + x y + \ / + w + + Thus each edge represents a cover relation in the Hasse diagram. + We represent his as the tuple `(w, x, y, z)`. + + OUTPUT: + + A tuple with + + - a list of all diamonds in the Hasse Diagram, + - a boolean checking that every `w,x,y` that form a ``V``, there is a + unique element `z`, which completes the diamond. + + EXAMPLES:: + + sage: P = Poset({0: [1,2], 1: [3], 2: [3], 3: []}) + sage: P.diamonds() + ([(0, 1, 2, 3)], True) + + sage: P = posets.YoungDiagramPoset(Partition([3, 2, 2])) + sage: P.diamonds() + ([((0, 0), (0, 1), (1, 0), (1, 1)), ((1, 0), (1, 1), (2, 0), (2, 1))], False) + """ + diamonds, all_diamonds_completed = self._hasse_diagram.diamonds() + return ([tuple(map(self._vertex_to_element, d)) for d in diamonds], all_diamonds_completed) + + def common_upper_covers(self, elmts): + r""" + Return all of the common upper covers of the elements ``elmts``. + + EXAMPLES:: + + sage: P = Poset({0: [1,2], 1: [3], 2: [3], 3: []}) + sage: P.common_upper_covers([1, 2]) + [3] + """ + vertices = list(map(self._element_to_vertex, elmts)) + return list(map(self._vertex_to_element, self._hasse_diagram.common_upper_covers(vertices))) + + def is_d_complete(self): + r""" + Return ``True`` if a poset is d-complete and ``False`` otherwise. + + .. SEEALSO:: + + - :mod:`~sage.combinat.posets.d_complete` + + EXAMPLES:: + + sage: from sage.combinat.posets.posets import FinitePoset + sage: A = Poset({0: [1,2]}) + sage: A.is_d_complete() + False + + sage: from sage.combinat.posets.poset_examples import Posets + sage: B = Posets.DoubleTailedDiamond(3) + sage: B.is_d_complete() + True + + sage: C = Poset({0: [2], 1: [2], 2: [3, 4], 3: [5], 4: [5], 5: [6]}) + sage: C.is_d_complete() + False + + sage: D = Poset({0: [1, 2], 1: [4], 2: [4], 3: [4]}) + sage: D.is_d_complete() + False + + sage: P = Posets.YoungDiagramPoset(Partition([3, 2, 2]), dual=True) + sage: P.is_d_complete() + True + """ + min_diamond = {} # Maps max of double-tailed diamond to min of double-tailed diamond + max_diamond = {} # Maps min of double-tailed diamond to max of double-tailed diamond + + H = self._hasse_diagram + + diamonds, all_diamonds_completed = H.diamonds() # Tuples of four elements that are diamonds + + if not all_diamonds_completed: + return False + + diamond_index = {} # Map max elmt of double tailed diamond to index of diamond + + # Find all the double-tailed diamonds and map the mins and maxes + for index, d in enumerate(diamonds): + min_diamond[d[3]] = d[0] + max_diamond[d[0]] = d[3] + diamond_index[d[3]] = index + + min_elmt = d[0] + max_elmt = d[3] + + if len(H.neighbors_in(max_elmt)) != 2: + # Top of a diamond cannot cover anything but the two side elements + return False + + while True: + potential_min = H.neighbors_in(min_elmt) + potential_max = H.neighbors_out(max_elmt) + max_dk_minus = max_elmt + + # Check if any of these make a longer double tailed diamond + found_diamond = False + for mn in potential_min: + if len(H.all_paths(mn, max_dk_minus)) > 2: + continue + for mx in potential_max: + if len(H.all_paths(mn, mx)) == 2: + if len(H.neighbors_in(mx)) != 1: + # Max element covers something outside of double tailed diamond + return False + # Success + if mx in min_diamond or mn in max_diamond: + # Two double tail diamonds that differ by minimal element + # or the dual + return False + min_elmt = mn + max_elmt = mx + + min_diamond[mx] = mn + max_diamond[mn] = mx + diamond_index[mx] = index + found_diamond = True + if not found_diamond: + break + + return True + def intervals_poset(self): r""" Return the natural partial order on the set of intervals of the poset. @@ -2982,7 +3270,7 @@ def is_EL_labelling(self, f, return_raising_chains=False): if max_chains[0] != sorted(max_chains[0]) or any( max_chains[i] == sorted(max_chains[i]) for i in range(1,len(max_chains)) ): return False elif return_raising_chains: - raising_chains[(a,b)] = max_chains[0] + raising_chains[(a, b)] = max_chains[0] if return_raising_chains: return raising_chains else: @@ -3148,7 +3436,7 @@ def init_LP(k,cycles,inc_P): # We create the digraphs of all color classes linear_extensions = [hasse_diagram.copy() for i in range(k)] - for ((u,v),i),x in iteritems(p.get_values(b)): + for ((u,v),i),x in p.get_values(b).items(): if x == 1: linear_extensions[i].add_edge(u,v) @@ -4132,7 +4420,7 @@ def isomorphic_subposets(self, other): return [self.subposet([self._list[i] for i in x]) for x in sorted(set(frozenset(y) for y in L))] # Caveat: list is overridden by the method list above!!! - def antichains(self, element_constructor=builtins.list): + def antichains(self, element_constructor=type([])): """ Return the antichains of the poset. @@ -4181,7 +4469,7 @@ def antichains(self, element_constructor=builtins.list): Internally, this uses :class:`sage.combinat.subsets_pairwise.PairwiseCompatibleSubsets` - and :class:`SearchForest`. At this point, iterating + and :class:`RecursivelyEnumeratedSet_forest`. At this point, iterating through this set is about twice slower than using :meth:`antichains_iterator` (tested on ``posets.AntichainPoset(15)``). The algorithm is the same @@ -4224,7 +4512,7 @@ def width(self, certificate=False): Return the width of the poset (the size of its longest antichain). It is computed through a matching in a bipartite graph; see - :wikipedia:`Dilworth's_theorem` for more information. The width is + :wikipedia:`Dilworth%27s_theorem` for more information. The width is also called Dilworth number. INPUT: @@ -4276,7 +4564,7 @@ def dilworth_decomposition(self): partitioned into `\alpha` chains, where `\alpha` is the cardinality of its largest antichain. This method returns such a partition. - See :wikipedia:`Dilworth's_theorem`. + See :wikipedia:`Dilworth%27s_theorem`. ALGORITHM: @@ -4338,7 +4626,7 @@ def dilworth_decomposition(self): chains.append(chain) return chains - def chains(self, element_constructor=builtins.list, exclude=None): + def chains(self, element_constructor=type([]), exclude=None): """ Return the chains of the poset. @@ -5682,7 +5970,7 @@ def canonical_label(self, algorithm=None): """ canonical_label = self._hasse_diagram.canonical_label(certificate=True, algorithm=algorithm)[1] - canonical_label = {self._elements[v]:i for v,i in iteritems(canonical_label)} + canonical_label = {self._elements[v]:i for v,i in canonical_label.items()} return self.relabel(canonical_label) def with_linear_extension(self, linear_extension): @@ -5745,9 +6033,9 @@ def with_linear_extension(self, linear_extension): category=self.category(), facade=self._is_facade) - def graphviz_string(self,graph_string="graph",edge_string="--"): + def graphviz_string(self, graph_string="graph", edge_string="--"): r""" - Returns a representation in the DOT language, ready to render in + Return a representation in the DOT language, ready to render in graphviz. See http://www.graphviz.org/doc/info/lang.html for more information @@ -6044,7 +6332,7 @@ def random_linear_extension(self): new = mins[new_index] result.append(new) mins = mins[:new_index]+mins[new_index+1:] - for u in H.neighbors_out(new): + for u in H.neighbor_out_iterator(new): indegs[u] -= 1 if indegs[u] == 0: mins.append(u) @@ -6079,7 +6367,7 @@ def order_filter(self, elements): sage: C.order_filter([]) [] """ - vertices = sorted(map(self._element_to_vertex,elements)) + vertices = sorted(map(self._element_to_vertex, elements)) of = self._hasse_diagram.order_filter(vertices) return [self._vertex_to_element(_) for _ in of] @@ -6115,6 +6403,22 @@ def order_ideal(self, elements): oi = self._hasse_diagram.order_ideal(vertices) return [self._vertex_to_element(_) for _ in oi] + def order_ideal_cardinality(self, elements): + r""" + Return the cardinality of the order ideal generated by ``elements``. + + The elements `I` is an order ideal if, for any `x \in I` and `y` + such that `y \le x`, then `y \in I`. + + EXAMPLES:: + + sage: P = posets.BooleanLattice(4) + sage: P.order_ideal_cardinality([7,10]) + 10 + """ + vertices = list(map(self._element_to_vertex, elements)) + return self._hasse_diagram.order_ideal_cardinality(vertices) + def order_ideal_plot(self, elements): r""" Return a plot of the order ideal generated by the elements of an @@ -6387,8 +6691,6 @@ def maximal_chains(self, partial=None): - ``partial`` -- list (optional); if present, find all maximal chains starting with the elements in partial - Returns list of the maximal chains of this poset. - This is used in constructing the order complex for the poset. EXAMPLES:: @@ -7308,7 +7610,7 @@ def is_slender(self, certificate=False): for y in self.upper_covers(x): for c in self.upper_covers(y): d[c] = d.get(c, 0) + 1 - for c, y in iteritems(d): + for c, y in d.items(): if y >= 3: if certificate: return (False, (x, c)) @@ -8194,13 +8496,13 @@ def __contains__(self, P): def __iter__(self): """ - Returns an iterator of representatives of the isomorphism classes + Return an iterator of representatives of the isomorphism classes of finite posets of a given size. - .. note:: + .. NOTE:: - This uses the DiGraph iterator as a backend to construct - transitively-reduced, acyclic digraphs. + This uses the DiGraph iterator as a backend to construct + transitively-reduced, acyclic digraphs. EXAMPLES:: @@ -8212,8 +8514,8 @@ def __iter__(self): for dig in DiGraphGenerators()(self._n, is_poset): # We need to relabel the digraph since range(self._n) must be a linear # extension. Too bad we need to compute this again. TODO: Fix this. - label_dict = dict(zip(dig.topological_sort(),range(dig.order()))) - yield FinitePoset(dig.relabel(label_dict,inplace=False)) + label_dict = dict(zip(dig.topological_sort(), range(dig.order()))) + yield FinitePoset(dig.relabel(label_dict, inplace=False)) def cardinality(self, from_iterator=False): r""" diff --git a/src/sage/combinat/q_analogues.py b/src/sage/combinat/q_analogues.py index 2b98f1ce7a9..8d9016cc65e 100644 --- a/src/sage/combinat/q_analogues.py +++ b/src/sage/combinat/q_analogues.py @@ -84,10 +84,18 @@ def q_int(n, q=None): def q_factorial(n, q=None): """ - Returns the `q`-analogue of the factorial `n!`. + Return the `q`-analogue of the factorial `n!`. - If `q` is unspecified, then it defaults to using the generator `q` for - a univariate polynomial ring over the integers. + This is the product + + .. MATH:: + + [1]_q [2]_q \cdots [n]_q + = 1 \cdot (1+q) \cdot (1+q+q^2) \cdots (1+q+q^2+\cdots+q^{n-1}) . + + If `q` is unspecified, then this function defaults to + using the generator `q` for a univariate polynomial + ring over the integers. EXAMPLES:: @@ -104,12 +112,19 @@ def q_factorial(n, q=None): sage: q_factorial(-2) Traceback (most recent call last): ... - ValueError: Argument (-2) must be a nonnegative integer. + ValueError: argument (-2) must be a nonnegative integer + + TESTS:: + + sage: q_factorial(0).parent() + Univariate Polynomial Ring in q over Integer Ring """ - if n in ZZ and n >= 0: - return prod(q_int(i, q) for i in range(1, n + 1)) - else: - raise ValueError("Argument (%s) must be a nonnegative integer." % n) + if n in ZZ: + if n == 0: + return q_int(1, q) + elif n >= 1: + return prod(q_int(i, q) for i in range(1, n + 1)) + raise ValueError("argument (%s) must be a nonnegative integer" % n) def q_binomial(n, k, q=None, algorithm='auto'): @@ -356,8 +371,8 @@ def q_binomial(n, k, q=None, algorithm='auto'): return q_binomial(n, k)(q) if algorithm == 'cyclotomic': from sage.rings.polynomial.cyclotomic import cyclotomic_value - return prod(cyclotomic_value(d,q) - for d in range(2,n+1) + return prod(cyclotomic_value(d, q) + for d in range(2, n + 1) if (n//d) != (k//d) + ((n-k)//d)) else: raise ValueError("unknown algorithm {!r}".format(algorithm)) @@ -433,6 +448,7 @@ def q_multinomial(seq, q=None, binomial_algorithm='auto'): binomials.append(q_binomial(partial_sum, elem, q=q, algorithm=binomial_algorithm)) return prod(binomials) + gaussian_multinomial = q_multinomial @@ -461,23 +477,27 @@ def q_catalan_number(n, q=None): sage: q_catalan_number(-2) Traceback (most recent call last): ... - ValueError: Argument (-2) must be a nonnegative integer. + ValueError: argument (-2) must be a nonnegative integer TESTS:: sage: q_catalan_number(3).parent() Univariate Polynomial Ring in q over Integer Ring + sage: q_catalan_number(0).parent() + Univariate Polynomial Ring in q over Integer Ring """ - if n in ZZ and n >= 0: - return (prod(q_int(j, q) for j in range(n + 2, 2 * n + 1)) // - prod(q_int(j, q) for j in range(2, n + 1))) - else: - raise ValueError("Argument (%s) must be a nonnegative integer." % n) + if n in ZZ: + if n in {0, 1}: + return q_int(1, q) + elif n >= 2: + return (prod(q_int(j, q) for j in range(n + 2, 2 * n + 1)) // + prod(q_int(j, q) for j in range(2, n + 1))) + raise ValueError("argument (%s) must be a nonnegative integer" % n) def qt_catalan_number(n): """ - Returns the `q,t`-Catalan number of index `n`. + Return the `q,t`-Catalan number of index `n`. EXAMPLES:: @@ -500,7 +520,7 @@ def qt_catalan_number(n): ValueError: Argument (-2) must be a nonnegative integer. """ if n in ZZ and n >= 0: - ZZqt = ZZ['q','t'] + ZZqt = ZZ['q', 't'] d = {} for dw in DyckWords(n): tup = (dw.area(), dw.bounce()) @@ -577,7 +597,7 @@ def q_pochhammer(n, a, q=None): R = parent(q) one = R(1) if n < 0: - return R.prod(one / (one - a/q**-k) for k in range(1,-n+1)) + return R.prod(one / (one - a/q**-k) for k in range(1, -n+1)) return R.prod((one - a*q**k) for k in range(n)) @@ -883,6 +903,7 @@ def q_stirling_number1(n, k, q=None): return (q_stirling_number1(n - 1, k - 1, q=q) + q_int(n - 1, q=q) * q_stirling_number1(n - 1, k, q=q)) + @cached_function def q_stirling_number2(n, k, q=None): r""" @@ -932,7 +953,6 @@ def q_stirling_number2(n, k, q=None): REFERENCES: - [Mil1978]_ - """ if q is None: q = ZZ['q'].gen() diff --git a/src/sage/combinat/quickref.py b/src/sage/combinat/quickref.py index f37950b34c6..a62944138ad 100644 --- a/src/sage/combinat/quickref.py +++ b/src/sage/combinat/quickref.py @@ -7,7 +7,10 @@ sage: s = oeis([1,3,19,211]); s # optional - internet 0: A000275: Coefficients of a Bessel function (reciprocal of J_0(z)); also pairs of permutations with rise/rise forbidden. sage: s[0].programs() # optional - internet - 0: (PARI) {a(n) = if( n<0, 0, n!^2 * 4^n * polcoeff( 1 / besselj(0, x + x * O(x^(2*n))), 2*n))}; /* _Michael Somos_, May 17 2004 */ + [('maple', ...), + ('mathematica', ...), + ('pari', + 0: {a(n) = if( n<0, 0, n!^2 * 4^n * polcoeff( 1 / besselj(0, x + x * O(x^(2*n))), 2*n))}; /* _Michael Somos_, May 17 2004 */)] Combinatorial objects:: diff --git a/src/sage/combinat/ranker.py b/src/sage/combinat/ranker.py index e3db5328a0e..4017b340522 100644 --- a/src/sage/combinat/ranker.py +++ b/src/sage/combinat/ranker.py @@ -17,7 +17,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from collections import Iterable, Sequence from sage.misc.cachefunc import cached_function diff --git a/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py b/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py index 0f9650c79c6..50533fbc842 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py +++ b/src/sage/combinat/rigged_configurations/bij_type_A2_dual.py @@ -1,5 +1,5 @@ r""" -Bijection classes for type `A_{2n}^{(2)\dagger}`. +Bijection classes for type `A_{2n}^{(2)\dagger}` Part of the (internal) classes which runs the bijection between rigged configurations and KR tableaux of type `A_{2n}^{(2)\dagger}`. diff --git a/src/sage/combinat/rigged_configurations/bij_type_A2_even.py b/src/sage/combinat/rigged_configurations/bij_type_A2_even.py index 55e73b5d3d2..be1cd2c4cd3 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_A2_even.py +++ b/src/sage/combinat/rigged_configurations/bij_type_A2_even.py @@ -1,5 +1,5 @@ r""" -Bijection classes for type `A_{2n}^{(2)}`. +Bijection classes for type `A_{2n}^{(2)}` Part of the (internal) classes which runs the bijection between rigged configurations and KR tableaux of type `A_{2n}^{(2)}`. diff --git a/src/sage/combinat/rigged_configurations/bij_type_B.py b/src/sage/combinat/rigged_configurations/bij_type_B.py index 20bf132e306..8556699d4e4 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_B.py +++ b/src/sage/combinat/rigged_configurations/bij_type_B.py @@ -1,5 +1,5 @@ r""" -Bijection classes for type `B_n^{(1)}`. +Bijection classes for type `B_n^{(1)}` Part of the (internal) classes which runs the bijection between rigged configurations and KR tableaux of type `B_n^{(1)}`. diff --git a/src/sage/combinat/rigged_configurations/bij_type_C.py b/src/sage/combinat/rigged_configurations/bij_type_C.py index d625f4136f2..c571978c353 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_C.py +++ b/src/sage/combinat/rigged_configurations/bij_type_C.py @@ -1,5 +1,5 @@ r""" -Bijection classes for type `C_n^{(1)}`. +Bijection classes for type `C_n^{(1)}` Part of the (internal) classes which runs the bijection between rigged configurations and KR tableaux of type `C_n^{(1)}`. diff --git a/src/sage/combinat/rigged_configurations/bij_type_D_tri.py b/src/sage/combinat/rigged_configurations/bij_type_D_tri.py index b5336e64246..dda29c33373 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_D_tri.py +++ b/src/sage/combinat/rigged_configurations/bij_type_D_tri.py @@ -1,5 +1,5 @@ r""" -Bijection classes for type `D_4^{(3)}`. +Bijection classes for type `D_4^{(3)}` Part of the (internal) classes which runs the bijection between rigged configurations and KR tableaux of type `D_4^{(3)}`. diff --git a/src/sage/combinat/rigged_configurations/bij_type_D_twisted.py b/src/sage/combinat/rigged_configurations/bij_type_D_twisted.py index a1fd32883d0..96e32e0664e 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_D_twisted.py +++ b/src/sage/combinat/rigged_configurations/bij_type_D_twisted.py @@ -1,5 +1,5 @@ r""" -Bijection classes for type `D_{n+1}^{(2)}`. +Bijection classes for type `D_{n+1}^{(2)}` Part of the (internal) classes which runs the bijection between rigged configurations and KR tableaux of type `D_{n+1}^{(2)}`. diff --git a/src/sage/combinat/rigged_configurations/kleber_tree.py b/src/sage/combinat/rigged_configurations/kleber_tree.py index 2bae940a517..0011a1d36c3 100644 --- a/src/sage/combinat/rigged_configurations/kleber_tree.py +++ b/src/sage/combinat/rigged_configurations/kleber_tree.py @@ -65,7 +65,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from six.moves import range import itertools from sage.misc.lazy_attribute import lazy_attribute diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py index f1009a0fc6f..99af798bfb3 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux_element.py @@ -25,8 +25,6 @@ #***************************************************************************** from __future__ import print_function -from six.moves import range - from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement class TensorProductOfKirillovReshetikhinTableauxElement(TensorProductOfRegularCrystalsElement): @@ -219,7 +217,7 @@ def classical_weight(self): EXAMPLES:: sage: KRT = crystals.TensorProductOfKirillovReshetikhinTableaux(['D',4,1], [[2,2]]) - sage: elt = KRT(pathlist=[[3,2,-1,1]]); elt + sage: elt = KRT(pathlist=[[3,2,-1,1]]); elt [[2, 1], [3, -1]] sage: elt.classical_weight() (0, 1, 1, 0) diff --git a/src/sage/combinat/root_system/__init__.py b/src/sage/combinat/root_system/__init__.py index fa932ab28ef..b04c503fad1 100644 --- a/src/sage/combinat/root_system/__init__.py +++ b/src/sage/combinat/root_system/__init__.py @@ -77,6 +77,7 @@ --------------------- - :ref:`sage.combinat.root_system.weyl_characters` +- :ref:`sage.combinat.root_system.fusion_ring` - :ref:`sage.combinat.root_system.integrable_representations` - :ref:`sage.combinat.root_system.branching_rules` - :ref:`sage.combinat.root_system.hecke_algebra_representation` diff --git a/src/sage/combinat/root_system/all.py b/src/sage/combinat/root_system/all.py index 8ed7e596a17..ccb40064690 100644 --- a/src/sage/combinat/root_system/all.py +++ b/src/sage/combinat/root_system/all.py @@ -19,8 +19,8 @@ 'ExtendedAffineWeylGroup') lazy_import('sage.combinat.root_system.coxeter_group', 'CoxeterGroup') lazy_import('sage.combinat.root_system.weyl_characters', ['WeylCharacterRing', - 'WeightRing', - 'FusionRing']) + 'WeightRing']) +lazy_import('sage.combinat.root_system.fusion_ring', ['FusionRing']) from .branching_rules import BranchingRule, branching_rule_from_plethysm, branching_rule lazy_import('sage.combinat.root_system.non_symmetric_macdonald_polynomials', 'NonSymmetricMacdonaldPolynomials') diff --git a/src/sage/combinat/root_system/ambient_space.py b/src/sage/combinat/root_system/ambient_space.py index 1ccd5c9c13c..3950cef7edc 100644 --- a/src/sage/combinat/root_system/ambient_space.py +++ b/src/sage/combinat/root_system/ambient_space.py @@ -15,8 +15,6 @@ from sage.rings.all import ZZ, QQ from sage.categories.homset import End -import six - class AmbientSpace(CombinatorialFreeModule): r""" @@ -384,7 +382,7 @@ def inner_product(self, lambdacheck): lambdacheck_mc = lambdacheck._monomial_coefficients result = self.parent().base_ring().zero() - for t,c in six.iteritems(lambdacheck_mc): + for t,c in lambdacheck_mc.items(): if t not in self_mc: continue result += c*self_mc[t] diff --git a/src/sage/combinat/root_system/branching_rules.py b/src/sage/combinat/root_system/branching_rules.py index 1bc814bff6e..4f9261f0660 100644 --- a/src/sage/combinat/root_system/branching_rules.py +++ b/src/sage/combinat/root_system/branching_rules.py @@ -1,12 +1,12 @@ """ Branching Rules """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2014 Daniel Bump # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from __future__ import print_function import sage.combinat.root_system.weyl_characters @@ -1750,7 +1750,7 @@ def rule(x): elif rule == "tensor" or rule == "tensor-debug": if not Stype.is_compound(): raise ValueError("Tensor product requires more than one factor") - if len(stypes) is not 2: + if len(stypes) != 2: raise ValueError("Not implemented") if Rtype[0] == 'A': nr = Rtype[1]+1 diff --git a/src/sage/combinat/root_system/cartan_matrix.py b/src/sage/combinat/root_system/cartan_matrix.py index ad04d337884..0a02a262f7c 100644 --- a/src/sage/combinat/root_system/cartan_matrix.py +++ b/src/sage/combinat/root_system/cartan_matrix.py @@ -25,8 +25,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range -from six import add_metaclass from sage.misc.cachefunc import cached_method from sage.matrix.constructor import matrix @@ -43,8 +41,8 @@ from sage.graphs.digraph import DiGraph -@add_metaclass(InheritComparisonClasscallMetaclass) -class CartanMatrix(Matrix_integer_sparse, CartanType_abstract): +class CartanMatrix(Matrix_integer_sparse, CartanType_abstract, + metaclass=InheritComparisonClasscallMetaclass): r""" A (generalized) Cartan matrix. diff --git a/src/sage/combinat/root_system/cartan_type.py b/src/sage/combinat/root_system/cartan_type.py index 202e1abe113..934c081781b 100644 --- a/src/sage/combinat/root_system/cartan_type.py +++ b/src/sage/combinat/root_system/cartan_type.py @@ -477,10 +477,6 @@ #***************************************************************************** from __future__ import print_function, absolute_import, division -from six.moves import range -from six.moves.builtins import sorted -from six import class_types, string_types - from sage.misc.cachefunc import cached_method from sage.misc.abstract_method import abstract_method from sage.misc.lazy_import import LazyImport @@ -554,7 +550,7 @@ def __call__(self, *args): sage: CT.cartan_matrix() [ 2 -1] [-1 2] - sage: CT = CartanType(['A2']) + sage: CT = CartanType(['A2']) sage: CT.is_irreducible() True sage: CartanType('A2') @@ -600,7 +596,7 @@ def __call__(self, *args): return t from sage.rings.semirings.non_negative_integer_semiring import NN - if isinstance(t, string_types): + if isinstance(t, str): if "x" in t: from . import type_reducible return type_reducible.CartanType([CartanType(u) for u in t.split("x")]) @@ -617,7 +613,7 @@ def __call__(self, *args): return CartanType([t[0], eval(t[1:])]) t = list(t) - if isinstance(t[0], string_types) and t[1] in [Infinity, ZZ, NN]: + if isinstance(t[0], str) and t[1] in [Infinity, ZZ, NN]: letter, n = t[0], t[1] if letter == 'A': from . import type_A_infinity @@ -626,7 +622,7 @@ def __call__(self, *args): else: return type_A_infinity.CartanType(ZZ) - if isinstance(t[0], string_types) and t[1] in ZZ and t[1] >= 0: + if isinstance(t[0], str) and t[1] in ZZ and t[1] >= 0: letter, n = t[0], t[1] if len(t) == 2: if letter == "A": @@ -726,7 +722,7 @@ def __call__(self, *args): return CartanType(["F", 4, 1]).dual() raise ValueError("%s is not a valid Cartan type" % t) - if isinstance(t[0], string_types) and isinstance(t[1], (list, tuple)): + if isinstance(t[0], str) and isinstance(t[1], (list, tuple)): letter, n = t[0], t[1] if len(t) == 2 and len(n) == 2: from . import type_super_A @@ -952,10 +948,10 @@ class options(GlobalOptions): alias=dict(BC="Stembridge", tilde="Stembridge", twisted="Kac")) dual_str = dict(default="*", description='The string used for dual Cartan types when printing', - checker=lambda char: isinstance(char, string_types)) + checker=lambda char: isinstance(char, str)) dual_latex = dict(default="\\vee", description='The latex used for dual CartanTypes when latexing', - checker=lambda char: isinstance(char, string_types)) + checker=lambda char: isinstance(char, str)) mark_special_node = dict(default="none", description="Make the special nodes", values=dict(none="no markup", latex="only in latex", @@ -963,10 +959,10 @@ class options(GlobalOptions): case_sensitive=False) special_node_str = dict(default="@", description="The string used to indicate which node is special when printing", - checker=lambda char: isinstance(char, string_types)) + checker=lambda char: isinstance(char, str)) marked_node_str = dict(default="X", description="The string used to indicate a marked node when printing", - checker=lambda char: isinstance(char, string_types)) + checker=lambda char: isinstance(char, str)) latex_relabel = dict(default=True, description="Indicate in the latex output if a Cartan type has been relabelled", checker=lambda x: isinstance(x, bool)) @@ -1036,7 +1032,7 @@ def _add_abstract_superclass(self, classes): .. TODO:: Generalize to :class:`SageObject`? """ from sage.structure.dynamic_class import dynamic_class - assert isinstance(classes, (tuple, class_types)) + assert isinstance(classes, (tuple, type)) if not isinstance(classes, tuple): classes = (classes,) bases = (self.__class__,) + classes @@ -3066,10 +3062,10 @@ def __setstate__(self, dict): sage: pg_CartanType_simple_finite = unpickle_global('sage.combinat.root_system.cartan_type', 'CartanType_simple_finite') sage: si1 = unpickle_newobj(pg_CartanType_simple_finite, ()) - sage: pg_unpickleModule = unpickle_global('twisted.persisted.styles', 'unpickleModule') + sage: from sage.misc.fpickle import unpickleModule sage: pg_make_integer = unpickle_global('sage.rings.integer', 'make_integer') sage: si2 = pg_make_integer('4') - sage: unpickle_build(si1, {'tools':pg_unpickleModule('sage.combinat.root_system.type_A'), 't':['A', si2], 'letter':'A', 'n':si2}) + sage: unpickle_build(si1, {'tools':unpickleModule('sage.combinat.root_system.type_A'), 't':['A', si2], 'letter':'A', 'n':si2}) sage: si1 ['A', 4] diff --git a/src/sage/combinat/root_system/coxeter_matrix.py b/src/sage/combinat/root_system/coxeter_matrix.py index cfc4daa41c0..2595166bf57 100644 --- a/src/sage/combinat/root_system/coxeter_matrix.py +++ b/src/sage/combinat/root_system/coxeter_matrix.py @@ -17,7 +17,6 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** -from six import add_metaclass from sage.misc.cachefunc import cached_method from sage.matrix.constructor import matrix @@ -31,8 +30,7 @@ from sage.combinat.root_system.coxeter_type import CoxeterType -@add_metaclass(ClasscallMetaclass) -class CoxeterMatrix(CoxeterType): +class CoxeterMatrix(CoxeterType, metaclass=ClasscallMetaclass): r""" A Coxeter matrix. diff --git a/src/sage/combinat/root_system/coxeter_type.py b/src/sage/combinat/root_system/coxeter_type.py index 00d235fe69e..7f6b48482f9 100644 --- a/src/sage/combinat/root_system/coxeter_type.py +++ b/src/sage/combinat/root_system/coxeter_type.py @@ -16,7 +16,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six import add_metaclass from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method @@ -30,8 +29,7 @@ from sage.rings.number_field.number_field import is_QuadraticField -@add_metaclass(ClasscallMetaclass) -class CoxeterType(SageObject): +class CoxeterType(SageObject, metaclass=ClasscallMetaclass): """ Abstract class for Coxeter types. """ diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py new file mode 100644 index 00000000000..4f457e0d8e7 --- /dev/null +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -0,0 +1,933 @@ +""" +Fusion Rings +""" +# **************************************************************************** +# Copyright (C) 2019 Daniel Bump +# Nicolas Thiery +# Guillermo Aboumrad +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.combinat.root_system.weyl_characters import WeylCharacterRing +from sage.combinat.q_analogues import q_int +from sage.matrix.special import diagonal_matrix +from sage.matrix.constructor import matrix +from sage.misc.misc import inject_variable +from sage.rings.integer_ring import ZZ +from sage.rings.number_field.number_field import CyclotomicField +from sage.misc.cachefunc import cached_method + +class FusionRing(WeylCharacterRing): + r""" + Return the Fusion Ring (Verlinde Algebra) of level ``k``. + + INPUT: + + - ``ct`` -- the Cartan type of a simple (finite-dimensional) Lie algebra + - ``k`` -- a nonnegative integer + - ``conjugate`` -- (default ``False``) set ``True`` to obtain + the complex conjugate ring + - ``cyclotomic_order`` -- (default computed depending on ``ct`` and ``k``) + + The cyclotomic order is an integer `N` such that all computations + will return elements of the cyclotomic field of `N`-th roots of unity. + Normally you will never need to change this but consider changing it + if :meth:`root_of_unity` ever returns ``None``. + + This algebra has a basis (sometimes called *primary fields* but here + called *simple objects*) indexed by the weights of level `\leq k`. + These arise as the fusion algebras of Wess-Zumino-Witten (WZW) conformal + field theories, or as Grothendieck groups of tilting modules for quantum + groups at roots of unity. The :class:`FusionRing` class is implemented as + a variant of the :class:`WeylCharacterRing`. + + REFERENCES: + + - [BaKi2001]_ Chapter 3 + - [DFMS1996]_ Chapter 16 + - [EGNO2015]_ Chapter 8 + - [Feingold2004]_ + - [Fuchs1994]_ + - [Row2006]_ + - [Walton1990]_ + + EXAMPLES:: + + sage: A22 = FusionRing("A2",2) + sage: [f1, f2] = A22.fundamental_weights() + sage: M = [A22(x) for x in [0*f1, 2*f1, 2*f2, f1+f2, f2, f1]] + sage: [M[3] * x for x in M] + [A22(1,1), + A22(0,1), + A22(1,0), + A22(0,0) + A22(1,1), + A22(0,1) + A22(2,0), + A22(1,0) + A22(0,2)] + + You may assign your own labels to the basis elements. In the next + example, we create the `SO(5)` fusion ring of level `2`, check the + weights of the basis elements, then assign new labels to them while + injecting them into the global namespace:: + + sage: B22 = FusionRing("B2", 2) + sage: b = [B22(x) for x in B22.get_order()]; b + [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] + sage: [x.weight() for x in b] + [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] + sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2'], inject_variables=True) + sage: b = [B22(x) for x in B22.get_order()]; b + [I0, Y1, X, Z, Xp, Y2] + sage: [(x, x.weight()) for x in b] + [(I0, (0, 0)), + (Y1, (1, 0)), + (X, (1/2, 1/2)), + (Z, (2, 0)), + (Xp, (3/2, 1/2)), + (Y2, (1, 1))] + sage: X * Y1 + X + Xp + sage: Z * Z + I0 + + A fixed order of the basis keys is available with :meth:`get_order`. + This is the order used by methods such as :meth:`s_matrix`. You may + use :meth:`CombinatorialFreeModule.set_order` to reorder the basis:: + + sage: B22.set_order([x.weight() for x in [I0,Y1,Y2,X,Xp,Z]]) + sage: [B22(x) for x in B22.get_order()] + [I0, Y1, Y2, X, Xp, Z] + + To reset the labels, you may run :meth:`fusion_labels` with no parameter:: + + sage: B22.fusion_labels() + sage: [B22(x) for x in B22.get_order()] + [B22(0,0), B22(1,0), B22(0,2), B22(0,1), B22(1,1), B22(2,0)] + + To reset the order to the default, simply set it to the list of basis + element keys:: + + sage: B22.set_order(B22.basis().keys().list()) + sage: [B22(x) for x in B22.get_order()] + [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] + + The fusion ring has a number of methods that reflect its role + as the Grothendieck ring of a *modular tensor category*. These + include a twist method :meth:`Element.twist` for its elements related + to the ribbon structure, and the S-matrix :meth:`s_ij`. + + There are two natural normalizations of the S-matrix. Both + are explained in Chapter 3 of [BaKi2001]_. The one that is computed + by the method :meth:`s_matrix`, or whose individual entries + are computed by :meth:`s_ij` is denoted `\tilde{s}` in + [BaKi2001]_. It is not unitary. + + The unitary S-matrix is `s=D^{-1/2}\tilde{s}` where + + .. MATH:: + + D = \sum_V d_i(V)^2. + + The sum is over all simple objects `V` with + `d_i(V)` the *quantum dimension*. We will call quantity `D` + the *global quantum dimension* and `\sqrt{D}` the + *total quantum order*. They are computed by :meth:`global_q_dimension` + and :meth:`total_q_order`. The unitary S-matrix `s` may be obtained + using :meth:`s_matrix` with the option ``unitary=True``. + + Let us check the Verlinde formula, which is [DFMS1996]_ (16.3). This + famous identity states that + + .. MATH:: + + N^k_{ij} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,\overline{s(k,\ell)}}{s(I,\ell)}, + + where `N^k_{ij}` are the fusion coefficients, i.e. the structure + constants of the fusion ring, and ``I`` is the unit object. + The S-matrix has the property that if `i*` denotes the dual + object of `i`, implemented in Sage as ``i.dual()``, then + + .. MATH:: + + s(i*,j) = s(i,j*) = \overline{s(i,j)}. + + This is equation (16.5) in [DFMS1996]_. Thus with `N_{ijk}=N^{k*}_{ij}` + the Verlinde formula is equivalent to + + .. MATH:: + + N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, + + In this formula `s` is the normalized unitary S-matrix + denoted `s` in [BaKi2001]_. We may define a function that + corresponds to the right-hand side, except using + `\tilde{s}` instead of `s`:: + + sage: def V(i,j,k): + ....: R = i.parent() + ....: return sum(R.s_ij(i,l) * R.s_ij(j,l) * R.s_ij(k,l) / R.s_ij(R.one(),l) + ....: for l in R.basis()) + + This does not produce ``self.N_ijk(i,j,k)`` exactly, because of the + missing normalization factor. The following code to check the + Verlinde formula takes this into account:: + + sage: def test_verlinde(R): + ....: b0 = R.one() + ....: c = R.global_q_dimension() + ....: return all(V(i,j,k) == c * R.N_ijk(i,j,k) for i in R.basis() + ....: for j in R.basis() for k in R.basis()) + + Every fusion ring should pass this test:: + + sage: test_verlinde(FusionRing("A2",1)) + True + sage: test_verlinde(FusionRing("B4",2)) # long time (.56s) + True + + As an exercise, the reader may verify the examples in + Section 5.3 of [RoStWa2009]_. Here we check the example + of the Ising modular tensor category, which is related + to the BPZ minimal model `M(4,3)` or to an `E_8` coset + model. See [DFMS1996]_ Sections 7.4.2 and 18.4.1. + [RoStWa2009]_ Example 5.3.4 tells us how to + construct it as the conjugate of the `E_8` level 2 + :class:`FusionRing`:: + + sage: I = FusionRing("E8",2,conjugate=True) + sage: I.fusion_labels(["i0","p","s"],inject_variables=True) + sage: b = I.basis().list(); b + [i0, p, s] + sage: [[x*y for x in b] for y in b] + [[i0, p, s], [p, i0, s], [s, s, i0 + p]] + sage: [x.twist() for x in b] + [0, 1, 1/8] + sage: I.global_q_dimension() + 4 + sage: I.total_q_order() + 2 + sage: [x.q_dimension()^2 for x in b] + [1, 1, 2] + sage: I.s_matrix() + [ 1 1 -zeta128^48 + zeta128^16] + [ 1 1 zeta128^48 - zeta128^16] + [-zeta128^48 + zeta128^16 zeta128^48 - zeta128^16 0] + sage: I.s_matrix().apply_map(lambda x:x^2) + [1 1 2] + [1 1 2] + [2 2 0] + + The term *modular tensor category* refers to the fact that associated + with the category there is a projective representation of the modular + group `SL(2,\ZZ)`. We recall that this group is generated by + + .. MATH:: + + S = \begin{pmatrix} & -1\\1\end{pmatrix},\qquad + T = \begin{pmatrix} 1 & 1\\ &1 \end{pmatrix} + + subject to the relations `(ST)^3 = S^2`, `S^2T = TS^2`, and `S^4 = I`. + Let `s` be the normalized S-matrix, and + `t` the diagonal matrix whose entries are the twists of the simple + objects. Let `s` the unitary S-matrix and `t` the matrix of twists, + and `C` the conjugation matrix :meth:`conj_matrix`. Let + + .. MATH:: + + D_+ = \sum_i d_i^2 \theta_i, \qquad D_- = d_i^2 \theta_i^{-1}, + + where `d_i` and `\theta_i` are the quantum dimensions and twists of the + simple objects. Let `c` be the Virasoro central charge, a rational number + that is computed in :meth:`virasoro_central_charge`. It is known that + + .. MATH:: + + \sqrt{\frac{D_+}{D_-}} = e^{i\pi c/4}. + + It is proved in [BaKi2001]_ Equation (3.1.17) that + + .. MATH:: + + (st)^3 = e^{i\pi c/4} s^2, \qquad + s^2 = C, \qquad C^2 = 1, \qquad Ct = tC. + + Therefore `S \mapsto s, T \mapsto t` is a projective representation + of `SL(2, \ZZ)`. Let us confirm these identities for the Fibonacci MTC + ``FusionRing("G2", 1)``:: + + sage: R = FusionRing("G2",1) + sage: S = R.s_matrix(unitary=True) + sage: T = R.twists_matrix() + sage: C = R.conj_matrix() + sage: c = R.virasoro_central_charge(); c + 14/5 + sage: (S*T)^3 == R.root_of_unity(c/4) * S^2 + True + sage: S^2 == C + True + sage: C*T == T*C + True + """ + @staticmethod + def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False, cyclotomic_order=None): + """ + Normalize input to ensure a unique representation. + + TESTS:: + + sage: F1 = FusionRing('B3', 2) + sage: F2 = FusionRing(CartanType('B3'), QQ(2), ZZ) + sage: F3 = FusionRing(CartanType('B3'), int(2), style="coroots") + sage: F1 is F2 and F2 is F3 + True + + sage: A23 = FusionRing('A2', 3) + sage: TestSuite(A23).run() + + sage: B22 = FusionRing('B2', 2) + sage: TestSuite(B22).run() + + sage: C31 = FusionRing('C3', 1) + sage: TestSuite(C31).run() + + sage: D41 = FusionRing('D4', 1) + sage: TestSuite(D41).run() + + sage: G22 = FusionRing('G2', 2) + sage: TestSuite(G22).run() + + sage: F41 = FusionRing('F4', 1) + sage: TestSuite(F41).run() + + sage: E61 = FusionRing('E6', 1) + sage: TestSuite(E61).run() + + sage: E71 = FusionRing('E7', 1) + sage: TestSuite(E71).run() + + sage: E81 = FusionRing('E8', 1) + sage: TestSuite(E81).run() + """ + return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, + prefix=prefix, style=style, k=k, + conjugate=conjugate, + cyclotomic_order=cyclotomic_order) + + def _test_verlinde(self, **options): + """ + Check the Verlinde formula for this :class:`FusionRing` instance. + + EXAMPLES:: + + sage: G22 = FusionRing("G2",2) + sage: G22._test_verlinde() + """ + tester = self._tester(**options) + c = self.global_q_dimension() + i0 = self.one() + from sage.misc.misc import some_tuples + B = self.basis() + for x,y,z in some_tuples(B, 3, tester._max_runs): + v = sum(self.s_ij(x,w) * self.s_ij(y,w) * self.s_ij(z,w) / self.s_ij(i0,w) for w in B) + tester.assertEqual(v, c * self.N_ijk(x,y,z)) + + def _test_total_q_order(self, **options): + r""" + Check that the total quantum order is real and positive. + + The total quantum order is the positive square root + of the global quantum dimension. This indirectly test the + Virasoro central charge. + + EXAMPLES:: + + sage: G22 = FusionRing("G2",2) + sage: G22._test_total_q_order() + """ + tester = self._tester(**options) + tqo = self.total_q_order() + tester.assertTrue(tqo.is_real_positive()) + tester.assertTrue(tqo**2 == self.global_q_dimension()) + + def fusion_labels(self, labels=None, inject_variables=False): + r""" + Set the labels of the basis. + + INPUT: + + - ``labels`` -- (default: ``None``) a list of strings or string + - ``inject_variables`` -- (default: ``False``) if ``True``, then + inject the variable names into the global namespace; note that + this could override objects already defined + + If ``labels`` is a list, the length of the list must equal the + number of basis elements. These become the names of + the basis elements. + + If ``labels`` is a string, this is treated as a prefix and a + list of names is generated. + + If ``labels`` is ``None``, then this resets the labels to the default. + + EXAMPLES:: + + sage: A13 = FusionRing("A1", 3) + sage: A13.fusion_labels("x") + sage: fb = list(A13.basis()); fb + [x0, x1, x2, x3] + sage: Matrix([[x*y for y in A13.basis()] for x in A13.basis()]) + [ x0 x1 x2 x3] + [ x1 x0 + x2 x1 + x3 x2] + [ x2 x1 + x3 x0 + x2 x1] + [ x3 x2 x1 x0] + + We give an example where the variables are injected into the + global namepsace:: + + sage: A13.fusion_labels("y", inject_variables=True) + sage: y0 + y0 + sage: y0.parent() is A13 + True + + We reset the labels to the default:: + + sage: A13.fusion_labels() + sage: fb + [A13(0), A13(1), A13(2), A13(3)] + sage: y0 + A13(0) + """ + if labels is None: + # Remove the fusion labels + self._fusion_labels = None + return + + B = self.basis() + if isinstance(labels, str): + labels = [labels + str(k) for k in range(len(B))] + elif len(labels) != len(B): + raise ValueError('invalid data') + + d = {} + ac = self.simple_coroots() + for j, b in enumerate(self.get_order()): + t = tuple([b.inner_product(x) for x in ac]) + d[t] = labels[j] + if inject_variables: + inject_variable(labels[j], B[b]) + self._fusion_labels = d + + @cached_method + def field(self): + r""" + Return a cyclotomic field large enough to + contain the `2 \ell`-th roots of unity, as well as + all the S-matrix entries. + + EXAMPLES:: + + sage: FusionRing("A2",2).field() + Cyclotomic Field of order 60 and degree 16 + sage: FusionRing("B2",2).field() + Cyclotomic Field of order 40 and degree 16 + """ + return CyclotomicField(4 * self._cyclotomic_order) + + def root_of_unity(self, r): + r""" + Return `e^{i\pi r}` as an element of ``self.field()`` if possible. + + INPUT: + + - ``r`` -- a rational number + + EXAMPLES:: + + sage: A11 = FusionRing("A1",1) + sage: A11.field() + Cyclotomic Field of order 24 and degree 8 + sage: [A11.root_of_unity(2/x) for x in [1..7]] + [1, -1, zeta24^4 - 1, zeta24^6, None, zeta24^4, None] + """ + n = 2 * r * self._cyclotomic_order + if n in ZZ: + return self.field().gen() ** n + else: + return None + + def get_order(self): + r""" + Return the weights of the basis vectors in a fixed order. + + You may change the order of the basis using :meth:`CombinatorialFreeModule.set_order` + + EXAMPLES:: + + sage: A14 = FusionRing("A1",4) + sage: w = A14.get_order(); w + [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] + sage: A14.set_order([w[k] for k in [0,4,1,3,2]]) + sage: [A14(x) for x in A14.get_order()] + [A14(0), A14(4), A14(1), A14(3), A14(2)] + + .. WARNING:: + + This duplicates :meth:`get_order` from + :class:`CombinatorialFreeModule` except the result + is *not* cached. Caching of + :meth:`CombinatorialFreeModule.get_order` causes inconsistent + results after calling :meth:`CombinatorialFreeModule.set_order`. + + """ + if self._order is None: + self.set_order(self.basis().keys().list()) + return self._order + + def some_elements(self): + """ + Return some elements of ``self``. + + EXAMPLES:: + + sage: D41 = FusionRing('D4', 1) + sage: D41.some_elements() + [D41(1,0,0,0), D41(0,0,1,0), D41(0,0,0,1)] + """ + return [self.monomial(x) for x in self.fundamental_weights() + if self.level(x) <= self._k] + + def fusion_level(self): + r""" + Return the level `k` of ``self``. + + EXAMPLES:: + + sage: B22 = FusionRing('B2',2) + sage: B22.fusion_level() + 2 + """ + return self._k + + def fusion_l(self): + r""" + Return the product `\ell = m_g(k + h^\vee)`, where `m_g` denotes the + square of the ratio of the lengths of long to short roots of + the underlying Lie algebra, `k` denotes the level of the FusionRing, + and `h^\vee` denotes the dual Coxeter number of the underlying Lie + algebra. + + This value is used to define the associated root `2\ell`-th + of unity `q = e^{i\pi/\ell}`. + + EXAMPLES:: + + sage: B22 = FusionRing('B2',2) + sage: B22.fusion_l() + 10 + sage: D52 = FusionRing('D5',2) + sage: D52.fusion_l() + 10 + """ + return self._l + + def virasoro_central_charge(self): + r""" + Return the Virasoro central charge of the WZW conformal + field theory associated with the Fusion Ring. + + If `\mathfrak{g}` is the corresponding semisimple Lie algebra, this is + + .. MATH:: + + \frac{k\dim\mathfrak{g}}{k+h^\vee}, + + where `k` is the level and `h^\vee` is the dual Coxeter number. + See [DFMS1996]_ Equation (15.61). + + Let `d_i` and `theta_i` be the quantum dimensions and + twists of the simple objects. By Proposition 2.3 in [RoStWa2009]_, + there exists a rational number `c` such that + `D_+ / \sqrt{D} = e^{i\pi c/4}`, where `D_+ = \sum d_i^2 \theta_i` + is computed in :meth:`D_plus` and `D = \sum d_i^2 > 0` is computed + by :meth:`global_q_dimension`. Squaring this identity and + remembering that `D_+ D_- = D` gives + + .. MATH:: + + D_+ / D_- = e^{i\pi c/2}. + + EXAMPLES:: + + sage: R = FusionRing("A1", 2) + sage: c = R.virasoro_central_charge(); c + 3/2 + sage: Dp = R.D_plus(); Dp + 2*zeta32^6 + sage: Dm = R.D_minus(); Dm + -2*zeta32^10 + sage: Dp / Dm == R.root_of_unity(c/2) + True + """ + dim_g = len(self.space().roots()) + self.cartan_type().rank() + return self._conj * self._k * dim_g / (self._k + self._h_check) + + def conj_matrix(self): + r""" + Return the conjugation matrix, which is the permutation matrix + for the conjugation (dual) operation on basis elements. + + EXAMPLES:: + + sage: FusionRing("A2",1).conj_matrix() + [1 0 0] + [0 0 1] + [0 1 0] + """ + b = self.basis().list() + return matrix(ZZ, [[i == j.dual() for i in b] for j in b]) + + def twists_matrix(self): + r""" + Return a diagonal matrix describing the twist corresponding to + each simple object in the ``FusionRing``. + + EXAMPLES:: + + sage: B21=FusionRing("B2",1) + sage: [x.twist() for x in B21.basis().list()] + [0, 1, 5/8] + sage: [B21.root_of_unity(x.twist()) for x in B21.basis().list()] + [1, -1, zeta32^10] + sage: B21.twists_matrix() + [ 1 0 0] + [ 0 -1 0] + [ 0 0 zeta32^10] + """ + B = self.basis() + return diagonal_matrix(self.root_of_unity(B[x].twist()) for x in self.get_order()) + + @cached_method + def N_ijk(self, elt_i, elt_j, elt_k): + r""" + Return the symmetric fusion coefficient `N_{ijk}`. + + INPUT: + + - ``elt_i``, ``elt_j``, ``elt_k`` -- elements of the fusion basis + + This is the same as `N_{ij}^{k\ast}`, where `N_{ij}^k` are + the structure coefficients of the ring (see :meth:`Nk_ij`), + and `k\ast`` denotes the dual element. The coefficient `N_{ijk}` + is unchanged under permutations of the three basis vectors. + + EXAMPLES:: + + sage: G23 = FusionRing("G2", 3) + sage: G23.fusion_labels("g") + sage: b = G23.basis().list(); b + [g0, g1, g2, g3, g4, g5] + sage: [(x,y,z) for x in b for y in b for z in b if G23.N_ijk(x,y,z) > 1] + [(g3, g3, g3), (g3, g3, g4), (g3, g4, g3), (g4, g3, g3)] + sage: all(G23.N_ijk(x,y,z)==G23.N_ijk(y,z,x) for x in b for y in b for z in b) + True + sage: all(G23.N_ijk(x,y,z)==G23.N_ijk(y,x,z) for x in b for y in b for z in b) + True + """ + return (elt_i * elt_j).monomial_coefficients().get(elt_k.dual().weight(), 0) + + @cached_method + def Nk_ij(self, elt_i, elt_j, elt_k): + r""" + Return the fusion coefficient `N^k_{ij}`. + + These are the structure coefficients of the fusion ring, so + + .. MATH:: + + i * j = \sum_{k} N_{ij}^k k. + + EXAMPLES:: + + sage: A22 = FusionRing("A2", 2) + sage: b = A22.basis().list() + sage: all(x*y == sum(A22.Nk_ij(x,y,k)*k for k in b) for x in b for y in b) + True + """ + return (elt_i * elt_j).monomial_coefficients(copy=False).get(elt_k.weight(), 0) + + @cached_method + def s_ij(self, elt_i, elt_j): + r""" + Return the element of the S-matrix of this fusion ring corresponding to + the given elements. + + This is computed using the formula + + .. MATH:: + + s_{i,j} = \frac{1}{\theta_i\theta_j} \sum_k N_{ik}^j d_k \theta_k, + + where `\theta_k` is the twist and `d_k` is the quantum + dimension. See [Row2006]_ Equation (2.2) or [EGNO2015]_ + Proposition 8.13.8. + + INPUT: + + - ``elt_i``, ``elt_j`` -- elements of the fusion basis + + EXAMPLES:: + + sage: G21 = FusionRing("G2", 1) + sage: b = G21.basis() + sage: [G21.s_ij(x, y) for x in b for y in b] + [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] + """ + ijtwist = elt_i.twist() + elt_j.twist() + return sum(k.q_dimension() * self.Nk_ij(elt_i, k, elt_j) + * self.root_of_unity(k.twist() - ijtwist) + for k in self.basis()) + + def s_matrix(self, unitary=False): + r""" + Return the S-matrix of this fusion ring. + + OPTIONAL: + + - ``unitary`` -- (default: ``False``) set to ``True`` to obtain + the unitary S-matrix + + Without the ``unitary`` parameter, this is the matrix denoted + `\widetilde{s}` in [BaKi2001]_. + + EXAMPLES:: + + sage: D91 = FusionRing("D9", 1) + sage: D91.s_matrix() + [ 1 1 1 1] + [ 1 1 -1 -1] + [ 1 -1 -zeta136^34 zeta136^34] + [ 1 -1 zeta136^34 -zeta136^34] + sage: S = D91.s_matrix(unitary=True); S + [ 1/2 1/2 1/2 1/2] + [ 1/2 1/2 -1/2 -1/2] + [ 1/2 -1/2 -1/2*zeta136^34 1/2*zeta136^34] + [ 1/2 -1/2 1/2*zeta136^34 -1/2*zeta136^34] + sage: S*S.conjugate() + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + [0 0 0 1] + """ + b = self.basis() + S = matrix([[self.s_ij(b[x], b[y]) for x in self.get_order()] for y in self.get_order()]) + if unitary: + return S / self.total_q_order() + else: + return S + + def global_q_dimension(self): + r""" + Return `\sum d_i^2`, where the sum is over all simple objects + and `d_i` is the quantum dimension. It is a positive real number. + + EXAMPLES:: + + sage: FusionRing("E6",1).global_q_dimension() + 3 + """ + return sum(x.q_dimension()**2 for x in self.basis()) + + def total_q_order(self): + r""" + Return the positive square root of ``self.global_q_dimension()`` + as an element of ``self.field()``. + + EXAMPLES:: + + sage: F = FusionRing("G2",1) + sage: tqo=F.total_q_order(); tqo + zeta60^15 - zeta60^11 - zeta60^9 + 2*zeta60^3 + zeta60 + sage: tqo.is_real_positive() + True + sage: tqo^2 == F.global_q_dimension() + True + """ + c = self.virasoro_central_charge() + return self.D_plus() * self.root_of_unity(-c/4) + + def D_plus(self): + r""" + Return `\sum d_i^2\theta_i` where `i` runs through the simple objects, + `d_i` is the quantum dimension and `\theta_i` is the twist. + + This is denoted `p_+` in [BaKi2001]_ Chapter 3. + + EXAMPLES:: + + sage: B31 = FusionRing("B3",1) + sage: Dp = B31.D_plus(); Dp + 2*zeta48^13 - 2*zeta48^5 + sage: Dm = B31.D_minus(); Dm + -2*zeta48^3 + sage: Dp*Dm == B31.global_q_dimension() + True + sage: c = B31.virasoro_central_charge(); c + 7/2 + sage: Dp/Dm == B31.root_of_unity(c/2) + True + """ + return sum((x.q_dimension())**2 * self.root_of_unity(x.twist()) for x in self.basis()) + + def D_minus(self): + r""" + Return `\sum d_i^2\theta_i^{-1}` where `i` runs through the simple + objects, `d_i` is the quantum dimension and `\theta_i` is the twist. + + This is denoted `p_-` in [BaKi2001]_ Chapter 3. + + EXAMPLES:: + + sage: E83 = FusionRing("E8",3,conjugate=True) + sage: [Dp,Dm] = [E83.D_plus(), E83.D_minus()] + sage: Dp*Dm == E83.global_q_dimension() + True + sage: c = E83.virasoro_central_charge(); c + -248/11 + sage: Dp*Dm == E83.global_q_dimension() + True + """ + return sum((x.q_dimension())**2 * self.root_of_unity(-x.twist()) for x in self.basis()) + + class Element(WeylCharacterRing.Element): + """ + A class for FusionRing elements. + """ + def is_simple_object(self): + r""" + Determine whether ``self`` is a simple object of the fusion ring. + + EXAMPLES:: + + sage: A22 = FusionRing("A2", 2) + sage: x = A22(1,0); x + A22(1,0) + sage: x.is_simple_object() + True + sage: x^2 + A22(0,1) + A22(2,0) + sage: (x^2).is_simple_object() + False + """ + return self.parent()._k is not None and len(self._monomial_coefficients) == 1 + + def weight(self): + r""" + Return the parametrizing dominant weight in the level `k` alcove. + + This method is only available for basis elements. + + EXAMPLES:: + + sage: A21 = FusionRing("A2",1) + sage: [x.weight() for x in A21.basis().list()] + [(0, 0, 0), (2/3, -1/3, -1/3), (1/3, 1/3, -2/3)] + """ + if len(self._monomial_coefficients) != 1: + raise ValueError("fusion weight is valid for basis elements only") + return next(iter(self._monomial_coefficients)) + + def twist(self): + r""" + Return a rational number `h` such that `\theta = e^{i \pi h}` + is the twist of ``self``. + + This method is only available for simple objects. If + `\lambda` is the weight of the object, then + `h = \langle \lambda, \lambda+2\rho \rangle`, where + `\rho` is half the sum of the positive roots. + As in [Row2006]_, this requires normalizing + the invariant bilinear form so that + `\langle \alpha, \alpha \rangle = 2` for short roots. + + EXAMPLES:: + + sage: G21 = FusionRing("G2", 1) + sage: [x.twist() for x in G21.basis()] + [0, 4/5] + sage: [G21.root_of_unity(x.twist()) for x in G21.basis()] + [1, zeta60^14 - zeta60^4] + sage: zeta60 = G21.field().gen() + sage: zeta60^((4/5)*(60/2)) + zeta60^14 - zeta60^4 + + sage: F42 = FusionRing("F4", 2) + sage: [x.twist() for x in F42.basis()] + [0, 18/11, 2/11, 12/11, 4/11] + + sage: E62 = FusionRing("E6", 2) + sage: [x.twist() for x in E62.basis()] + [0, 26/21, 12/7, 8/21, 8/21, 26/21, 2/3, 4/7, 2/3] + """ + if not self.is_simple_object(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + P = self.parent() + rho = P.space().rho() + # We copy self.weight() to skip the test (which was already done + # by self.is_simple_object()). + lam = next(iter(self._monomial_coefficients)) + inner = lam.inner_product(lam + 2*rho) + twist = P._conj * P._nf * inner / P.fusion_l() + # Reduce to canonical form + f = twist.floor() + twist -= f + return twist + (f % 2) + + @cached_method + def q_dimension(self): + r""" + Return the quantum dimension as an element of the cyclotomic + field of the `2\ell`-th roots of unity, where `l = m (k+h^\vee)` + with `m=1,2,3` depending on whether type is simply, doubly or + triply laced, `k` is the level and `h^\vee` is the dual + Coxeter number. + + EXAMPLES:: + + sage: B22 = FusionRing("B2",2) + sage: [(b.q_dimension())^2 for b in B22.basis()] + [1, 4, 5, 1, 5, 4] + """ + if not self.is_simple_object(): + raise ValueError("quantum dimension is only available for simple objects of a FusionRing") + P = self.parent() + lam = self.weight() + space = P.space() + rho = space.rho() + powers = {} + for alpha in space.positive_roots(): + val = alpha.inner_product(lam + rho) + if val in powers: + powers[val] += 1 + else: + powers[val] = 1 + val = alpha.inner_product(rho) + if val in powers: + powers[val] -= 1 + else: + powers[val] = -1 + R = ZZ['q'] + q = R.gen() + expr = R.fraction_field().one() + for val in powers: + exp = powers[val] + if exp > 0: + expr *= q_int(P._nf * val, q)**exp + elif exp < 0: + expr /= q_int(P._nf * val, q)**(-exp) + expr = R(expr) + expr = expr.substitute(q=q**4) / (q**(2*expr.degree())) + zet = P.field().gen() ** P._fg + return expr.substitute(q=zet) + diff --git a/src/sage/combinat/root_system/hecke_algebra_representation.py b/src/sage/combinat/root_system/hecke_algebra_representation.py index 7c4f64a6a5e..51f41137062 100644 --- a/src/sage/combinat/root_system/hecke_algebra_representation.py +++ b/src/sage/combinat/root_system/hecke_algebra_representation.py @@ -602,6 +602,7 @@ def Y_lambdacheck(self, lambdacheck): # (co)weight space, because the alcove walks currently uses # rho_classical and, in type BC, the later does not have # integral coefficients: + # sage: RootSystem(["BC",2,2]).coweight_lattice().rho_classical() # On the other hand, at this point we need the expression of diff --git a/src/sage/combinat/root_system/pieri_factors.py b/src/sage/combinat/root_system/pieri_factors.py index 9f3056ef73e..0e66a15aaab 100644 --- a/src/sage/combinat/root_system/pieri_factors.py +++ b/src/sage/combinat/root_system/pieri_factors.py @@ -7,9 +7,8 @@ # Nicolas M. Thiery # # Distributed under the terms of the GNU General Public License (GPL) -# https://www.gnu.org/licenses/ -# ***************************************************************************** -from six.moves import range +# http://www.gnu.org/licenses/ +#****************************************************************************** from sage.misc.cachefunc import cached_method from sage.misc.constant_function import ConstantFunction diff --git a/src/sage/combinat/root_system/plot.py b/src/sage/combinat/root_system/plot.py index 627f7875b50..f2bc78e04b7 100644 --- a/src/sage/combinat/root_system/plot.py +++ b/src/sage/combinat/root_system/plot.py @@ -141,7 +141,7 @@ One can also customize the projection by specifying a function. Here, we display all the roots for type `E_8` using the projection from its eight dimensional ambient space onto 3D described on -:wikipedia:`Wikipedia's E8 3D picture `:: +:wikipedia:`Wikipedia%27s E8 3D picture `:: sage: M = matrix([[0., -0.556793440452, 0.19694925177, -0.19694925177, 0.0805477263944, -0.385290876171, 0., 0.385290876171], ....: [0.180913155536, 0., 0.160212955043, 0.160212955043, 0., 0.0990170516545, 0.766360424875, 0.0990170516545], @@ -805,7 +805,6 @@ # **************************************************************************** from __future__ import print_function -import six from sage.misc.cachefunc import cached_method, cached_function from sage.misc.latex import latex from sage.misc.lazy_import import lazy_import @@ -988,13 +987,13 @@ def text(self, label, position, rgbcolor=(0,0,0)): """ if self.labels: if self.dimension <= 2: - if not isinstance(label, six.string_types): + if not isinstance(label, str): label = "$"+str(latex(label))+"$" from sage.plot.text import text return text(label, position, fontsize=15, rgbcolor=rgbcolor) elif self.dimension == 3: # LaTeX labels not yet supported in 3D - if isinstance(label, six.string_types): + if isinstance(label, str): label = label.replace("{","").replace("}","").replace("$","").replace("_","") else: label = str(label) diff --git a/src/sage/combinat/root_system/reflection_group_complex.py b/src/sage/combinat/root_system/reflection_group_complex.py index 0567f8bf304..746cdb8b55a 100644 --- a/src/sage/combinat/root_system/reflection_group_complex.py +++ b/src/sage/combinat/root_system/reflection_group_complex.py @@ -196,8 +196,6 @@ #***************************************************************************** from __future__ import print_function -#from six.moves import range - from sage.misc.cachefunc import cached_method, cached_function from sage.misc.misc_c import prod from sage.categories.category import Category @@ -216,10 +214,9 @@ from sage.modules.free_module_element import vector from sage.combinat.root_system.cartan_matrix import CartanMatrix from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField - - from sage.misc.sage_eval import sage_eval + class ComplexReflectionGroup(UniqueRepresentation, PermutationGroup_generic): """ A complex reflection group given as a permutation group. @@ -478,7 +475,7 @@ def distinguished_reflections(self): sage: W = ReflectionGroup((1,1,3),hyperplane_index_set=['a','b','c']) # optional - gap3 sage: W.distinguished_reflections() # optional - gap3 - Finite family {'a': (1,4)(2,3)(5,6), 'c': (1,5)(2,4)(3,6), 'b': (1,3)(2,5)(4,6)} + Finite family {'a': (1,4)(2,3)(5,6), 'b': (1,3)(2,5)(4,6), 'c': (1,5)(2,4)(3,6)} sage: W = ReflectionGroup((3,1,1)) # optional - gap3 sage: W.distinguished_reflections() # optional - gap3 @@ -675,7 +672,7 @@ def reflections(self): sage: W = ReflectionGroup((1,1,3),reflection_index_set=['a','b','c']) # optional - gap3 sage: W.reflections() # optional - gap3 - Finite family {'a': (1,4)(2,3)(5,6), 'c': (1,5)(2,4)(3,6), 'b': (1,3)(2,5)(4,6)} + Finite family {'a': (1,4)(2,3)(5,6), 'b': (1,3)(2,5)(4,6), 'c': (1,5)(2,4)(3,6)} sage: W = ReflectionGroup((3,1,1)) # optional - gap3 sage: W.reflections() # optional - gap3 @@ -967,9 +964,8 @@ def conjugacy_classes_representatives(self): [1, 2, 1, 2, 1, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3]] """ # This can be converted to usual GAP - S = str(gap3('List(ConjugacyClasses(%s),Representative)'%self._gap_group._name)) - exec('_conjugacy_classes_representatives=' + _gap_return(S)) - return _conjugacy_classes_representatives + S = str(gap3('List(ConjugacyClasses(%s),Representative)' % self._gap_group._name)) + return sage_eval(_gap_return(S), {'self': self}) def conjugacy_classes(self): r""" @@ -1318,7 +1314,7 @@ def independent_roots(self): basis = {} for ind in self._index_set: vec = Delta[ind] - if Matrix(basis.values()+[vec]).rank() == len(basis) + 1: + if Matrix(list(basis.values()) + [vec]).rank() == len(basis) + 1: basis[ind] = vec return Family(basis) @@ -1367,7 +1363,7 @@ def roots(self): (0, 0, 0, -E(3), E(3)^2), (0, 0, 0, E(3)^2, -E(3)^2), (0, 0, 0, -E(3)^2, E(3)^2)] """ - roots = [vector(sage_eval(str(root).replace("^","**"))) + roots = [vector(sage_eval(str(root).replace("^", "**"))) for root in self._gap_group.roots] for v in roots: v.set_immutable() @@ -1382,15 +1378,15 @@ def braid_relations(self): sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.braid_relations() # optional - gap3 - [[[2, 1, 2], [1, 2, 1]]] + [[[1, 2, 1], [2, 1, 2]]] sage: W = ReflectionGroup((2,1,3)) # optional - gap3 sage: W.braid_relations() # optional - gap3 - [[[2, 1, 2, 1], [1, 2, 1, 2]], [[3, 1], [1, 3]], [[3, 2, 3], [2, 3, 2]]] + [[[1, 2, 1, 2], [2, 1, 2, 1]], [[1, 3], [3, 1]], [[2, 3, 2], [3, 2, 3]]] sage: W = ReflectionGroup((2,2,3)) # optional - gap3 sage: W.braid_relations() # optional - gap3 - [[[2, 1, 2], [1, 2, 1]], [[3, 1], [1, 3]], [[3, 2, 3], [2, 3, 2]]] + [[[1, 2, 1], [2, 1, 2]], [[1, 3], [3, 1]], [[2, 3, 2], [3, 2, 3]]] """ if self.is_real(): return super(ComplexReflectionGroup,self).braid_relations() diff --git a/src/sage/combinat/root_system/reflection_group_element.pyx b/src/sage/combinat/root_system/reflection_group_element.pyx index ed552674922..50d63f5855d 100644 --- a/src/sage/combinat/root_system/reflection_group_element.pyx +++ b/src/sage/combinat/root_system/reflection_group_element.pyx @@ -39,6 +39,40 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): """ An element in a complex reflection group. """ + def __hash__(self): + r""" + Return a hash for this reflection group element. + + This hash stores both the element as a reduced word and the parent group. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',5]) # optional - gap3 + sage: W_hash = set(hash(w) for w in W) # optional - gap3 + sage: len(W_hash) == W.cardinality() # optional - gap3 + True + + TESTS: + + Check that types B and C are hashed differently, see :trac:`29726`:: + + sage: WB = ReflectionGroup(['B',5]) # optional - gap3 + sage: WC = ReflectionGroup(['C',5]) # optional - gap3 + + sage: WB_hash = set(hash(w) for w in WB) # optional - gap3 + sage: WC_hash = set(hash(w) for w in WC) # optional - gap3 + + sage: len(WB_hash) == WB.cardinality() # optional - gap3 + True + + sage: len(WC_hash) == WC.cardinality() # optional - gap3 + True + + sage: WB_hash.intersection(WC_hash) # optional - gap3 + set() + """ + return hash(self._parent) | hash(tuple(self._reduced_word)) + def reduced_word(self): r""" Return a word in the simple reflections to obtain ``self``. @@ -423,7 +457,8 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): (1,2,6)(3,4,5) True (1,5)(2,4)(3,6) True """ - return PermutationGroupElement(self) + W = self._parent + return PermutationGroupElement(self, W) #@cached_in_parent_method def fix_space(self): @@ -465,8 +500,8 @@ cdef class ComplexReflectionGroupElement(PermutationGroupElement): Basis matrix: [ 1 -1] - sage: W = ReflectionGroup(23) # optional - gap3 - sage: W.an_element().fix_space() # optional - gap3 + sage: W = ReflectionGroup(23) # optional - gap3 + sage: W.gen(0).fix_space() # optional - gap3 Vector space of degree 3 and dimension 2 over Universal Cyclotomic Field Basis matrix: [0 1 0] @@ -1175,4 +1210,3 @@ def _gap_return(S, coerce_obj='self'): S = S.replace(' ','').replace('\n','') S = S.replace(',(','\',check=False),%s(\'('%coerce_obj).replace('[','[%s(\''%coerce_obj).replace(']','\',check=False)]') return S - diff --git a/src/sage/combinat/root_system/reflection_group_real.py b/src/sage/combinat/root_system/reflection_group_real.py index 96cbbcd9431..395bdb52cab 100644 --- a/src/sage/combinat/root_system/reflection_group_real.py +++ b/src/sage/combinat/root_system/reflection_group_real.py @@ -45,8 +45,6 @@ # **************************************************************************** from __future__ import print_function -from six.moves import range - from sage.misc.cachefunc import cached_function, cached_method, cached_in_parent_method from sage.combinat.root_system.cartan_type import CartanType, CartanType_abstract from sage.rings.all import ZZ @@ -407,11 +405,27 @@ def cartan_type(self): sage: W = ReflectionGroup(['A',3], ['B',3]) # optional - gap3 sage: W.cartan_type() # optional - gap3 - A3xB3 relabelled by {1: 3, 2: 2, 3: 1} + A3xB3 relabelled by {1: 3, 2: 2, 3: 1} + + TESTS: + + Check that dihedral types are handled properly:: + + sage: W = ReflectionGroup(['I',3]); W # optional - gap3 + Irreducible real reflection group of rank 2 and type A2 + + sage: W = ReflectionGroup(['I',4]); W # optional - gap3 + Irreducible real reflection group of rank 2 and type C2 + + sage: W = ReflectionGroup(['I',5]); W # optional - gap3 + Irreducible real reflection group of rank 2 and type I2(5) """ if len(self._type) == 1: ct = self._type[0] - C = CartanType([ct['series'], ct['rank']]) + if ct['series'] == "I": + C = CartanType([ct['series'], ct['bond']]) + else: + C = CartanType([ct['series'], ct['rank']]) CG = C.coxeter_diagram() G = self.coxeter_diagram() return C.relabel(CG.is_isomorphic(G, edge_labels=True, certificate=True)[1]) diff --git a/src/sage/combinat/root_system/root_lattice_realization_algebras.py b/src/sage/combinat/root_system/root_lattice_realization_algebras.py index 9368d309291..e391955783d 100644 --- a/src/sage/combinat/root_system/root_lattice_realization_algebras.py +++ b/src/sage/combinat/root_system/root_lattice_realization_algebras.py @@ -21,8 +21,6 @@ from sage.modules.free_module_element import vector from sage.combinat.root_system.hecke_algebra_representation import HeckeAlgebraRepresentation -import six - class Algebras(AlgebrasCategory): """ @@ -119,7 +117,8 @@ def from_polynomial(self, p): .. TODO:: make this work for Laurent polynomials too """ L = self.basis().keys() - return self.sum_of_terms((L.from_vector(vector(t)), c) for (t,c) in six.iteritems(p.dict())) + return self.sum_of_terms((L.from_vector(vector(t)), c) + for (t,c) in p.dict().items()) @cached_method def divided_difference_on_basis(self, weight, i): diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index f55c24f61f1..faced58a262 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -12,10 +12,9 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range from sage.misc.abstract_method import abstract_method, AbstractMethod -from sage.misc.misc import attrcall +from sage.misc.call import attrcall from sage.misc.cachefunc import cached_method, cached_in_parent_method from sage.misc.lazy_attribute import lazy_attribute from sage.misc.lazy_import import LazyImport @@ -1098,7 +1097,7 @@ def nonnesting_partition_lattice(self, facade=False): REFERENCES: .. [Reiner97] Victor Reiner. *Non-crossing partitions for - classical reflection groups*. Discrete Mathematics 177 (1997) + classical reflection groups*. Discrete Mathematics 177 (1997) .. [Arm06] Drew Armstrong. *Generalized Noncrossing Partitions and Combinatorics of Coxeter Groups*. :arxiv:`math/0611106` """ @@ -1284,7 +1283,7 @@ def alphacheck(self): .. todo:: add a non simply laced example - Finaly, here is an affine example:: + Finally, here is an affine example:: sage: RootSystem(["A",2,1]).weight_space().alphacheck() Finite family {0: alphacheck[0], 1: alphacheck[1], 2: alphacheck[2]} @@ -3131,8 +3130,8 @@ def plot_crystal(self, crystal, sage: L = RootSystem(['A',2]).ambient_space() sage: C = crystals.Tableaux(['A',2], shape=[2,1]) - sage: L.plot_crystal(C) - Graphics object consisting of 16 graphics primitives + sage: L.plot_crystal(C, plot_labels='multiplicities') + Graphics object consisting of 15 graphics primitives sage: C = crystals.Tableaux(['A',2], shape=[8,4]) sage: p = L.plot_crystal(C, plot_labels='circles') sage: p.show(figsize=15) @@ -3143,6 +3142,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 +3197,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/root_system/type_E.py b/src/sage/combinat/root_system/type_E.py index 206e01b4c0b..7d6cd905ff6 100644 --- a/src/sage/combinat/root_system/type_E.py +++ b/src/sage/combinat/root_system/type_E.py @@ -12,8 +12,6 @@ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range - from . import ambient_space from sage.rings.all import ZZ from sage.combinat.family import Family diff --git a/src/sage/combinat/root_system/type_F.py b/src/sage/combinat/root_system/type_F.py index be62ee359ec..61cdd485740 100644 --- a/src/sage/combinat/root_system/type_F.py +++ b/src/sage/combinat/root_system/type_F.py @@ -12,8 +12,6 @@ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range - from . import ambient_space from sage.rings.all import ZZ from sage.combinat.family import Family diff --git a/src/sage/combinat/root_system/type_dual.py b/src/sage/combinat/root_system/type_dual.py index 885724a5eb1..ae1d25776d9 100644 --- a/src/sage/combinat/root_system/type_dual.py +++ b/src/sage/combinat/root_system/type_dual.py @@ -10,7 +10,7 @@ # **************************************************************************** from __future__ import print_function, absolute_import -from sage.misc.misc import attrcall +from sage.misc.call import attrcall from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.combinat.root_system import cartan_type @@ -596,14 +596,13 @@ def _repr_(self, compact=False): sage: CartanType(['F', 4, 1]).dual()._repr_(compact = True) 'F4~*' """ - dual_str = self.options.dual_str if self.options.notation == "Kac": if self._type.type() == 'B': if compact: return 'A%s^2'%(self.classical().rank()*2-1) return "['A', %s, 2]"%(self.classical().rank()*2-1) elif self._type.type() == 'BC': - dual_str = '+' # UNUSED ? + pass elif self._type.type() == 'C': if compact: return 'D%s^2'%(self.rank()) diff --git a/src/sage/combinat/root_system/type_folded.py b/src/sage/combinat/root_system/type_folded.py index a894281c331..93891fc941a 100644 --- a/src/sage/combinat/root_system/type_folded.py +++ b/src/sage/combinat/root_system/type_folded.py @@ -19,8 +19,6 @@ from sage.sets.family import Family from sage.combinat.root_system.cartan_type import CartanType -import six - class CartanTypeFolded(UniqueRepresentation, SageObject): r""" @@ -181,7 +179,7 @@ def __classcall_private__(cls, cartan_type, virtual, orbit): if isinstance(orbit, dict): i_set = cartan_type.index_set() orb = [None]*len(i_set) - for k,v in six.iteritems(orbit): + for k,v in orbit.items(): orb[i_set.index(k)] = tuple(v) orbit = tuple(orb) else: diff --git a/src/sage/combinat/root_system/type_relabel.py b/src/sage/combinat/root_system/type_relabel.py index a00b415e50f..01a50856343 100644 --- a/src/sage/combinat/root_system/type_relabel.py +++ b/src/sage/combinat/root_system/type_relabel.py @@ -8,7 +8,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from six.moves import range from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute diff --git a/src/sage/combinat/root_system/type_super_A.py b/src/sage/combinat/root_system/type_super_A.py index 0905f8e2051..cdfc23855a4 100644 --- a/src/sage/combinat/root_system/type_super_A.py +++ b/src/sage/combinat/root_system/type_super_A.py @@ -11,8 +11,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range -from six import iteritems from sage.rings.all import ZZ from sage.misc.cachefunc import cached_method @@ -326,7 +324,7 @@ def inner_product(self, lambdacheck): lambdacheck_mc = lambdacheck._monomial_coefficients result = self.parent().base_ring().zero() - for t,c in iteritems(lambdacheck_mc): + for t,c in lambdacheck_mc.items(): if t not in self_mc: continue if t > 0: @@ -375,7 +373,7 @@ def associated_coroot(self): dep = V.linear_dependence([self._vector_()] + [al[i]._vector_() for i in P.index_set()])[0] I = P.index_set() - return P.sum((-c/dep[0]) * h[I[i]] for i,c in dep[1:].iteritems()) + return P.sum((-c/dep[0]) * h[I[i]] for i,c in dep[1:].items()) def has_descent(self, i, positive=False): """ diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 744fcda226e..a07b06f9e43 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -19,10 +19,8 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet from sage.misc.functional import is_even -from sage.misc.misc import inject_variable from sage.rings.all import ZZ - class WeylCharacterRing(CombinatorialFreeModule): r""" A class for rings of Weyl characters. @@ -92,7 +90,7 @@ class WeylCharacterRing(CombinatorialFreeModule): https://doc.sagemath.org/html/en/thematic_tutorials/lie.html """ @staticmethod - def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None): + def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None): """ TESTS:: @@ -108,9 +106,9 @@ def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None): prefix = ct[0]+str(ct[1]) else: prefix = repr(ct) - return super(WeylCharacterRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k) + return super(WeylCharacterRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate, cyclotomic_order=cyclotomic_order) - def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None): + def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None): """ EXAMPLES:: @@ -146,7 +144,7 @@ def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None): def next_level(wt): return [wt + la for la in fw if self.level(wt + la) <= k] B = list(RecursivelyEnumeratedSet([self._space.zero()], next_level)) - B = [self._space.from_vector_notation(wt, style=style) for wt in B] + B = [self._space.from_vector_notation(wt, style="coroots") for wt in B] else: B = self._space @@ -162,6 +160,42 @@ def next_level(wt): # Register the partial inverse as a conversion self.register_conversion(self.retract) + # Record properties of the FusionRing + # mg = square of long to short root lengths + # nf = normalizing factor for the inner product + # fg = order of the fundamental group (except for Type B) + if k is not None: + if ct[0] in ['A', 'D', 'E']: + self._m_g = 1 + elif ct[0] in ['B', 'C', 'F']: + self._m_g = 2 + else: + self._m_g = 3 + if ct[0] in ['B','F']: + self._nf = 2 + else: + self._nf = 1 + self._h_check = ct.dual_coxeter_number() + self._l = self._m_g * (self._k + self._h_check) + if conjugate: + self._conj = -1 + else: + self._conj = 1 + if ct[0] == 'A': + self._fg = ct[1] + 1 + elif ct[0] == 'E' and ct[1] == 6: + self._fg = 3 + elif ct[0] == 'E' and ct[1] == 7: + self._fg = 2 + elif ct[0] == 'D': + self._fg = 2 + else: + self._fg = 1 + if cyclotomic_order is None: + self._cyclotomic_order = self._fg * self._l + else: + self._cyclotomic_order = cyclotomic_order + @cached_method def ambient(self): """ @@ -195,7 +229,7 @@ def lift_on_basis(self, irr): sage: A2.lift_on_basis(v) 2*a2(1,1,1) + a2(1,2,0) + a2(1,0,2) + a2(2,1,0) + a2(2,0,1) + a2(0,1,2) + a2(0,2,1) - This is consistent with the analogous calculation with symmetric + This is consistent with the analoguous calculation with symmetric Schur functions:: sage: s = SymmetricFunctions(QQ).s() @@ -1221,20 +1255,15 @@ def dual(self): for k in d) def highest_weight(self): - r""" - Return the parametrizing dominant weight of an irreducible - character or simple element of a FusionRing. - - This method is only available for basis elements. + """ + This method is only available for basis elements. Returns the + parametrizing dominant weight of an irreducible character. EXAMPLES:: sage: G2 = WeylCharacterRing("G2", style="coroots") sage: [x.highest_weight() for x in [G2(1,0),G2(0,1)]] [(1, 0, -1), (2, -1, -1)] - sage: A21 = FusionRing("A2",1) - sage: sorted([x.highest_weight() for x in A21.basis()]) - [(0, 0, 0), (1/3, 1/3, -2/3), (2/3, -1/3, -1/3)] """ if len(self.monomial_coefficients()) != 1: raise ValueError("fusion weight is valid for basis elements only") @@ -1598,7 +1627,6 @@ def multiplicity(self, other): raise ValueError("{} is not irreducible".format(other)) return self.coefficient(other.support()[0]) - def irreducible_character_freudenthal(hwv, debug=False): r""" Return the dictionary of multiplicities for the irreducible @@ -2189,161 +2217,3 @@ def demazure_lusztig(self, i, v): return self.demazure_lusztig(i.reduced_word(), v) except Exception: raise ValueError("unknown index {}".format(i)) - - -class FusionRing(WeylCharacterRing): - r""" - Return the Fusion Ring (Verlinde Algebra) of level ``k``. - - INPUT: - - - ``ct`` -- the Cartan type of a simple (finite-dimensional) Lie algebra - - ``k`` -- a nonnegative integer - - This algebra has a basis indexed by the weights of level `\leq k`. - It is implemented as a variant of the :class:`WeylCharacterRing`. - - EXAMPLES:: - - sage: A22 = FusionRing("A2",2) - sage: [f1, f2] = A22.fundamental_weights() - sage: M = [A22(x) for x in [0*f1, 2*f1, 2*f2, f1+f2, f2, f1]] - sage: [M[3] * x for x in M] - [A22(1,1), - A22(0,1), - A22(1,0), - A22(0,0) + A22(1,1), - A22(0,1) + A22(2,0), - A22(1,0) + A22(0,2)] - - You may assign your own labels to the basis elements. In the next - example, we create the `SO(5)` fusion ring of level `2`, check the - weights of the basis elements, then assign new labels to them:: - - sage: B22 = FusionRing("B2", 2) - sage: basis = sorted(B22.basis(), key=str) - sage: basis - [B22(0,0), B22(0,1), B22(0,2), B22(1,0), B22(1,1), B22(2,0)] - sage: [x.highest_weight() for x in basis] - [(0, 0), (1/2, 1/2), (1, 1), (1, 0), (3/2, 1/2), (2, 0)] - sage: B22.fusion_labels(['1','X','Y2','Y1','Xp','Z']) - sage: relabeled_basis = sorted(B22.basis(), key=str) - sage: relabeled_basis - [1, X, Xp, Y1, Y2, Z] - sage: [(x, x.highest_weight()) for x in relabeled_basis] - [(1, (0, 0)), - (X, (1/2, 1/2)), - (Xp, (3/2, 1/2)), - (Y1, (1, 0)), - (Y2, (1, 1)), - (Z, (2, 0))] - sage: X*Y1 - X + Xp - sage: Z*Z - 1 - - sage: C22 = FusionRing("C2", 2) - sage: sorted(C22.basis(), key=str) - [C22(0,0), C22(0,1), C22(0,2), C22(1,0), C22(1,1), C22(2,0)] - - REFERENCES: - - - [DFMS1996]_ Chapter 16 - - [Feingold2004]_ - - [Fuchs1994]_ - - [Walton1990]_ - """ - @staticmethod - def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots"): - """ - Normalize input to ensure a unique representation. - - TESTS:: - - sage: F1 = FusionRing('B3', 2) - sage: F2 = FusionRing(CartanType('B3'), QQ(2), ZZ) - sage: F3 = FusionRing(CartanType('B3'), int(2), style="coroots") - sage: F1 is F2 and F2 is F3 - True - - sage: A23 = FusionRing('A2', 3) - sage: TestSuite(A23).run() - - sage: B22 = FusionRing('B2', 2) - sage: TestSuite(B22).run() - - sage: C31 = FusionRing('C3', 1) - sage: TestSuite(C31).run() - - sage: D41 = FusionRing('D4', 1) - sage: TestSuite(D41).run() - """ - return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, - prefix=prefix, style=style, k=k) - - def some_elements(self): - """ - Return some elements of ``self``. - - EXAMPLES:: - - sage: D41 = FusionRing('D4', 1) - sage: D41.some_elements() - [D41(1,0,0,0), D41(0,0,1,0), D41(0,0,0,1)] - """ - return [self.monomial(x) for x in self.fundamental_weights() - if self.level(x) <= self._k] - - def fusion_labels(self, labels=None, key=str): - r""" - Set the labels of the basis. - - INPUT: - - - ``labels`` -- (default: ``None``) a list of strings - - ``key`` -- (default: ``str``) key to use to sort basis - - The length of the list ``labels`` must equal the - number of basis elements. These become the names of - the basis elements. If ``labels`` is ``None``, then - this resets the labels to the default. - - Note that the basis is stored as unsorted data, so to obtain - consistent results, it should be sorted when applying - labels. The argument ``key`` (default ``str``) specifies how - to sort the basis. If you call this with ``key=None``, then no - sorting is done. This may lead to random results, at least - with Python 3. - - EXAMPLES:: - - sage: A13 = FusionRing("A1", 3) - sage: A13.fusion_labels(['x0','x1','x2','x3']) - sage: fb = list(A13.basis()); fb - [x0, x1, x2, x3] - sage: Matrix([[x*y for y in A13.basis()] for x in A13.basis()]) - [ x0 x1 x2 x3] - [ x1 x0 + x2 x1 + x3 x2] - [ x2 x1 + x3 x0 + x2 x1] - [ x3 x2 x1 x0] - - We reset the labels to the default:: - - sage: A13.fusion_labels() - sage: fb - [A13(0), A13(1), A13(2), A13(3)] - """ - if labels is None: - self._fusion_labels = None - return - d = {} - if key: - fb = sorted(self.basis(), key=key) - else: - fb = list(self.basis()) - for j, b in enumerate(fb): - wt = b.highest_weight() - t = tuple([wt.inner_product(x) for x in self.simple_coroots()]) - d[t] = labels[j] - inject_variable(labels[j], b) - self._fusion_labels = d diff --git a/src/sage/combinat/rooted_tree.py b/src/sage/combinat/rooted_tree.py index d842394de99..f12c6c1dbc2 100644 --- a/src/sage/combinat/rooted_tree.py +++ b/src/sage/combinat/rooted_tree.py @@ -5,7 +5,6 @@ - Florent Hivert (2011): initial version """ -from six import add_metaclass from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.sets_cat import Sets @@ -56,8 +55,8 @@ def number_of_rooted_trees(n): for k in ZZ.range(1, n)) // (n - 1) -@add_metaclass(InheritComparisonClasscallMetaclass) -class RootedTree(AbstractClonableTree, NormalizedClonableList): +class RootedTree(AbstractClonableTree, NormalizedClonableList, + metaclass=InheritComparisonClasscallMetaclass): r""" The class for unordered rooted trees. diff --git a/src/sage/combinat/rsk.py b/src/sage/combinat/rsk.py index afc72c79be2..e6a457bcf72 100644 --- a/src/sage/combinat/rsk.py +++ b/src/sage/combinat/rsk.py @@ -2009,7 +2009,7 @@ def to_pairs(self, obj1=None, obj2=None, check=True): two `n`-tuples ``obj1`` `= [a_1, a_2, \ldots, a_n]` and ``obj2`` `= [b_1, b_2, \ldots, b_n]` forming a restricted super biword (i.e., entries with even and odd parity and no - repeatition of corresponding pairs with mixed parity entries) + repetition of corresponding pairs with mixed parity entries) return the array `[(a_1, b_1), (a_2, b_2), \ldots, (a_n, b_n)]`. INPUT: @@ -2053,7 +2053,7 @@ def to_pairs(self, obj1=None, obj2=None, check=True): raise ValueError("the two arrays must be the same length") mixed_parity = [] # Check it is a restricted superbiword: that is, - # the entries can have even or odd parity, but repeatition of + # the entries can have even or odd parity, but repetition of # the pairs of corresponding entries of obj1 # and obj2 with mixed-parity is not allowed for t, b in zip(obj1, obj2): diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index ef78e8b4e2e..fecf9055cd3 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -31,8 +31,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import, division -from six.moves import range -from six import add_metaclass from sage.sets.set import Set, Set_generic @@ -58,8 +56,8 @@ from sage.sets.disjoint_set import DisjointSet from sage.combinat.posets.hasse_diagram import HasseDiagram -@add_metaclass(InheritComparisonClasscallMetaclass) -class AbstractSetPartition(ClonableArray): +class AbstractSetPartition(ClonableArray, + metaclass=InheritComparisonClasscallMetaclass): r""" Methods of set partitions which are independent of the base set """ @@ -478,8 +476,8 @@ def max_block_size(self): return max(len(block) for block in self) -@add_metaclass(InheritComparisonClasscallMetaclass) -class SetPartition(AbstractSetPartition): +class SetPartition(AbstractSetPartition, + metaclass=InheritComparisonClasscallMetaclass): r""" A partition of a set. diff --git a/src/sage/combinat/set_partition_ordered.py b/src/sage/combinat/set_partition_ordered.py index 70408ad52f1..b47f027614b 100644 --- a/src/sage/combinat/set_partition_ordered.py +++ b/src/sage/combinat/set_partition_ordered.py @@ -10,7 +10,7 @@ - Travis Scrimshaw (2013-02-28): Removed ``CombinatorialClass`` and added entry point through :class:`OrderedSetPartition`. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Mike Hansen , # # Distributed under the terms of the GNU General Public License (GPL) @@ -23,16 +23,13 @@ # The full text of the GPL is available at: # # https://www.gnu.org/licenses/ -#***************************************************************************** -from six import add_metaclass - -from sage.arith.all import factorial +# **************************************************************************** +from sage.arith.all import factorial, multinomial from sage.sets.set import Set, Set_generic from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.sets.finite_enumerated_set import FiniteEnumeratedSet from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -from sage.misc.all import prod from sage.structure.parent import Parent from sage.structure.element import parent from sage.structure.unique_representation import UniqueRepresentation @@ -50,8 +47,8 @@ from sage.categories.cartesian_product import cartesian_product -@add_metaclass(InheritComparisonClasscallMetaclass) -class OrderedSetPartition(ClonableArray): +class OrderedSetPartition(ClonableArray, + metaclass=InheritComparisonClasscallMetaclass): r""" An ordered partition of a set. @@ -185,10 +182,11 @@ def __classcall_private__(cls, parts=None, from_word=None): if from_word: return OrderedSetPartitions().from_finite_word(Words()(from_word)) # if `parts` looks like a sequence of "letters" then treat it like a word. - if parts in Words() or (len(parts) > 0 and (parts[0] in ZZ or isinstance(parts[0], str))): + if parts in Words() or (parts and (parts[0] in ZZ or isinstance(parts[0], str))): return OrderedSetPartitions().from_finite_word(Words()(parts)) else: - P = OrderedSetPartitions( reduce(lambda x,y: x.union(y), map(Set, parts), Set([])) ) + P = OrderedSetPartitions(reduce(lambda x, y: x.union(y), + parts, frozenset())) return P.element_class(P, parts) def __init__(self, parent, s): @@ -201,10 +199,9 @@ def __init__(self, parent, s): sage: s = OS([[1, 3], [2, 4]]) sage: TestSuite(s).run() """ - self._base_set = reduce(lambda x,y: x.union(y), map(Set, s), Set([])) + self._base_set = reduce(lambda x, y: x.union(y), map(Set, s), Set([])) ClonableArray.__init__(self, parent, [Set(_) for _ in s]) - def _repr_(self): """ Return a string representation of ``self``. @@ -369,7 +366,7 @@ def reversed(self): [] """ par = parent(self) - return par(list(reversed(list(self)))) + return par(list(reversed(self))) def complement(self): r""" @@ -479,7 +476,7 @@ def is_finer(self, co2): """ co1 = self if co1.base_set() != co2.base_set(): - raise ValueError("ordered set partitions self (= %s) and co2 (= %s) must be of the same set"%(self, co2)) + raise ValueError("ordered set partitions self (= %s) and co2 (= %s) must be of the same set" % (self, co2)) i1 = 0 for j2 in co2: @@ -665,7 +662,6 @@ def strongly_finer(self): return FiniteEnumeratedSet([par(sum((list(P) for P in C), [])) for C in cartesian_product([[buo(X, comp) for comp in Compositions(len(X))] for X in self])]) - def is_strongly_finer(self, co2): r""" Return ``True`` if the ordered set partition ``self`` is strongly @@ -762,11 +758,12 @@ def strongly_fatter(self): [[{4}, {1, 5}, {3}], [{4}, {1}, {5}, {3}]] """ c = [sorted(X) for X in self] - l = len(c) - g = [-1] + [i for i in range(l-1) if c[i][-1] > c[i+1][0]] + [l-1] + l = len(c) - 1 + g = [-1] + [i for i in range(l) if c[i][-1] > c[i + 1][0]] + [l] # g lists the positions of the blocks that cannot be merged # with their right neighbors. - subcomps = [OrderedSetPartition(c[g[i] + 1 : g[i+1] + 1]) for i in range(len(g)-1)] + subcomps = [OrderedSetPartition(c[g[i] + 1: g[i + 1] + 1]) + for i in range(len(g) - 1)] # Now, self is the concatenation of the entries of subcomps. # We can fatten each of the ordered set partitions setcomps # arbitrarily, and then concatenate the results. @@ -903,7 +900,7 @@ def __classcall_private__(cls, s=None, c=None): if isinstance(c, (int, Integer)): return OrderedSetPartitions_sn(s, c) if c not in Compositions(len(s)): - raise ValueError("c must be a composition of %s"%len(s)) + raise ValueError("c must be a composition of %s" % len(s)) return OrderedSetPartitions_scomp(s, Composition(c)) def __init__(self, s): @@ -929,7 +926,7 @@ def _element_constructor_(self, s): [{1, 3}, {2, 4}] """ if isinstance(s, OrderedSetPartition): - raise ValueError("cannot convert %s into an element of %s"%(s, self)) + raise ValueError("cannot convert %s into an element of %s" % (s, self)) return self.element_class(self, list(s)) Element = OrderedSetPartition @@ -954,21 +951,21 @@ def __contains__(self, x): if not isinstance(x, (OrderedSetPartition, list, tuple)): return False - #The total number of elements in the list - #should be the same as the number is self._set + # The total number of elements in the list + # should be the same as the number is self._set if sum(map(len, x)) != len(self._set): return False - #Check to make sure each element of the list - #is a nonempty set + # Check to make sure each element of the list + # is a nonempty set u = Set([]) for s in x: if not s or not isinstance(s, (set, frozenset, Set_generic)): return False u = u.union(s) - #Make sure that the union of all the - #sets is the original set + # Make sure that the union of all the + # sets is the original set if u != Set(self._set): return False @@ -1057,6 +1054,7 @@ def __iter__(self): for z in OrderedSetPartitions(self._set, x): yield self.element_class(self, z) + class OrderedSetPartitions_sn(OrderedSetPartitions): def __init__(self, s, n): """ @@ -1130,7 +1128,7 @@ def __iter__(self): [{3}, {1, 2, 4}], [{4}, {1, 2, 3}]] """ - for x in Compositions(len(self._set),length=self.n): + for x in Compositions(len(self._set), length=self.n): for z in OrderedSetPartitions_scomp(self._set, x): yield self.element_class(self, z) @@ -1194,7 +1192,7 @@ def cardinality(self): sage: OrderedSetPartitions(5, [2,0,3]).cardinality() 10 """ - return factorial(len(self._set)) / prod([factorial(i) for i in self.c]) + return multinomial(self.c) def __iter__(self): """ @@ -1252,6 +1250,7 @@ def __iter__(self): yield self.element_class(self, [Set(res[dcomp[i]+1:dcomp[i+1]+1]) for i in range(l)]) + class OrderedSetPartitions_all(OrderedSetPartitions): r""" Ordered set partitions of `\{1, \ldots, n\}` for all @@ -1298,9 +1297,9 @@ def _element_constructor_(self, s): """ if isinstance(s, OrderedSetPartition): gset = s.parent()._set - if gset == frozenset(range(1,len(gset)+1)): + if gset == frozenset(range(1, len(gset) + 1)): return self.element_class(self, list(s)) - raise ValueError("cannot convert %s into an element of %s"%(s, self)) + raise ValueError("cannot convert %s into an element of %s" % (s, self)) return self.element_class(self, list(s)) def __contains__(self, x): @@ -1406,7 +1405,8 @@ def __setstate__(self, state): self.__class__ = OrderedSetPartitions_scomp n = state['_n'] k = state['_k'] - OrderedSetPartitions_scomp.__init__(self, range(state['_n']), (k,n-k)) + OrderedSetPartitions_scomp.__init__(self, range(state['_n']), (k, n-k)) + from sage.misc.persist import register_unpickle_override register_unpickle_override("sage.combinat.split_nk", "SplitNK_nk", SplitNK) diff --git a/src/sage/combinat/sf/character.py b/src/sage/combinat/sf/character.py index 548c67c6344..da8b5589b9d 100644 --- a/src/sage/combinat/sf/character.py +++ b/src/sage/combinat/sf/character.py @@ -35,8 +35,6 @@ from sage.functions.other import binomial from sage.rings.integer import Integer -import six - class generic_character(SFA_generic): def _my_key(self, la): @@ -303,8 +301,8 @@ def _b_bar_power_gamma(self, gamma): 3*p[1, 1] + p[1, 1, 1] - 3*p[3, 1] - 2*p[3, 1, 1] + p[3, 3, 1] """ - return self._p.prod( self._b_bar_power_k_r(Integer(k),Integer(r)) - for (k,r) in six.iteritems(gamma.to_exp_dict()) ) + return self._p.prod(self._b_bar_power_k_r(Integer(k), Integer(r)) + for k, r in gamma.to_exp_dict().items()) def _self_to_power_on_basis(self, lam): r""" @@ -531,8 +529,8 @@ def _b_power_gamma(self, gamma): p[] - p[1, 1] - p[3] + p[3, 1] """ - return self._p.prod( self._b_power_k_r(Integer(k),Integer(r)) - for (k,r) in six.iteritems(gamma.to_exp_dict()) ) + return self._p.prod(self._b_power_k_r(Integer(k), Integer(r)) + for k, r in gamma.to_exp_dict().items()) def _self_to_power_on_basis(self, lam): r""" diff --git a/src/sage/combinat/sf/classical.py b/src/sage/combinat/sf/classical.py index fc832ab9375..be0bbfe5aea 100644 --- a/src/sage/combinat/sf/classical.py +++ b/src/sage/combinat/sf/classical.py @@ -1,8 +1,7 @@ """ Classical symmetric functions """ -from __future__ import absolute_import -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Mike Hansen # 2012 Mike Zabrocki # @@ -15,9 +14,9 @@ # # 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 sage.rings.integer import Integer from sage.rings.integer_ring import IntegerRing from sage.rings.rational_field import RationalField @@ -31,9 +30,6 @@ from . import jack from . import orthotriang -import six - - ZZ = IntegerRing() QQ = RationalField() @@ -236,7 +232,7 @@ def _element_constructor_(self, x): raise TypeError("no coerce map from x's parent's base ring (= %s) to self's base ring (= %s)"%(PBR, self.base_ring())) z_elt = {} - for m, c in six.iteritems(x._monomial_coefficients): + for m, c in x._monomial_coefficients.items(): n = sum(m) P._m_cache(n) for part in P._self_to_m_cache[n][m]: diff --git a/src/sage/combinat/sf/elementary.py b/src/sage/combinat/sf/elementary.py index f65ea34ed69..678e065d96b 100644 --- a/src/sage/combinat/sf/elementary.py +++ b/src/sage/combinat/sf/elementary.py @@ -20,7 +20,9 @@ #***************************************************************************** from . import multiplicative, classical from sage.combinat.partition import Partition - +from sage.misc.all import prod +from sage.functions.other import factorial, binomial +from sage.rings.all import infinity ################################### # # @@ -305,6 +307,220 @@ def expand(self, n, alphabet='x'): condition = lambda part: max(part) > n return self._expand(condition, n, alphabet) + + def principal_specialization(self, n=infinity, q=None): + r""" + Return the principal specialization of a symmetric function. + + The *principal specialization* of order `n` at `q` + is the ring homomorphism `ps_{n,q}` from the ring of + symmetric functions to another commutative ring `R` + given by `x_i \mapsto q^{i-1}` for `i \in \{1,\dots,n\}` + and `x_i \mapsto 0` for `i > n`. + Here, `q` is a given element of `R`, and we assume that + the variables of our symmetric functions are + `x_1, x_2, x_3, \ldots`. + (To be more precise, `ps_{n,q}` is a `K`-algebra + homomorphism, where `K` is the base ring.) + See Section 7.8 of [EnumComb2]_. + + The *stable principal specialization* at `q` is the ring + homomorphism `ps_q` from the ring of symmetric functions + to another commutative ring `R` given by + `x_i \mapsto q^{i-1}` for all `i`. + This is well-defined only if the resulting infinite sums + converge; thus, in particular, setting `q = 1` in the + stable principal specialization is an invalid operation. + + INPUT: + + - ``n`` (default: ``infinity``) -- a nonnegative integer or + ``infinity``, specifying whether to compute the principal + specialization of order ``n`` or the stable principal + specialization. + + - ``q`` (default: ``None``) -- the value to use for `q`; the + default is to create a ring of polynomials in ``q`` + (or a field of rational functions in ``q``) over the + given coefficient ring. + + We use the formulas from Proposition 7.8.3 of [EnumComb2]_ + (using Gaussian binomial coefficients `\binom{u}{v}_q`): + + .. MATH:: + + ps_{n,q}(e_\lambda) = \prod_i q^{\binom{\lambda_i}{2}} \binom{n}{\lambda_i}_q, + + ps_{n,1}(e_\lambda) = \prod_i \binom{n}{\lambda_i}, + + ps_q(e_\lambda) = \prod_i q^{\binom{\lambda_i}{2}} / \prod_{j=1}^{\lambda_i} (1-q^j). + + EXAMPLES:: + + sage: e = SymmetricFunctions(QQ).e() + sage: x = e[3,1] + sage: x.principal_specialization(3) + q^5 + q^4 + q^3 + sage: x = 5*e[1,1,1] + 3*e[2,1] + 1 + sage: x.principal_specialization(3) + 5*q^6 + 18*q^5 + 36*q^4 + 44*q^3 + 36*q^2 + 18*q + 6 + + By default, we return a rational functions in `q`. Sometimes + it is better to obtain an element of the symbolic ring:: + + sage: x.principal_specialization(q=var("q")) + -3*q/((q^2 - 1)*(q - 1)^2) - 5/(q - 1)^3 + 1 + + TESTS:: + + sage: e.zero().principal_specialization(3) + 0 + + """ + from sage.combinat.q_analogues import q_binomial + + def get_variable(ring, name): + try: + ring(name) + except TypeError: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + return PolynomialRing(ring, name).gen() + else: + raise ValueError("the variable %s is in the base ring, pass it explicitly" % name) + + if q is None: + q = get_variable(self.base_ring(), "q") + + if q == 1: + if n == infinity: + raise ValueError("the stable principal specialization at q=1 is not defined") + f = lambda partition: prod(binomial(n, part) for part in partition) + elif n == infinity: + f = lambda partition: prod(q**binomial(part, 2)/prod((1-q**i) + for i in range(1,part+1)) + for part in partition) + else: + f = lambda partition: prod(q**binomial(part, 2)*q_binomial(n, part, q=q) + for part in partition) + + return self.parent()._apply_module_morphism(self, f, q.parent()) + + + def exponential_specialization(self, t=None, q=1): + r""" + Return the exponential specialization of a + symmetric function (when `q = 1`), or the + `q`-exponential specialization (when `q \neq 1`). + + The *exponential specialization* `ex` at `t` is a + `K`-algebra homomorphism from the `K`-algebra of + symmetric functions to another `K`-algebra `R`. + It is defined whenever the base ring `K` is a + `\QQ`-algebra and `t` is an element of `R`. + The easiest way to define it is by specifying its + values on the powersum symmetric functions to be + `p_1 = t` and `p_n = 0` for `n > 1`. + Equivalently, on the homogeneous functions it is + given by `ex(h_n) = t^n / n!`; see Proposition 7.8.4 of + [EnumComb2]_. + + By analogy, the `q`-exponential specialization is a + `K`-algebra homomorphism from the `K`-algebra of + symmetric functions to another `K`-algebra `R` that + depends on two elements `t` and `q` of `R` for which + the elements `1 - q^i` for all positive integers `i` + are invertible. + It can be defined by specifying its values on the + complete homogeneous symmetric functions to be + + .. MATH:: + + ex_q(h_n) = t^n / [n]_q!, + + where `[n]_q!` is the `q`-factorial. Equivalently, for + `q \neq 1` and a homogeneous symmetric function `f` of + degree `n`, we have + + .. MATH:: + + ex_q(f) = (1-q)^n t^n ps_q(f), + + where `ps_q(f)` is the stable principal specialization of `f` + (see :meth:`principal_specialization`). + (See (7.29) in [EnumComb2]_.) + + The limit of `ex_q` as `q \to 1` is `ex`. + + INPUT: + + - ``t`` (default: ``None``) -- the value to use for `t`; + the default is to create a ring of polynomials in ``t``. + + - ``q`` (default: `1`) -- the value to use for `q`. If + ``q`` is ``None``, then a ring (or fraction field) of + polynomials in ``q`` is created. + + EXAMPLES:: + + sage: e = SymmetricFunctions(QQ).e() + sage: x = e[3,2] + sage: x.exponential_specialization() + 1/12*t^5 + sage: x = 5*e[2] + 3*e[1] + 1 + sage: x.exponential_specialization(t=var("t"), q=var("q")) + 5*q*t^2/(q + 1) + 3*t + 1 + + TESTS:: + + sage: e.zero().exponential_specialization() + 0 + + """ + from sage.combinat.q_analogues import q_factorial + + def get_variable(ring, name): + try: + ring(name) + except TypeError: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + return PolynomialRing(ring, name).gen() + else: + raise ValueError("the variable %s is in the base ring, pass it explicitly" % name) + + if q == 1: + if t is None: + t = get_variable(self.base_ring(), 't') + + def f(partition): + n = 0 + m = 1 + for part in partition: + n += part + m *= factorial(part) + return t**n/m + + return self.parent()._apply_module_morphism(self, f, t.parent()) + + if q is None and t is None: + q = get_variable(self.base_ring(), 'q') + t = get_variable(q.parent(), 't') + elif q is None: + q = get_variable(t.parent(), 'q') + elif t is None: + t = get_variable(q.parent(), 't') + + def f(partition): + n = 0 + m = 1 + for part in partition: + n += part + m *= q**binomial(part, 2)/q_factorial(part, q=q) + + return t**n * m + + return self.parent()._apply_module_morphism(self, f, t.parent()) + + # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.combinat.sf.elementary', 'SymmetricFunctionAlgebraElement_elementary', SymmetricFunctionAlgebra_elementary.Element) diff --git a/src/sage/combinat/sf/homogeneous.py b/src/sage/combinat/sf/homogeneous.py index 3e5462d1742..497e30bf709 100644 --- a/src/sage/combinat/sf/homogeneous.py +++ b/src/sage/combinat/sf/homogeneous.py @@ -28,6 +28,9 @@ #################################### from . import multiplicative, classical from sage.combinat.partition import Partition +from sage.rings.all import infinity +from sage.misc.all import prod +from sage.functions.other import factorial, binomial class SymmetricFunctionAlgebra_homogeneous(multiplicative.SymmetricFunctionAlgebra_multiplicative): def __init__(self, Sym): @@ -223,6 +226,219 @@ def expand(self, n, alphabet='x'): condition = lambda part: False return self._expand(condition, n, alphabet) + def principal_specialization(self, n=infinity, q=None): + r""" + Return the principal specialization of a symmetric function. + + The *principal specialization* of order `n` at `q` + is the ring homomorphism `ps_{n,q}` from the ring of + symmetric functions to another commutative ring `R` + given by `x_i \mapsto q^{i-1}` for `i \in \{1,\dots,n\}` + and `x_i \mapsto 0` for `i > n`. + Here, `q` is a given element of `R`, and we assume that + the variables of our symmetric functions are + `x_1, x_2, x_3, \ldots`. + (To be more precise, `ps_{n,q}` is a `K`-algebra + homomorphism, where `K` is the base ring.) + See Section 7.8 of [EnumComb2]_. + + The *stable principal specialization* at `q` is the ring + homomorphism `ps_q` from the ring of symmetric functions + to another commutative ring `R` given by + `x_i \mapsto q^{i-1}` for all `i`. + This is well-defined only if the resulting infinite sums + converge; thus, in particular, setting `q = 1` in the + stable principal specialization is an invalid operation. + + INPUT: + + - ``n`` (default: ``infinity``) -- a nonnegative integer or + ``infinity``, specifying whether to compute the principal + specialization of order ``n`` or the stable principal + specialization. + + - ``q`` (default: ``None``) -- the value to use for `q`; the + default is to create a ring of polynomials in ``q`` + (or a field of rational functions in ``q``) over the + given coefficient ring. + + We use the formulas from Proposition 7.8.3 of [EnumComb2]_ + (using Gaussian binomial coefficients `\binom{u}{v}_q`): + + .. MATH:: + + ps_{n,q}(h_\lambda) = \prod_i \binom{n+\lambda_i-1}{\lambda_i}_q, + + ps_{n,1}(h_\lambda) = \prod_i \binom{n+\lambda_i-1}{\lambda_i}, + + ps_q(h_\lambda) = 1 / \prod_i \prod_{j=1}^{\lambda_i} (1-q^j). + + EXAMPLES:: + + sage: h = SymmetricFunctions(QQ).h() + sage: x = h[2,1] + sage: x.principal_specialization(3) + q^6 + 2*q^5 + 4*q^4 + 4*q^3 + 4*q^2 + 2*q + 1 + sage: x = 3*h[2] + 2*h[1] + 1 + sage: x.principal_specialization(3, q=var("q")) + 2*(q^3 - 1)/(q - 1) + 3*(q^4 - 1)*(q^3 - 1)/((q^2 - 1)*(q - 1)) + 1 + + TESTS:: + + sage: x = h.zero() + sage: s = x.principal_specialization(3); s + 0 + + """ + from sage.combinat.q_analogues import q_binomial + + def get_variable(ring, name): + try: + ring(name) + except TypeError: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + return PolynomialRing(ring, name).gen() + else: + raise ValueError("the variable %s is in the base ring, pass it explicitly" % name) + + if q is None: + q = get_variable(self.base_ring(), 'q') + + if q == 1: + if n == infinity: + raise ValueError("the stable principal specialization at q=1 is not defined") + f = lambda partition: prod(binomial(n+part-1, part) for part in partition) + elif n == infinity: + f = lambda partition: prod(1/prod((1-q**i) for i in range(1, part+1)) for part in partition) + else: + f = lambda partition: prod(q_binomial(n+part-1, part, q=q) for part in partition) + + return self.parent()._apply_module_morphism(self, f, q.parent()) + + + def exponential_specialization(self, t=None, q=1): + r""" + Return the exponential specialization of a + symmetric function (when `q = 1`), or the + `q`-exponential specialization (when `q \neq 1`). + + The *exponential specialization* `ex` at `t` is a + `K`-algebra homomorphism from the `K`-algebra of + symmetric functions to another `K`-algebra `R`. + It is defined whenever the base ring `K` is a + `\QQ`-algebra and `t` is an element of `R`. + The easiest way to define it is by specifying its + values on the powersum symmetric functions to be + `p_1 = t` and `p_n = 0` for `n > 1`. + Equivalently, on the homogeneous functions it is + given by `ex(h_n) = t^n / n!`; see Proposition 7.8.4 of + [EnumComb2]_. + + By analogy, the `q`-exponential specialization is a + `K`-algebra homomorphism from the `K`-algebra of + symmetric functions to another `K`-algebra `R` that + depends on two elements `t` and `q` of `R` for which + the elements `1 - q^i` for all positive integers `i` + are invertible. + It can be defined by specifying its values on the + complete homogeneous symmetric functions to be + + .. MATH:: + + ex_q(h_n) = t^n / [n]_q!, + + where `[n]_q!` is the `q`-factorial. Equivalently, for + `q \neq 1` and a homogeneous symmetric function `f` of + degree `n`, we have + + .. MATH:: + + ex_q(f) = (1-q)^n t^n ps_q(f), + + where `ps_q(f)` is the stable principal specialization of `f` + (see :meth:`principal_specialization`). + (See (7.29) in [EnumComb2]_.) + + The limit of `ex_q` as `q \to 1` is `ex`. + + INPUT: + + - ``t`` (default: ``None``) -- the value to use for `t`; + the default is to create a ring of polynomials in ``t``. + + - ``q`` (default: `1`) -- the value to use for `q`. If + ``q`` is ``None``, then a ring (or fraction field) of + polynomials in ``q`` is created. + + EXAMPLES:: + + sage: h = SymmetricFunctions(QQ).h() + sage: x = h[5,3] + sage: x.exponential_specialization() + 1/720*t^8 + sage: factorial(5)*factorial(3) + 720 + + sage: x = 5*h[1,1,1] + 3*h[2,1] + 1 + sage: x.exponential_specialization() + 13/2*t^3 + 1 + + We also support the `q`-exponential_specialization:: + + sage: factor(h[3].exponential_specialization(q=var("q"), t=var("t"))) + t^3/((q^2 + q + 1)*(q + 1)) + + TESTS:: + + sage: x = h.zero() + sage: s = x.exponential_specialization(); s + 0 + + """ + from sage.combinat.q_analogues import q_factorial + + def get_variable(ring, name): + try: + ring(name) + except TypeError: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + return PolynomialRing(ring, name).gen() + else: + raise ValueError("the variable %s is in the base ring, pass it explicitly" % name) + + if q == 1: + if t is None: + t = get_variable(self.base_ring(), 't') + + def f(partition): + n = 0 + m = 1 + for part in partition: + n += part + m *= factorial(part) + return t**n/m + + return self.parent()._apply_module_morphism(self, f, t.parent()) + + if q is None and t is None: + q = get_variable(self.base_ring(), 'q') + t = get_variable(q.parent(), 't') + elif q is None: + q = get_variable(t.parent(), 'q') + elif t is None: + t = get_variable(q.parent(), 't') + + def f(partition): + n = 0 + m = 1 + for part in partition: + n += part + m *= q_factorial(part, q=q) + return t**n/m + + return self.parent()._apply_module_morphism(self, f, t.parent()) + + # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.combinat.sf.homogeneous', 'SymmetricFunctionAlgebraElement_homogeneous', SymmetricFunctionAlgebra_homogeneous.Element) diff --git a/src/sage/combinat/sf/k_dual.py b/src/sage/combinat/sf/k_dual.py index 160eeadfbcc..65f27bf7be0 100644 --- a/src/sage/combinat/sf/k_dual.py +++ b/src/sage/combinat/sf/k_dual.py @@ -28,7 +28,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six import iteritems from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -1125,7 +1124,7 @@ def _m_to_kHLP_on_basis(self, la): else: HLP = self._kBoundedRing._quotient_basis m = self._kBoundedRing._sym.m() - elt = dict(x for x in iteritems(dict(HLP(m(la)))) + elt = dict(x for x in dict(HLP(m(la))).items() if x[0] in self._kbounded_partitions) return self._from_dict(elt) diff --git a/src/sage/combinat/sf/macdonald.py b/src/sage/combinat/sf/macdonald.py index 0d32a8dfbf0..e664e21b5a5 100644 --- a/src/sage/combinat/sf/macdonald.py +++ b/src/sage/combinat/sf/macdonald.py @@ -572,10 +572,11 @@ def c1(part, q, t): sage: c1(Partition([1,1]),q,t) q^2*t - q*t - q + 1 """ - res = q.parent().one() - for i in range(part.size()): - res *= 1-q**(sum(part.arm_lengths(),[])[i]+1)*t**(sum(part.leg_lengths(),[])[i]) - return res + R = q.parent() + arms = part.arm_lengths(flat=True) + legs = part.leg_lengths(flat=True) + return R.prod(1 - q**(a + 1) * t**l for a, l in zip(arms, legs)) + def c2(part, q, t): r""" @@ -603,10 +604,11 @@ def c2(part, q, t): sage: c2(Partition([2,1]),q,t) -q*t^4 + 2*q*t^3 - q*t^2 + t^2 - 2*t + 1 """ - res = q.parent().one() - for i in range(part.size()): - res *= 1-q**(sum(part.arm_lengths(),[])[i])*t**(sum(part.leg_lengths(),[])[i]+1) - return res + R = q.parent() + arms = part.arm_lengths(flat=True) + legs = part.leg_lengths(flat=True) + return R.prod(1 - q**a * t**(l + 1) for a, l in zip(arms, legs)) + @cached_function def cmunu1(mu, nu): diff --git a/src/sage/combinat/sf/monomial.py b/src/sage/combinat/sf/monomial.py index 5c03f5d2a71..60c05831742 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,17 @@ # # 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.rings.infinity import infinity +from sage.combinat.partition import Partition, _Partitions +from sage.functions.other import factorial, binomial +from sage.arith.misc import multinomial class SymmetricFunctionAlgebra_monomial(classical.SymmetricFunctionAlgebra_classical): def __init__(self, Sym): @@ -46,7 +49,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 +107,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 +162,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 +175,12 @@ 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: + if check and not f.is_symmetric(): raise ValueError("%s is not a symmetric polynomial"%f) + out = self._from_dict({_Partitions.element_class(_Partitions, list(e)): c + for (e,c) in f.dict().items() + if all(e[i+1] <= e[i] for i in range(len(e)-1))}, + remove_zeros=False) return out def from_polynomial_exp(self, p): @@ -225,7 +230,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 +304,192 @@ 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) + def principal_specialization(self, n=infinity, q=None): + r""" + Return the principal specialization of a symmetric function. + + The *principal specialization* of order `n` at `q` + is the ring homomorphism `ps_{n,q}` from the ring of + symmetric functions to another commutative ring `R` + given by `x_i \mapsto q^{i-1}` for `i \in \{1,\dots,n\}` + and `x_i \mapsto 0` for `i > n`. + Here, `q` is a given element of `R`, and we assume that + the variables of our symmetric functions are + `x_1, x_2, x_3, \ldots`. + (To be more precise, `ps_{n,q}` is a `K`-algebra + homomorphism, where `K` is the base ring.) + See Section 7.8 of [EnumComb2]_. + + The *stable principal specialization* at `q` is the ring + homomorphism `ps_q` from the ring of symmetric functions + to another commutative ring `R` given by + `x_i \mapsto q^{i-1}` for all `i`. + This is well-defined only if the resulting infinite sums + converge; thus, in particular, setting `q = 1` in the + stable principal specialization is an invalid operation. + + INPUT: + + - ``n`` (default: ``infinity``) -- a nonnegative integer or + ``infinity``, specifying whether to compute the principal + specialization of order ``n`` or the stable principal + specialization. + + - ``q`` (default: ``None``) -- the value to use for `q`; the + default is to create a ring of polynomials in ``q`` + (or a field of rational functions in ``q``) over the + given coefficient ring. + + For ``q=1`` and finite ``n`` we use the formula from + Proposition 7.8.3 of [EnumComb2]_: + + .. MATH:: + + ps_{n,1}(m_\lambda) = \binom{n}{\ell(\lambda)} + \binom{\ell(\lambda)}{m_1(\lambda), m_2(\lambda),\dots}, + + where `\ell(\lambda)` denotes the length of `\lambda`. + + In all other cases, we convert to complete homogeneous + symmetric functions. + + EXAMPLES:: + + sage: m = SymmetricFunctions(QQ).m() + sage: x = m[3,1] + sage: x.principal_specialization(3) + q^7 + q^6 + q^5 + q^3 + q^2 + q + + sage: x = 5*m[2] + 3*m[1] + 1 + sage: x.principal_specialization(3, q=var("q")) + -10*(q^3 - 1)*q/(q - 1) + 5*(q^3 - 1)^2/(q - 1)^2 + 3*(q^3 - 1)/(q - 1) + 1 + + TESTS:: + + sage: m.zero().principal_specialization(3) + 0 + + """ + if q == 1: + if n == infinity: + raise ValueError("the stable principal specialization at q=1 is not defined") + f = lambda partition: binomial(n, len(partition))*multinomial(partition.to_exp()) + return self.parent()._apply_module_morphism(self, f, q.parent()) + + # heuristically, it seems fastest to fall back to the + # elementary basis - using the powersum basis would + # introduce singularities, because it is not a Z-basis + return self.parent().realization_of().elementary()(self).principal_specialization(n=n, q=q) + + def exponential_specialization(self, t=None, q=1): + r""" + Return the exponential specialization of a + symmetric function (when `q = 1`), or the + `q`-exponential specialization (when `q \neq 1`). + + The *exponential specialization* `ex` at `t` is a + `K`-algebra homomorphism from the `K`-algebra of + symmetric functions to another `K`-algebra `R`. + It is defined whenever the base ring `K` is a + `\QQ`-algebra and `t` is an element of `R`. + The easiest way to define it is by specifying its + values on the powersum symmetric functions to be + `p_1 = t` and `p_n = 0` for `n > 1`. + Equivalently, on the homogeneous functions it is + given by `ex(h_n) = t^n / n!`; see Proposition 7.8.4 of + [EnumComb2]_. + + By analogy, the `q`-exponential specialization is a + `K`-algebra homomorphism from the `K`-algebra of + symmetric functions to another `K`-algebra `R` that + depends on two elements `t` and `q` of `R` for which + the elements `1 - q^i` for all positive integers `i` + are invertible. + It can be defined by specifying its values on the + complete homogeneous symmetric functions to be + + .. MATH:: + + ex_q(h_n) = t^n / [n]_q!, + + where `[n]_q!` is the `q`-factorial. Equivalently, for + `q \neq 1` and a homogeneous symmetric function `f` of + degree `n`, we have + + .. MATH:: + + ex_q(f) = (1-q)^n t^n ps_q(f), + + where `ps_q(f)` is the stable principal specialization of `f` + (see :meth:`principal_specialization`). + (See (7.29) in [EnumComb2]_.) + + The limit of `ex_q` as `q \to 1` is `ex`. + + INPUT: + + - ``t`` (default: ``None``) -- the value to use for `t`; + the default is to create a ring of polynomials in ``t``. + + - ``q`` (default: `1`) -- the value to use for `q`. If + ``q`` is ``None``, then a ring (or fraction field) of + polynomials in ``q`` is created. + + EXAMPLES:: + + sage: m = SymmetricFunctions(QQ).m() + sage: (m[3]+m[2,1]+m[1,1,1]).exponential_specialization() + 1/6*t^3 + + sage: x = 5*m[1,1,1] + 3*m[2,1] + 1 + sage: x.exponential_specialization() + 5/6*t^3 + 1 + + We also support the `q`-exponential_specialization:: + + sage: factor(m[3].exponential_specialization(q=var("q"), t=var("t"))) + (q - 1)^2*t^3/(q^2 + q + 1) + + TESTS:: + + sage: m.zero().exponential_specialization() + 0 + + """ + def get_variable(ring, name): + try: + ring(name) + except TypeError: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + return PolynomialRing(ring, name).gen() + else: + raise ValueError("the variable %s is in the base ring, pass it explicitly" % name) + + if q == 1: + if t is None: + t = get_variable(self.base_ring(), 't') + def f(partition): + n = 0 + for part in partition: + if part != 1: + return 0 + n += 1 + return t**n/factorial(n) + + return self.parent()._apply_module_morphism(self, f, t.parent()) + + # heuristically, it seems fastest to fall back to the + # elementary basis - using the powersum basis would + # introduce singularities, because it is not a Z-basis + return self.parent().realization_of().elementary()(self).exponential_specialization(t=t, q=q) + # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override -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/combinat/sf/orthotriang.py b/src/sage/combinat/sf/orthotriang.py index f3e7ae0377a..e4fd40fe99f 100644 --- a/src/sage/combinat/sf/orthotriang.py +++ b/src/sage/combinat/sf/orthotriang.py @@ -23,8 +23,7 @@ class SymmetricFunctionAlgebra_orthotriang to obtain the Schur sage: s2([2,1])^2 s[2, 2, 1, 1] + s[2, 2, 2] + s[3, 1, 1, 1] + 2*s[3, 2, 1] + s[3, 3] + s[4, 1, 1] + s[4, 2] """ -from __future__ import absolute_import -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Mike Hansen # 2012 Mike Zabrocki # @@ -37,13 +36,15 @@ class SymmetricFunctionAlgebra_orthotriang to obtain the Schur # # 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 sfa from sage.categories.morphism import SetMorphism from sage.categories.homset import Hom + class SymmetricFunctionAlgebra_orthotriang(sfa.SymmetricFunctionAlgebra_generic): class Element(sfa.SymmetricFunctionAlgebra_generic.Element): diff --git a/src/sage/combinat/sf/powersum.py b/src/sage/combinat/sf/powersum.py index 80d820ace7a..63cd8165d76 100644 --- a/src/sage/combinat/sf/powersum.py +++ b/src/sage/combinat/sf/powersum.py @@ -21,6 +21,9 @@ from . import sfa, multiplicative, classical from sage.combinat.partition import Partition from sage.arith.all import divisors +from sage.rings.all import infinity +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.misc.all import prod class SymmetricFunctionAlgebra_power(multiplicative.SymmetricFunctionAlgebra_multiplicative): def __init__(self, Sym): @@ -704,6 +707,234 @@ def eval_at_permutation_roots(self, rho): p.eval_at_permutation_roots_on_generators(k, rho) for k in lam) return p._apply_module_morphism(self, on_basis, R) + def principal_specialization(self, n=infinity, q=None): + r""" + Return the principal specialization of a symmetric function. + + The *principal specialization* of order `n` at `q` + is the ring homomorphism `ps_{n,q}` from the ring of + symmetric functions to another commutative ring `R` + given by `x_i \mapsto q^{i-1}` for `i \in \{1,\dots,n\}` + and `x_i \mapsto 0` for `i > n`. + Here, `q` is a given element of `R`, and we assume that + the variables of our symmetric functions are + `x_1, x_2, x_3, \ldots`. + (To be more precise, `ps_{n,q}` is a `K`-algebra + homomorphism, where `K` is the base ring.) + See Section 7.8 of [EnumComb2]_. + + The *stable principal specialization* at `q` is the ring + homomorphism `ps_q` from the ring of symmetric functions + to another commutative ring `R` given by + `x_i \mapsto q^{i-1}` for all `i`. + This is well-defined only if the resulting infinite sums + converge; thus, in particular, setting `q = 1` in the + stable principal specialization is an invalid operation. + + INPUT: + + - ``n`` (default: ``infinity``) -- a nonnegative integer or + ``infinity``, specifying whether to compute the principal + specialization of order ``n`` or the stable principal + specialization. + + - ``q`` (default: ``None``) -- the value to use for `q`; the + default is to create a ring of polynomials in ``q`` + (or a field of rational functions in ``q``) over the + given coefficient ring. + + We use the formulas from Proposition 7.8.3 of [EnumComb2]_: + + .. MATH:: + + ps_{n,q}(p_\lambda) = \prod_i (1-q^{n\lambda_i}) / (1-q^{\lambda_i}), + + ps_{n,1}(p_\lambda) = n^{\ell(\lambda)}, + + ps_q(p_\lambda) = 1 / \prod_i (1-q^{\lambda_i}), + + where `\ell(\lambda)` denotes the length of `\lambda`, + and where the products range from `i=1` to `i=\ell(\lambda)`. + + EXAMPLES:: + + sage: p = SymmetricFunctions(QQ).p() + sage: x = p[8,7,3,1] + sage: x.principal_specialization(3, q=var("q")) + (q^24 - 1)*(q^21 - 1)*(q^9 - 1)/((q^8 - 1)*(q^7 - 1)*(q - 1)) + + sage: x = 5*p[1,1,1] + 3*p[2,1] + 1 + sage: x.principal_specialization(3, q=var("q")) + 5*(q^3 - 1)^3/(q - 1)^3 + 3*(q^6 - 1)*(q^3 - 1)/((q^2 - 1)*(q - 1)) + 1 + + By default, we return a rational function in `q`:: + + sage: x.principal_specialization(3) + 8*q^6 + 18*q^5 + 36*q^4 + 38*q^3 + 36*q^2 + 18*q + 9 + + If ``n`` is not given we return the stable principal specialization:: + + sage: x.principal_specialization(q=var("q")) + 3/((q^2 - 1)*(q - 1)) - 5/(q - 1)^3 + 1 + + TESTS:: + + sage: p.zero().principal_specialization(3) + 0 + + """ + def get_variable(ring, name): + try: + ring(name) + except TypeError: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + return PolynomialRing(ring, name).gen() + else: + raise ValueError("the variable %s is in the base ring, pass it explicitly" % name) + + if q is None: + q = get_variable(self.base_ring(), 'q') + + if q == 1: + if n == infinity: + raise ValueError("the stable principal specialization at q=1 is not defined") + f = lambda partition: n**len(partition) + elif n == infinity: + f = lambda partition: prod(1/(1-q**part) for part in partition) + else: + from sage.rings.integer_ring import ZZ + ZZq = PolynomialRing(ZZ, "q") + q_lim = ZZq.gen() + def f(partition): + denom = prod((1-q**part) for part in partition) + try: + ~denom + rational = prod((1-q**(n*part)) for part in partition)/denom + return q.parent()(rational) + except (ZeroDivisionError, NotImplementedError, TypeError): + # If denom is not invertible, we need to do the + # computation with universal coefficients instead: + quotient = ZZq(prod((1-q_lim**(n*part))/(1-q_lim**part) for part in partition)) + return quotient.subs({q_lim: q}) + + return self.parent()._apply_module_morphism(self, f, q.parent()) + + def exponential_specialization(self, t=None, q=1): + r""" + Return the exponential specialization of a + symmetric function (when `q = 1`), or the + `q`-exponential specialization (when `q \neq 1`). + + The *exponential specialization* `ex` at `t` is a + `K`-algebra homomorphism from the `K`-algebra of + symmetric functions to another `K`-algebra `R`. + It is defined whenever the base ring `K` is a + `\QQ`-algebra and `t` is an element of `R`. + The easiest way to define it is by specifying its + values on the powersum symmetric functions to be + `p_1 = t` and `p_n = 0` for `n > 1`. + Equivalently, on the homogeneous functions it is + given by `ex(h_n) = t^n / n!`; see Proposition 7.8.4 of + [EnumComb2]_. + + By analogy, the `q`-exponential specialization is a + `K`-algebra homomorphism from the `K`-algebra of + symmetric functions to another `K`-algebra `R` that + depends on two elements `t` and `q` of `R` for which + the elements `1 - q^i` for all positive integers `i` + are invertible. + It can be defined by specifying its values on the + complete homogeneous symmetric functions to be + + .. MATH:: + + ex_q(h_n) = t^n / [n]_q!, + + where `[n]_q!` is the `q`-factorial. Equivalently, for + `q \neq 1` and a homogeneous symmetric function `f` of + degree `n`, we have + + .. MATH:: + + ex_q(f) = (1-q)^n t^n ps_q(f), + + where `ps_q(f)` is the stable principal specialization of `f` + (see :meth:`principal_specialization`). + (See (7.29) in [EnumComb2]_.) + + The limit of `ex_q` as `q \to 1` is `ex`. + + INPUT: + + - ``t`` (default: ``None``) -- the value to use for `t`; + the default is to create a ring of polynomials in ``t``. + + - ``q`` (default: `1`) -- the value to use for `q`. If + ``q`` is ``None``, then a ring (or fraction field) of + polynomials in ``q`` is created. + + EXAMPLES:: + + sage: p = SymmetricFunctions(QQ).p() + sage: x = p[8,7,3,1] + sage: x.exponential_specialization() + 0 + sage: x = p[3] + 5*p[1,1] + 2*p[1] + 1 + sage: x.exponential_specialization(t=var("t")) + 5*t^2 + 2*t + 1 + + We also support the `q`-exponential_specialization:: + + sage: factor(p[3].exponential_specialization(q=var("q"), t=var("t"))) + (q - 1)^2*t^3/(q^2 + q + 1) + + TESTS:: + + sage: p.zero().exponential_specialization() + 0 + + """ + def get_variable(ring, name): + try: + ring(name) + except TypeError: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + return PolynomialRing(ring, name).gen() + else: + raise ValueError("the variable %s is in the base ring, pass it explicitly" % name) + + if q == 1: + if t is None: + t = get_variable(self.base_ring(), 't') + + def f(partition): + n = 0 + for part in partition: + if part != 1: + return 0 + n += 1 + return t**n + + return self.parent()._apply_module_morphism(self, f, t.parent()) + + if q is None and t is None: + q = get_variable(self.base_ring(), 'q') + t = get_variable(q.parent(), 't') + elif q is None: + q = get_variable(t.parent(), 'q') + elif t is None: + t = get_variable(q.parent(), 't') + + def f(partition): + n = 0 + m = 1 + for part in partition: + n += part + m *= 1-q**part + return (1-q)**n * t**n / m + + return self.parent()._apply_module_morphism(self, f, t.parent()) + # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.combinat.sf.powersum', 'SymmetricFunctionAlgebraElement_power', SymmetricFunctionAlgebra_power.Element) diff --git a/src/sage/combinat/sf/schur.py b/src/sage/combinat/sf/schur.py index d193157aa3e..eb079c6f0ff 100644 --- a/src/sage/combinat/sf/schur.py +++ b/src/sage/combinat/sf/schur.py @@ -17,11 +17,14 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import -from six.moves import zip from . import classical import sage.libs.lrcalc.lrcalc as lrcalc - +from sage.misc.all import prod +from sage.rings.infinity import infinity +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.functions.other import factorial +from sage.combinat.tableau import StandardTableaux class SymmetricFunctionAlgebra_schur(classical.SymmetricFunctionAlgebra_classical): def __init__(self, Sym): @@ -571,6 +574,260 @@ def expand(self, n, alphabet='x'): condition = lambda part: len(part) > n return self._expand(condition, n, alphabet) + def principal_specialization(self, n=infinity, q=None): + r""" + Return the principal specialization of a symmetric function. + + The *principal specialization* of order `n` at `q` + is the ring homomorphism `ps_{n,q}` from the ring of + symmetric functions to another commutative ring `R` + given by `x_i \mapsto q^{i-1}` for `i \in \{1,\dots,n\}` + and `x_i \mapsto 0` for `i > n`. + Here, `q` is a given element of `R`, and we assume that + the variables of our symmetric functions are + `x_1, x_2, x_3, \ldots`. + (To be more precise, `ps_{n,q}` is a `K`-algebra + homomorphism, where `K` is the base ring.) + See Section 7.8 of [EnumComb2]_. + + The *stable principal specialization* at `q` is the ring + homomorphism `ps_q` from the ring of symmetric functions + to another commutative ring `R` given by + `x_i \mapsto q^{i-1}` for all `i`. + This is well-defined only if the resulting infinite sums + converge; thus, in particular, setting `q = 1` in the + stable principal specialization is an invalid operation. + + INPUT: + + - ``n`` (default: ``infinity``) -- a nonnegative integer or + ``infinity``, specifying whether to compute the principal + specialization of order ``n`` or the stable principal + specialization. + + - ``q`` (default: ``None``) -- the value to use for `q`; the + default is to create a ring of polynomials in ``q`` + (or a field of rational functions in ``q``) over the + given coefficient ring. + + For `q=1` we use the formula from Corollary 7.21.4 of [EnumComb2]_: + + .. MATH:: + + ps_{n,1}(s_\lambda) = \prod_{u\in\lambda} (n+c(u)) / h(u), + + where `h(u)` is the hook length of a cell `u` in `\lambda`, + and where `c(u)` is the content of a cell `u` in `\lambda`. + + For `n=infinity` we use the formula from Corollary 7.21.3 of [EnumComb2]_ + + .. MATH:: + + ps_q(s_\lambda) = q^{\sum_i (i-1)\lambda_i} / \prod_{u\in\lambda} (1-q^{h(u)}). + + Otherwise, we use the formula from Theorem 7.21.2 of [EnumComb2]_, + + .. MATH:: + + ps_{n,q}(s_\lambda) = q^{\sum_i (i-1)\lambda_i} + \prod_{u\in\lambda} (1-q^{n+c(u)})/(1-q^{h(u)}). + + EXAMPLES:: + + sage: s = SymmetricFunctions(QQ).s() + sage: x = s[2] + sage: x.principal_specialization(3) + q^4 + q^3 + 2*q^2 + q + 1 + + sage: x = 3*s[2,2] + 2*s[1] + 1 + sage: x.principal_specialization(3, q=var("q")) + 3*(q^4 - 1)*(q^3 - 1)*q^2/((q^2 - 1)*(q - 1)) + 2*(q^3 - 1)/(q - 1) + 1 + + sage: x.principal_specialization(q=var("q")) + -2/(q - 1) + 3*q^2/((q^3 - 1)*(q^2 - 1)^2*(q - 1)) + 1 + + TESTS:: + + sage: s.zero().principal_specialization(3) + 0 + + """ + def get_variable(ring, name): + try: + ring(name) + except TypeError: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + return PolynomialRing(ring, name).gen() + else: + raise ValueError("the variable %s is in the base ring, pass it explicitly" % name) + + if q is None: + q = get_variable(self.base_ring(), 'q') + if q == 1: + if n == infinity: + raise ValueError("the stable principal specialization at q=1 is not defined") + f = lambda partition: (prod(n+j-i for (i, j) in partition.cells()) + // prod(h for h in partition.hooks())) + elif n == infinity: + f = lambda partition: (q**sum(i*part for i, part in enumerate(partition)) + / prod(1-q**h for h in partition.hooks())) + else: + from sage.rings.integer_ring import ZZ + ZZq = PolynomialRing(ZZ, "q") + q_lim = ZZq.gen() + def f(partition): + if n < len(partition): + return 0 + power = q**sum(i*part for i, part in enumerate(partition)) + denom = prod(1-q**h for h in partition.hooks()) + try: + ~denom + rational = (power + * prod(1-q**(n+j-i) + for (i, j) in partition.cells()) + / denom) + return q.parent()(rational) + except (ZeroDivisionError, NotImplementedError, TypeError): + # If denom is not invertible, we need to do the + # computation with universal coefficients instead: + quotient = ZZq((prod(1-q_lim**(n+j-i) + for (i, j) in partition.cells())) + / prod(1-q_lim**h for h in partition.hooks())) + return (power * quotient.subs({q_lim: q})) + + return self.parent()._apply_module_morphism(self, f, q.parent()) + + + def exponential_specialization(self, t=None, q=1): + r""" + Return the exponential specialization of a + symmetric function (when `q = 1`), or the + `q`-exponential specialization (when `q \neq 1`). + + The *exponential specialization* `ex` at `t` is a + `K`-algebra homomorphism from the `K`-algebra of + symmetric functions to another `K`-algebra `R`. + It is defined whenever the base ring `K` is a + `\QQ`-algebra and `t` is an element of `R`. + The easiest way to define it is by specifying its + values on the powersum symmetric functions to be + `p_1 = t` and `p_n = 0` for `n > 1`. + Equivalently, on the homogeneous functions it is + given by `ex(h_n) = t^n / n!`; see Proposition 7.8.4 of + [EnumComb2]_. + + By analogy, the `q`-exponential specialization is a + `K`-algebra homomorphism from the `K`-algebra of + symmetric functions to another `K`-algebra `R` that + depends on two elements `t` and `q` of `R` for which + the elements `1 - q^i` for all positive integers `i` + are invertible. + It can be defined by specifying its values on the + complete homogeneous symmetric functions to be + + .. MATH:: + + ex_q(h_n) = t^n / [n]_q!, + + where `[n]_q!` is the `q`-factorial. Equivalently, for + `q \neq 1` and a homogeneous symmetric function `f` of + degree `n`, we have + + .. MATH:: + + ex_q(f) = (1-q)^n t^n ps_q(f), + + where `ps_q(f)` is the stable principal specialization of `f` + (see :meth:`principal_specialization`). + (See (7.29) in [EnumComb2]_.) + + The limit of `ex_q` as `q \to 1` is `ex`. + + INPUT: + + - ``t`` (default: ``None``) -- the value to use for `t`; + the default is to create a ring of polynomials in ``t``. + + - ``q`` (default: `1`) -- the value to use for `q`. If + ``q`` is ``None``, then a ring (or fraction field) of + polynomials in ``q`` is created. + + We use the formula in the proof of Corollary 7.21.6 of + [EnumComb2]_ + + .. MATH:: + + ex_{q}(s_\lambda) = t^{|\lambda|} q^{\sum_i (i-1)\lambda_i} + / \prod_{u\in\lambda} (1 + q + q^2 + \dots + q^{h(u)-1}) + + where `h(u)` is the hook length of a cell `u` in `\lambda`. + + As a limit case, we obtain a formula for `q=1` + + .. MATH:: + + ex_{1}(s_\lambda) = f^\lambda t^{|\lambda|} / |\lambda|! + + where `f^\lambda` is the number of standard Young + tableaux of shape `\lambda`. + + EXAMPLES:: + + sage: s = SymmetricFunctions(QQ).s() + sage: x = s[5,3] + sage: x.exponential_specialization() + 1/1440*t^8 + + sage: x = 5*s[1,1,1] + 3*s[2,1] + 1 + sage: x.exponential_specialization() + 11/6*t^3 + 1 + + We also support the `q`-exponential_specialization:: + + sage: factor(s[3].exponential_specialization(q=var("q"), t=var("t"))) + t^3/((q^2 + q + 1)*(q + 1)) + + TESTS:: + + sage: s.zero().exponential_specialization() + 0 + + """ + def get_variable(ring, name): + try: + ring(name) + except TypeError: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + return PolynomialRing(ring, name).gen() + else: + raise ValueError("the variable %s is in the base ring, pass it explicitly" % name) + + if q == 1: + if t is None: + t = get_variable(self.base_ring(), 't') + + def f(partition): + n = partition.size() + return (StandardTableaux(partition).cardinality() + * t**n / factorial(n)) + + return self.parent()._apply_module_morphism(self, f, t.parent()) + + if q is None and t is None: + q = get_variable(self.base_ring(), 'q') + t = get_variable(q.parent(), 't') + elif q is None: + q = get_variable(t.parent(), 'q') + elif t is None: + t = get_variable(q.parent(), 't') + + f = lambda partition: (t**partition.size() + * q**sum(i*part for i, part in enumerate(partition)) + / prod(sum(q**i for i in range(h)) for h in partition.hooks())) + + return self.parent()._apply_module_morphism(self, f, t.parent()) + + # Backward compatibility for unpickling from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.combinat.sf.schur', 'SymmetricFunctionAlgebraElement_schur', SymmetricFunctionAlgebra_schur.Element) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 35838d1cd01..73da832155d 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -213,7 +213,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from sage.misc.cachefunc import cached_method -from sage.rings.all import Integer, PolynomialRing, QQ, ZZ +from sage.rings.all import Integer, PolynomialRing, QQ, ZZ, infinity from sage.rings.polynomial.polynomial_element import is_Polynomial from sage.rings.polynomial.multi_polynomial import is_MPolynomial from sage.combinat.partition import _Partitions, Partitions, Partitions_n, Partition @@ -300,8 +300,6 @@ def is_SymmetricFunction(x): from sage.categories.realizations import Category_realization_of_parent -import six - class SymmetricFunctionsBases(Category_realization_of_parent): r""" @@ -664,7 +662,7 @@ def corresponding_basis_over(self, R): put on a more robust and systematic footing. """ from sage.combinat.sf.sf import SymmetricFunctions - from sage.misc.misc import attrcall + from sage.misc.call import attrcall try: return attrcall(self._basis)(SymmetricFunctions(R)) except AttributeError: # or except (AttributeError, ValueError): @@ -782,8 +780,9 @@ def gessel_reutenauer(self, lam): Let `\lambda` be a partition. The *Gessel-Reutenauer symmetric function* `\mathbf{GR}_\lambda` corresponding to `\lambda` is the symmetric function denoted `L_\lambda` in - [GR1993]_ and in Exercise 7.89 of [STA]_. It can be defined - in several ways: + [GR1993]_ and in Exercise 7.89 of [STA]_ and denoted + `\mathbf{GR}_\lambda` in Definition 6.6.34 of [GriRei18]_. + It can be defined in several ways: - It is the sum of the monomials `\mathbf{x}_w` over all words `w` over the alphabet @@ -800,15 +799,15 @@ def gessel_reutenauer(self, lam): - It is the sum of the fundamental quasisymmetric functions `F_{\operatorname{Des} \sigma}` over all - permutations `\sigma` which have cycle type `\lambda`. See + permutations `\sigma` that have cycle type `\lambda`. See :class:`sage.combinat.ncsf_qsym.qsym.QuasiSymmetricFunctions.Fundamental` for the definition of fundamental quasisymmetric functions, and :meth:`~sage.combinat.permutation.Permutation.cycle_type` for that of cycle type. For a permutation `\sigma`, we use `\operatorname{Des} \sigma` to denote the descent composition (:meth:`~sage.combinat.permutation.Permutation.descents_composition`) - of `\sigma`. Again, this definition makes the symmetry - of `\mathbf{GR}_\lambda` far from obvious. + of `\sigma`. Again, this definition does not make the + symmetry of `\mathbf{GR}_\lambda` obvious. - For every positive integer `n`, we have @@ -834,7 +833,9 @@ def gessel_reutenauer(self, lam): `\mathbf{GR}_\lambda` obvious. The equivalences of these three definitions are proven in - [GR1993]_ Sections 2-3. + [GR1993]_ Sections 2-3. (See also [GriRei18]_ Subsection + 6.6.2 for the equivalence of the first two definitions and + further formulas.) INPUT: @@ -855,6 +856,8 @@ def gessel_reutenauer(self, lam): Journal of Combinatorial Theory, Series A, 64 (1993), pp. 189--215. + .. [GriRei18]_ + EXAMPLES: The first few values of `\mathbf{GR}_{(n)} = L_n`:: @@ -1634,7 +1637,7 @@ def _change_by_proportionality(self, x, function): """ BR = self.base_ring() z_elt = {} - for m, c in six.iteritems(x._monomial_coefficients): + for m, c in x._monomial_coefficients.items(): coeff = function(m) z_elt[m] = BR( c*coeff ) return self._from_dict(z_elt) @@ -1719,7 +1722,7 @@ def _apply_multi_module_morphism(self, x, y, f, orthogonal=False): if orthogonal: # could check which of x and y has less terms # for mx, cx in x: - for mx, cx in six.iteritems(x._monomial_coefficients): + for mx, cx in x._monomial_coefficients.items(): if mx not in y._monomial_coefficients: continue else: @@ -1729,8 +1732,8 @@ def _apply_multi_module_morphism(self, x, y, f, orthogonal=False): res += cx*cy*f(mx, mx) return res else: - for mx, cx in six.iteritems(x._monomial_coefficients): - for my, cy in six.iteritems(y._monomial_coefficients): + for mx, cx in x._monomial_coefficients.items(): + for my, cy in y._monomial_coefficients.items(): res += cx*cy*f(mx,my) return res @@ -1814,13 +1817,13 @@ def _from_cache(self, element, cache_function, cache_dict, **subs_dict): BR = self.base_ring() zero = BR.zero() z_elt = {} - for part, c in six.iteritems(element.monomial_coefficients()): + for part, c in element.monomial_coefficients().items(): if sum(part) not in cache_dict: cache_function(sum(part)) # Make sure it is a partition (for #13605), this is # needed for the old kschur functions - TCS part = _Partitions(part) - for part2, c2 in six.iteritems(cache_dict[sum(part)][part]): + for part2, c2 in cache_dict[sum(part)][part].items(): if hasattr(c2,'subs'): # c3 may be in the base ring c3 = c*BR(c2.subs(**subs_dict)) else: @@ -3094,8 +3097,8 @@ def inner_plethysm(self, x): p = parent.realization_of().power() cache = {} ip_pnu_g = parent._inner_plethysm_pnu_g - return parent.sum( c*ip_pnu_g(p(x), cache, nu) - for (nu, c) in six.iteritems(p(self).monomial_coefficients()) ) + return parent.sum(c*ip_pnu_g(p(x), cache, nu) + for (nu, c) in p(self).monomial_coefficients().items()) def omega(self): @@ -5439,6 +5442,395 @@ def character_to_frobenius_image(self, n): return self.parent()(p.sum(self.eval_at_permutation_roots(rho) \ *p(rho)/rho.centralizer_size() for rho in Partitions(n))) + def principal_specialization(self, n=infinity, q=None): + r""" + Return the principal specialization of a symmetric function. + + The *principal specialization* of order `n` at `q` + is the ring homomorphism `ps_{n,q}` from the ring of + symmetric functions to another commutative ring `R` + given by `x_i \mapsto q^{i-1}` for `i \in \{1,\dots,n\}` + and `x_i \mapsto 0` for `i > n`. + Here, `q` is a given element of `R`, and we assume that + the variables of our symmetric functions are + `x_1, x_2, x_3, \ldots`. + (To be more precise, `ps_{n,q}` is a `K`-algebra + homomorphism, where `K` is the base ring.) + See Section 7.8 of [EnumComb2]_. + + The *stable principal specialization* at `q` is the ring + homomorphism `ps_q` from the ring of symmetric functions + to another commutative ring `R` given by + `x_i \mapsto q^{i-1}` for all `i`. + This is well-defined only if the resulting infinite sums + converge; thus, in particular, setting `q = 1` in the + stable principal specialization is an invalid operation. + + INPUT: + + - ``n`` (default: ``infinity``) -- a nonnegative integer or + ``infinity``, specifying whether to compute the principal + specialization of order ``n`` or the stable principal + specialization. + + - ``q`` (default: ``None``) -- the value to use for `q`; the + default is to create a ring of polynomials in ``q`` + (or a field of rational functions in ``q``) over the + given coefficient ring. + + EXAMPLES:: + + sage: m = SymmetricFunctions(QQ).m() + sage: x = m[1,1] + sage: x.principal_specialization(3) + q^3 + q^2 + q + + By default we return a rational function in ``q``. Sometimes + it is better to obtain an element of the symbolic ring:: + + sage: h = SymmetricFunctions(QQ).h() + sage: (h[3]+h[2]).principal_specialization(q=var("q")) + 1/((q^2 - 1)*(q - 1)) - 1/((q^3 - 1)*(q^2 - 1)*(q - 1)) + + In case ``q`` is in the base ring, it must be passed explicitly:: + + sage: R = QQ['q,t'] + sage: Ht = SymmetricFunctions(R).macdonald().Ht() + sage: Ht[2].principal_specialization() + Traceback (most recent call last): + ... + ValueError: the variable q is in the base ring, pass it explicitly + + sage: Ht[2].principal_specialization(q=R("q")) + (q^2 + 1)/(q^3 - q^2 - q + 1) + + Note that the principal specialization can be obtained as a plethysm:: + + sage: R = QQ['q'].fraction_field() + sage: s = SymmetricFunctions(R).s() + sage: one = s.one() + sage: q = R("q") + sage: f = s[3,2,2] + sage: f.principal_specialization(q=q) == f(one/(1-q)).coefficient([]) + True + sage: f.principal_specialization(n=4, q=q) == f(one*(1-q^4)/(1-q)).coefficient([]) + True + + TESTS:: + + sage: m = SymmetricFunctions(QQ).m() + sage: m.zero().principal_specialization(3) + 0 + + sage: x = 5*m[1,1,1] + 3*m[2,1] + 1 + sage: x.principal_specialization(3) + 3*q^5 + 6*q^4 + 5*q^3 + 6*q^2 + 3*q + 1 + + Check that the principal specializations in different bases + are all the same. When specific implementations for further + bases are added, this test should be adapted:: + + sage: S = SymmetricFunctions(QQ) + sage: B = [S.p(), S.m(), S.e(), S.h(), S.s(), S.f()] + sage: m = S.m(); x = m[2,1] + sage: len(set([b(x).principal_specialization(n=3) for b in B])) + 1 + sage: len(set([b(x).principal_specialization() for b in B])) + 1 + sage: len(set([b(x).principal_specialization(n=4, q=1) for b in B])) + 1 + sage: len(set([b(x).principal_specialization(n=4, q=2) for b in B])) + 1 + + Check that the stable principal specialization at `q = 1` + raises a ``ValueError``: + + sage: def test_error(x): + ....: message = "the stable principal specialization of %s at q=1 should raise a ValueError" + ....: try: + ....: x.principal_specialization(q=1) + ....: except ValueError as e: + ....: return(e) + ....: except StandardError as e: + ....: raise ValueError((message + ", but raised '%s' instead") % (x, e)) + ....: raise ValueError((message + ", but didn't") % x) + + sage: set([str(test_error(b(x))) for b in B]) + {'the stable principal specialization at q=1 is not defined'} + + Check that specifying `q` which is a removable singularity works:: + + sage: S = SymmetricFunctions(QQ) + sage: B = [S.p(), S.m(), S.e(), S.h(), S.s(), S.f()] + sage: m = S.m(); x = m[2,2,1] + sage: set([b(x).principal_specialization(n=4, q=QQbar.zeta(3)) for b in B]) + {-3} + + sage: S = SymmetricFunctions(GF(3)) + sage: B = [S.p(), S.m(), S.e(), S.h(), S.s(), S.f()] + sage: m = S.m(); x = m[3,2,1] + sage: set([b(x).principal_specialization(n=4, q=GF(3)(2)) for b in B]) + {1} + + sage: S = SymmetricFunctions(Zmod(4)) + sage: B = [S.p(), S.m(), S.e(), S.h(), S.s(), S.f()] + sage: m = S.m(); x = m[3,2,1] + sage: set([b(x).principal_specialization(n=4, q=Zmod(4)(2)) for b in B]) + {0} + sage: y = m[3,1] + sage: set([b(y).principal_specialization(n=4, q=Zmod(4)(2)) for b in B]) + {2} + sage: B = [S.m(), S.e(), S.h(), S.s(), S.f()] + sage: z = m[1,1] + sage: set([b(z).principal_specialization(n=4) for b in B]) + {q^5 + q^4 + 2*q^3 + q^2 + q} + + Check that parents are correct over `\mathbb{F}_3`:: + + sage: S = SymmetricFunctions(GF(3)) + sage: B = [S.p(), S.m(), S.e(), S.h(), S.s(), S.f()] + sage: lams = [Partition([]), Partition([1]), Partition([2,1])] + sage: set(b[lam].principal_specialization(n=2, q=GF(3)(0)).parent() for b in B for lam in lams) + {Finite Field of size 3} + sage: set(b[lam].principal_specialization(n=2, q=GF(3)(1)).parent() for b in B for lam in lams) + {Finite Field of size 3} + sage: set(b[lam].principal_specialization(n=2, q=GF(3)(2)).parent() for b in B for lam in lams) + {Finite Field of size 3} + sage: set(b[lam].principal_specialization(n=2).parent() for b in B for lam in lams) + {Univariate Polynomial Ring in q over Finite Field of size 3} + sage: set(b[lam].principal_specialization().parent() for b in B for lam in lams) + {Fraction Field of Univariate Polynomial Ring in q over Finite Field of size 3, + Univariate Polynomial Ring in q over Finite Field of size 3} + + sage: a = S.e()[2,1].principal_specialization(n=2, q=GF(3)(2)); a + 0 + sage: a = S.e()[1,1,1].principal_specialization(n=2); a + q^3 + 1 + + sage: set(b.one().principal_specialization(n=2, q=GF(3)(2)) for b in B) + {1} + sage: set(b.one().principal_specialization(n=2, q=GF(3)(1)) for b in B) + {1} + sage: set(b.one().principal_specialization(n=2) for b in B) + {1} + sage: set(b.one().principal_specialization() for b in B) + {1} + + Check that parents are correct over the integer ring:: + + sage: S = SymmetricFunctions(ZZ) + sage: B = [S.p(), S.m(), S.e(), S.h(), S.s(), S.f()] + sage: lams = [Partition([]), Partition([1]), Partition([2,1])] + sage: set(b[lam].principal_specialization(n=2, q=0).parent() for b in B for lam in lams) + {Integer Ring} + sage: set(b[lam].principal_specialization(n=2, q=1).parent() for b in B for lam in lams) + {Integer Ring} + sage: set(b[lam].principal_specialization(n=2, q=2).parent() for b in B for lam in lams) + {Integer Ring} + sage: set(b[lam].principal_specialization(n=2).parent() for b in B for lam in lams) + {Univariate Polynomial Ring in q over Integer Ring} + sage: sorted(set(b[lam].principal_specialization().parent() for b in B for lam in lams), key=str) + [Fraction Field of Univariate Polynomial Ring in q over Integer Ring, + Univariate Polynomial Ring in q over Integer Ring, + Univariate Polynomial Ring in q over Rational Field] + + Check that parents are correct over a polynomial ring:: + + sage: P = PolynomialRing(ZZ, "q") + sage: q = P.gen() + sage: S = SymmetricFunctions(P) + sage: B = [S.p(), S.m(), S.e(), S.h(), S.s(), S.f()] + sage: lams = [Partition([]), Partition([1]), Partition([2,1])] + sage: set(b[lam].principal_specialization(n=2, q=P(0)).parent() for b in B for lam in lams) + {Univariate Polynomial Ring in q over Integer Ring} + sage: set(b[lam].principal_specialization(n=2, q=P(1)).parent() for b in B for lam in lams) + {Univariate Polynomial Ring in q over Integer Ring} + sage: set(b[lam].principal_specialization(n=2, q=P(2)).parent() for b in B for lam in lams) + {Univariate Polynomial Ring in q over Integer Ring} + sage: set(b[lam].principal_specialization(n=2, q=q).parent() for b in B for lam in lams) + {Univariate Polynomial Ring in q over Integer Ring} + sage: sorted(set(b[lam].principal_specialization(q=q).parent() for b in B for lam in lams), key=str) + [Fraction Field of Univariate Polynomial Ring in q over Integer Ring, + Univariate Polynomial Ring in q over Integer Ring, + Univariate Polynomial Ring in q over Rational Field] + + sage: a = S.e()[2,1].principal_specialization(n=2, q=2); a + 6 + sage: a = S.e()[2,1].principal_specialization(n=2, q=q); a + q^2 + q + + sage: set(b.one().principal_specialization(n=2, q=P(2)) for b in B) + {1} + sage: set(b.one().principal_specialization(n=2, q=P(1)) for b in B) + {1} + sage: set(b.one().principal_specialization(n=2, q=q) for b in B) + {1} + sage: set(b.one().principal_specialization(q=q) for b in B) + {1} + + """ + # heuristically, it seems fastest to fall back to the + # elementary basis - using the powersum basis would + # introduce singularities, because it is not a Z-basis + e = self.parent().realization_of().elementary() + return e(self).principal_specialization(n, q=q) + + def exponential_specialization(self, t=None, q=1): + r""" + Return the exponential specialization of a + symmetric function (when `q = 1`), or the + `q`-exponential specialization (when `q \neq 1`). + + The *exponential specialization* `ex` at `t` is a + `K`-algebra homomorphism from the `K`-algebra of + symmetric functions to another `K`-algebra `R`. + It is defined whenever the base ring `K` is a + `\QQ`-algebra and `t` is an element of `R`. + The easiest way to define it is by specifying its + values on the powersum symmetric functions to be + `p_1 = t` and `p_n = 0` for `n > 1`. + Equivalently, on the homogeneous functions it is + given by `ex(h_n) = t^n / n!`; see Proposition 7.8.4 of + [EnumComb2]_. + + By analogy, the `q`-exponential specialization is a + `K`-algebra homomorphism from the `K`-algebra of + symmetric functions to another `K`-algebra `R` that + depends on two elements `t` and `q` of `R` for which + the elements `1 - q^i` for all positive integers `i` + are invertible. + It can be defined by specifying its values on the + complete homogeneous symmetric functions to be + + .. MATH:: + + ex_q(h_n) = t^n / [n]_q!, + + where `[n]_q!` is the `q`-factorial. Equivalently, for + `q \neq 1` and a homogeneous symmetric function `f` of + degree `n`, we have + + .. MATH:: + + ex_q(f) = (1-q)^n t^n ps_q(f), + + where `ps_q(f)` is the stable principal specialization of `f` + (see :meth:`principal_specialization`). + (See (7.29) in [EnumComb2]_.) + + The limit of `ex_q` as `q \to 1` is `ex`. + + INPUT: + + - ``t`` (default: ``None``) -- the value to use for `t`; + the default is to create a ring of polynomials in ``t``. + + - ``q`` (default: `1`) -- the value to use for `q`. If + ``q`` is ``None``, then a ring (or fraction field) of + polynomials in ``q`` is created. + + EXAMPLES:: + + sage: m = SymmetricFunctions(QQ).m() + sage: (m[2,1]+m[1,1]).exponential_specialization() + 1/2*t^2 + sage: (m[2,1]+m[1,1]).exponential_specialization(q=1) + 1/2*t^2 + sage: m[1,1].exponential_specialization(q=None) + (q/(q + 1))*t^2 + sage: Qq = PolynomialRing(QQ, "q"); q = Qq.gen() + sage: m[1,1].exponential_specialization(q=q) + (q/(q + 1))*t^2 + sage: Qt = PolynomialRing(QQ, "t"); t = Qt.gen() + sage: m[1,1].exponential_specialization(t=t) + 1/2*t^2 + sage: Qqt = PolynomialRing(QQ, ["q", "t"]); q, t = Qqt.gens() + sage: m[1,1].exponential_specialization(q=q, t=t) + q*t^2/(q + 1) + + sage: x = m[3]+m[2,1]+m[1,1,1] + sage: d = x.homogeneous_degree() + sage: var("q t") + (q, t) + sage: factor((x.principal_specialization()*(1-q)^d*t^d)) + t^3/((q^2 + q + 1)*(q + 1)) + sage: factor(x.exponential_specialization(q=q, t=t)) + t^3/((q^2 + q + 1)*(q + 1)) + + TESTS:: + + sage: m = SymmetricFunctions(QQ).m() + sage: m.zero().exponential_specialization() + 0 + + Check that the exponential specializations in different bases + are all the same. When specific implementations for further + bases are added, this test should be adapted:: + + sage: S = SymmetricFunctions(QQ) + sage: B = [S.p(), S.m(), S.e(), S.h(), S.s(), S.f()] + sage: m = S.m(); x = m[3]+m[2,1]+m[1,1,1] + sage: len(set([b(x).exponential_specialization(q=None, t=None) for b in B])) + 1 + sage: len(set([b(x).exponential_specialization(q=1) for b in B])) + 1 + sage: len(set([b(x).exponential_specialization(q=2) for b in B])) + 1 + sage: len(set([b(x).exponential_specialization(t=2) for b in B])) + 1 + + Check that parents are correct over `\mathbb{F}_3`:: + + sage: S = SymmetricFunctions(GF(3)) + sage: B = [S.p(), S.m(), S.e(), S.h(), S.s(), S.f()] + sage: lams = [Partition([]), Partition([1]), Partition([2,1])] + sage: sorted(set(b[lam].exponential_specialization(q=None).parent() for b in B for lam in lams), key=str) + [Univariate Polynomial Ring in t over Fraction Field + of Univariate Polynomial Ring in q over Finite Field of size 3, + Univariate Polynomial Ring in t over Univariate Polynomial Ring + in q over Finite Field of size 3] + sage: P2 = PolynomialRing(GF(3), ["q", "t"]) + sage: q2, t2 = P2.gens() + sage: sorted(set(b[lam].exponential_specialization(q=q2, t=t2).parent() for b in B for lam in lams), key=str) + [Fraction Field of Multivariate Polynomial Ring in q, t over Finite Field of size 3, + Multivariate Polynomial Ring in q, t over Finite Field of size 3] + + Check that parents are correct over `\QQ` for `q = 1`:: + + sage: S = SymmetricFunctions(QQ) + sage: B = [S.p(), S.m(), S.e(), S.h(), S.s(), S.f()] + sage: lams = [Partition([]), Partition([1]), Partition([2,1])] + sage: set(b[lam].exponential_specialization(q=1).parent() for b in B for lam in lams) + {Univariate Polynomial Ring in t over Rational Field} + sage: set(b[lam].exponential_specialization(q=1, t=1).parent() for b in B for lam in lams) + {Rational Field} + sage: P2 = PolynomialRing(QQ, ["q", "t"]) + sage: q2, t2 = P2.gens() + sage: set(b[lam].exponential_specialization(q=1, t=t2).parent() for b in B for lam in lams) + {Multivariate Polynomial Ring in q, t over Rational Field} + + Check that parents are correct over a polynomial ring:: + + sage: P = PolynomialRing(QQ, "q") + sage: q = P.gen() + sage: S = SymmetricFunctions(P) + sage: B = [S.p(), S.m(), S.e(), S.h(), S.s(), S.f()] + sage: lams = [Partition([]), Partition([1]), Partition([2,1])] + sage: sorted(set(b[lam].exponential_specialization(q=q).parent() for b in B for lam in lams), key=str) + [Univariate Polynomial Ring in t over + Fraction Field of Univariate Polynomial Ring in q over Rational Field, + Univariate Polynomial Ring in t over Univariate Polynomial Ring + in q over Rational Field] + sage: sorted(set(b[lam].exponential_specialization(q=q, t=1).parent() for b in B for lam in lams), key=str) + [Fraction Field of Univariate Polynomial Ring in q over Rational Field, + Univariate Polynomial Ring in q over Rational Field] + """ + # heuristically, it seems fastest to fall back to the + # elementary basis - using the powersum basis would + # introduce singularities, because it is not a Z-basis + e = self.parent().realization_of().elementary() + return e(self).exponential_specialization(t=t, q=q) + SymmetricFunctionAlgebra_generic.Element = SymmetricFunctionAlgebra_generic_Element diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index ca76bed033e..5c23ae2d335 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -18,7 +18,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import, division -from six import add_metaclass from sage.combinat.partition import Partition, Partitions, _Partitions, OrderedPartitions from sage.combinat.partitions import ZS1_iterator @@ -45,8 +44,8 @@ from sage.combinat.combination import Combinations -@add_metaclass(InheritComparisonClasscallMetaclass) -class ShiftedPrimedTableau(ClonableArray): +class ShiftedPrimedTableau(ClonableArray, + metaclass=InheritComparisonClasscallMetaclass): r""" A shifted primed tableau. @@ -2416,6 +2415,7 @@ def _contains_tableau(self, T): Check if ``self`` contains preprocessed tableau ``T``. TESTS:: + sage: t = ShiftedPrimedTableau._preprocess([[1,1.5],[2]]) sage: ShiftedPrimedTableaux(weight=(1,2))._contains_tableau(t) True diff --git a/src/sage/combinat/shuffle.py b/src/sage/combinat/shuffle.py index f3591750fa6..06845e0d75f 100644 --- a/src/sage/combinat/shuffle.py +++ b/src/sage/combinat/shuffle.py @@ -324,17 +324,17 @@ def __iter__(self): sage: B = BinaryTree sage: ascii_art(list(ShuffleProduct([B([]), B([[],[]])], ....: [B([[[],[]],[[],None]])]))) - [ [ o, o , __o__ ] [ __o__ , o, o ] [ o, __o__ , - [ [ / \ / \ ] [ / \ / \ ] [ / \ - [ [ o o o o ] [ o o o o ] [ o o - [ [ / \ / ] [ / \ / ] [ / \ / - [ [ o o o ], [ o o o ], [ o o o + [ [ o, o , __o__ ] [ __o__ , o, o ] + [ [ / \ / \ ] [ / \ / \ ] + [ [ o o o o ] [ o o o o ] + [ [ / \ / ] [ / \ / ] + [ [ o o o ], [ o o o ], - o ] ] - / \ ] ] - o o ] ] - ] ] - ] ] + [ o, __o__ , o ] ] + [ / \ / \ ] ] + [ o o o o ] ] + [ / \ / ] ] + [ o o o ] ] """ ############ Gray code ############# diff --git a/src/sage/combinat/similarity_class_type.py b/src/sage/combinat/similarity_class_type.py index c52ea6e3944..ec3676a710c 100644 --- a/src/sage/combinat/similarity_class_type.py +++ b/src/sage/combinat/similarity_class_type.py @@ -175,8 +175,6 @@ class type, it is also possible to compute the number of classes of that type # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six.moves import range -from six import add_metaclass from operator import mul from itertools import chain, product @@ -340,8 +338,8 @@ def centralizer_group_cardinality(la, q = None): return q**centralizer_algebra_dim(la)*prod([fq(m, q = q) for m in la.to_exp()]) -@add_metaclass(InheritComparisonClasscallMetaclass) -class PrimarySimilarityClassType(Element): +class PrimarySimilarityClassType(Element, + metaclass=InheritComparisonClasscallMetaclass): r""" A primary similarity class type is a pair consisting of a partition and a positive integer. diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index 34c7f62b482..c2825d8de23 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -143,8 +143,6 @@ #***************************************************************************** from __future__ import print_function -from six.moves import range - from sage.structure.global_options import GlobalOptions from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -696,7 +694,7 @@ def is_ribbon(self): l_out = len(lam) l_in = len(mu) mu += [0]*(l_out-l_in) - + if l_out == 0: return True else: @@ -710,7 +708,7 @@ def is_ribbon(self): else: u += 1 - # Find the least v strictly greater than u for which + # Find the least v strictly greater than u for which # lam[v] != mu[v-1]+1 v = u + 1 v_test = True diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index e35c782d13a..cd9182ab58b 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -24,8 +24,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import -from six import add_metaclass -from six.moves import range, zip from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.structure.parent import Parent @@ -48,8 +46,8 @@ from sage.combinat.words.words import Words -@add_metaclass(InheritComparisonClasscallMetaclass) -class SkewTableau(ClonableList): +class SkewTableau(ClonableList, + metaclass=InheritComparisonClasscallMetaclass): r""" A skew tableau. diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index a0ebd69bef5..91edfc0f723 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -124,8 +124,6 @@ # just used for handy .load, .save, etc. from __future__ import print_function, absolute_import -from six.moves import range -from six import integer_types import inspect from sage.structure.sage_object import SageObject @@ -227,7 +225,7 @@ def __call__(self, n): ... ValueError: input n (=0) must be a positive integer """ - if not isinstance(n, integer_types + (Integer_class,)): + if not isinstance(n, (int, Integer_class)): raise TypeError("input must be an int or Integer") m = ZZ(n) if m < self.offset: diff --git a/src/sage/combinat/species/characteristic_species.py b/src/sage/combinat/species/characteristic_species.py index be0f2bb0ff6..4e1dfe4605b 100644 --- a/src/sage/combinat/species/characteristic_species.py +++ b/src/sage/combinat/species/characteristic_species.py @@ -16,7 +16,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from .species import GenericCombinatorialSpecies from .generating_series import factorial_stream diff --git a/src/sage/combinat/species/library.py b/src/sage/combinat/species/library.py index 79eabc06763..4b4b535a611 100644 --- a/src/sage/combinat/species/library.py +++ b/src/sage/combinat/species/library.py @@ -102,7 +102,7 @@ def BinaryTreeSpecies(): sage: seq = B.isotype_generating_series().counts(10)[1:] sage: oeis(seq)[0] # optional -- internet - A000108: Catalan numbers: C(n) = binomial(2n,n)/(n+1) = (2n)!/(n!(n+1)!). Also called Segner numbers. + A000108: Catalan numbers: ... """ B = CombinatorialSpecies() X = SingletonSpecies() diff --git a/src/sage/combinat/species/permutation_species.py b/src/sage/combinat/species/permutation_species.py index d9962fb460e..c640324d9cf 100644 --- a/src/sage/combinat/species/permutation_species.py +++ b/src/sage/combinat/species/permutation_species.py @@ -16,7 +16,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from .species import GenericCombinatorialSpecies from .structure import GenericSpeciesStructure diff --git a/src/sage/combinat/species/series.py b/src/sage/combinat/species/series.py index c847580abb2..ead9daae563 100644 --- a/src/sage/combinat/species/series.py +++ b/src/sage/combinat/species/series.py @@ -30,12 +30,15 @@ # **************************************************************************** from __future__ import absolute_import +import builtins + from .stream import Stream, Stream_class from .series_order import bounded_decrement, increment, inf, unk from sage.rings.all import Integer from sage.misc.all import prod from functools import partial -from sage.misc.misc import repr_lincomb, is_iterator +from sage.misc.misc import is_iterator +from sage.misc.repr import repr_lincomb from sage.misc.cachefunc import cached_method from sage.algebras.algebra import Algebra @@ -1743,7 +1746,6 @@ def restricted(self, min=None, max=None): sage: a.restricted(min=2, max=6).coefficients(10) [0, 0, 1, 1, 1, 1, 0, 0, 0, 0] """ - from six.moves import builtins if ((min is None and max is None) or (max is None and self.get_aorder() >= min)): diff --git a/src/sage/combinat/species/set_species.py b/src/sage/combinat/species/set_species.py index 991de91227e..1f8d6e3f6de 100644 --- a/src/sage/combinat/species/set_species.py +++ b/src/sage/combinat/species/set_species.py @@ -16,7 +16,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from .species import GenericCombinatorialSpecies from .generating_series import factorial_stream, _integers_from diff --git a/src/sage/combinat/species/subset_species.py b/src/sage/combinat/species/subset_species.py index 86a8cea8703..dc2539807c5 100644 --- a/src/sage/combinat/species/subset_species.py +++ b/src/sage/combinat/species/subset_species.py @@ -16,7 +16,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from .species import GenericCombinatorialSpecies from .set_species import SetSpecies diff --git a/src/sage/combinat/subset.py b/src/sage/combinat/subset.py index b4278891e23..9ef2781d7ea 100644 --- a/src/sage/combinat/subset.py +++ b/src/sage/combinat/subset.py @@ -28,8 +28,6 @@ # **************************************************************************** from __future__ import print_function, absolute_import -import six -from six.moves import range import sage.misc.prandom as rnd import itertools @@ -359,7 +357,22 @@ def cardinality(self): """ return Integer(1) << self._s.cardinality() - __len__ = cardinality + def __len__(self): + r""" + Equivalent to ``self.cardinality()``. + + TESTS:: + + ``__len__`` should return a Python int; in Python 3.7+ this happens + automatically, but not on Python 3.6. + + sage: S = Subsets(Set([1,2,3])) + sage: len(S) + 8 + sage: type(len(S)) is int + True + """ + return int(self.cardinality()) def first(self): """ @@ -880,7 +893,7 @@ def dict_to_list(d): ['a', 'b', 'b', 'b'] """ l = [] - for i, j in six.iteritems(d): + for i, j in d.items(): l.extend([i] * j) return l diff --git a/src/sage/combinat/subsets_hereditary.py b/src/sage/combinat/subsets_hereditary.py index 27558ed630d..6c0dfe42c35 100644 --- a/src/sage/combinat/subsets_hereditary.py +++ b/src/sage/combinat/subsets_hereditary.py @@ -64,7 +64,7 @@ def subsets_with_hereditary_property(f,X,max_obstruction_size=None,ncpus=1): Same, on two threads:: - sage: sorted(list(subsets_with_hereditary_property(f,range(4),ncpus=2))) + sage: sorted(subsets_with_hereditary_property(f,range(4),ncpus=2)) [[], [0], [0, 2], [1], [1, 3], [2], [3]] One can use this function to compute the independent sets of a graph. We diff --git a/src/sage/combinat/subsets_pairwise.py b/src/sage/combinat/subsets_pairwise.py index 7c6adde7eec..b126c5cf223 100644 --- a/src/sage/combinat/subsets_pairwise.py +++ b/src/sage/combinat/subsets_pairwise.py @@ -10,11 +10,11 @@ from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.sets.set import Set_object_enumerated -from sage.combinat.backtrack import SearchForest +from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest from sage.combinat.subset import Subsets -class PairwiseCompatibleSubsets(SearchForest): +class PairwiseCompatibleSubsets(RecursivelyEnumeratedSet_forest): r""" The set of all subsets of ``ambient`` whose elements satisfy ``predicate`` pairwise @@ -105,7 +105,7 @@ def __init__(self, ambient, predicate, maximal = False, element_class = Set_obje # TODO: use self.element_class for consistency # At this point (2011/03) TestSuite fails if we do so self._element_class = element_class - SearchForest.__init__(self, algorithm = 'depth', category = FiniteEnumeratedSets()) + RecursivelyEnumeratedSet_forest.__init__(self, algorithm = 'depth', category = FiniteEnumeratedSets()) def __eq__(self, other): """ diff --git a/src/sage/combinat/subword.py b/src/sage/combinat/subword.py index 0d503c1c722..90aa323d986 100644 --- a/src/sage/combinat/subword.py +++ b/src/sage/combinat/subword.py @@ -56,7 +56,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range import itertools diff --git a/src/sage/combinat/subword_complex.py b/src/sage/combinat/subword_complex.py index bafd060a272..0027f5229eb 100644 --- a/src/sage/combinat/subword_complex.py +++ b/src/sage/combinat/subword_complex.py @@ -113,7 +113,6 @@ # **************************************************************************** # python3 from __future__ import division, print_function -from six.moves import range from copy import copy from sage.misc.cachefunc import cached_method @@ -714,7 +713,7 @@ def flip(self, i, return_position=False): ((2, 3), 3) """ S = self.parent() - F = set(list(self)) + F = set(self) R = list(self._extended_root_configuration_indices()) j = _flip_c(self.parent().group(), F, R, i) # F and R are changed here new_facet = S.element_class(self.parent(), F) diff --git a/src/sage/combinat/superpartition.py b/src/sage/combinat/superpartition.py index 3c65b5f8373..4d58f25a722 100644 --- a/src/sage/combinat/superpartition.py +++ b/src/sage/combinat/superpartition.py @@ -74,7 +74,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import, division -from six import add_metaclass from functools import reduce @@ -92,8 +91,8 @@ @richcmp_method -@add_metaclass(InheritComparisonClasscallMetaclass) -class SuperPartition(ClonableArray): +class SuperPartition(ClonableArray, + metaclass=InheritComparisonClasscallMetaclass): r""" A super partition. diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index ff4b003c9bc..131f7eabe6a 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -9,8 +9,6 @@ # **************************************************************************** from __future__ import print_function, absolute_import import itertools -import six -from six.moves import range from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute @@ -820,7 +818,7 @@ def retract_plain(self, f, m): I = RSm.group() pairs = [] P = Permutations(self.n) - for (p, coeff) in six.iteritems(f.monomial_coefficients()): + for (p, coeff) in f.monomial_coefficients().items(): p_ret = P(p).retract_plain(m) if p_ret is not None: pairs.append((I(p_ret), coeff)) @@ -886,7 +884,7 @@ def retract_direct_product(self, f, m): I = RSm.group() dct = {} P = Permutations(self.n) - for (p, coeff) in six.iteritems(f.monomial_coefficients()): + for (p, coeff) in f.monomial_coefficients().items(): p_ret = P(p).retract_direct_product(m) if p_ret is not None: p_ret = I(p_ret) @@ -949,7 +947,7 @@ def retract_okounkov_vershik(self, f, m): I = RSm.group() dct = {} P = Permutations(self.n) - for (p, coeff) in six.iteritems(f.monomial_coefficients()): + for (p, coeff) in f.monomial_coefficients().items(): p_ret = I(P(p).retract_okounkov_vershik(m)) if not p_ret in dct: dct[p_ret] = coeff @@ -1966,7 +1964,7 @@ def epsilon_ik(self, itab, ktab, star=0, mult='l2r'): I = self._indices z_elts = {} epik = epsilon_ik(it, kt, star=star) - for m, c in six.iteritems(epik._monomial_coefficients): + for m, c in epik._monomial_coefficients.items(): z_elts[I(m)] = BR(c) z = self._from_dict(z_elts) diff --git a/src/sage/combinat/symmetric_group_representations.py b/src/sage/combinat/symmetric_group_representations.py index d3c2495e446..554b161bf30 100644 --- a/src/sage/combinat/symmetric_group_representations.py +++ b/src/sage/combinat/symmetric_group_representations.py @@ -28,8 +28,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -import six -from six.moves import range from sage.symbolic.ring import SR from sage.functions.all import sqrt @@ -570,7 +568,7 @@ def _word_dict(self): (2, 0, 1, -1, 0): (2, 4, 1, 3, 5)} """ word_dict = {} - for (v,t) in six.iteritems(self._tableau_dict): + for (v,t) in self._tableau_dict.items(): word_dict[v] = sum(reversed(t), ()) return word_dict diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 6e3b55d2d33..59c23d38524 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -85,8 +85,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range, zip, map -from six import add_metaclass, text_type from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.sets.family import Family @@ -117,8 +115,7 @@ from sage.combinat.posets.posets import Poset @richcmp_method -@add_metaclass(InheritComparisonClasscallMetaclass) -class Tableau(ClonableList): +class Tableau(ClonableList, metaclass=InheritComparisonClasscallMetaclass): """ A class to model a tableau. @@ -605,7 +602,7 @@ def _ascii_art_table(self, use_unicode=False): if use_unicode: # Special handling of overline not adding to printed length def get_len(e): - return len(e) - list(text_type(e)).count(u"\u0304") + return len(e) - list(str(e)).count(u"\u0304") else: get_len = len for row in str_tab: @@ -788,8 +785,6 @@ def __truediv__(self, t): from sage.combinat.skew_tableau import SkewTableau return SkewTableau(st) - __div__ = __truediv__ - def __call__(self, *cell): r""" @@ -6500,9 +6495,9 @@ def random_element(self): from sage.rings.all import ZZ from sage.matrix.constructor import diagonal_matrix from sage.combinat.rsk import RSK - kchoose2m1 = self.max_entry * (self.max_entry - 1) / 2 - 1 + kchoose2m1 = self.max_entry * (self.max_entry - 1) // 2 - 1 km1 = self.max_entry - 1 - weights = [binomial(self.size - i + km1, km1) * binomial((i/2) + kchoose2m1, kchoose2m1) + weights = [binomial(self.size - i + km1, km1) * binomial((i//2) + kchoose2m1, kchoose2m1) for i in range(0, self.size + 1, 2)] randpos = ZZ.random_element(sum(weights)) tot = weights[0] diff --git a/src/sage/combinat/tableau_residues.py b/src/sage/combinat/tableau_residues.py index 67d6e13a413..b3e87317645 100644 --- a/src/sage/combinat/tableau_residues.py +++ b/src/sage/combinat/tableau_residues.py @@ -119,7 +119,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import, print_function -from six import add_metaclass from sage.categories.sets_cat import Sets from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass @@ -140,8 +139,8 @@ # ------------------------------------------------- # needed for __classcall_private__ -@add_metaclass(InheritComparisonClasscallMetaclass) -class ResidueSequence(ClonableArray): +class ResidueSequence(ClonableArray, + metaclass=InheritComparisonClasscallMetaclass): r""" A residue sequence. diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index b9fe5fba35c..49dec045a86 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -211,8 +211,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range -from six import add_metaclass from sage.combinat.combinat import CombinatorialElement from sage.combinat.words.word import Word @@ -1386,8 +1384,7 @@ def residue(self, k, e, multicharge): #-------------------------------------------------- # Row standard tableau tuple - element class #-------------------------------------------------- -@add_metaclass(ClasscallMetaclass) -class RowStandardTableauTuple(TableauTuple): +class RowStandardTableauTuple(TableauTuple, metaclass=ClasscallMetaclass): r""" A class for row standard tableau tuples of shape a partition tuple. diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py index acc46406754..3a70f1351a9 100644 --- a/src/sage/combinat/tiling.py +++ b/src/sage/combinat/tiling.py @@ -277,10 +277,6 @@ # **************************************************************************** from __future__ import division -from builtins import zip -from six import iteritems -from six.moves import range - import itertools from sage.structure.sage_object import SageObject from sage.modules.free_module_element import vector @@ -447,7 +443,8 @@ def ncube_isometry_group_cosets(n, orientation_preserving=True): G_todo = set(G) # Make sure that H is a subset of G - for h in H: h.set_immutable() + for h in H: + h.set_immutable() assert all(h in G_todo for h in H), "H must be a subset of G" # Construct the cosets @@ -479,6 +476,9 @@ class Polyomino(SageObject): - ``coords`` -- iterable of integer coordinates in `\ZZ^d` - ``color`` -- string (default: ``'gray'``), color for display + - ``dimension`` -- integer (default: ``None``), dimension of the space, + if ``None``, it is guessed from the ``coords`` if ``coords`` is non + empty EXAMPLES:: @@ -486,7 +486,7 @@ class Polyomino(SageObject): sage: Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue """ - def __init__(self, coords, color='gray'): + def __init__(self, coords, color='gray', dimension=None): r""" Constructor. @@ -500,9 +500,14 @@ def __init__(self, coords, color='gray'): :: - sage: from sage.combinat.tiling import Polyomino sage: Polyomino([(0,0), (1,0), (2,0)]) Polyomino: [(0, 0), (1, 0), (2, 0)], Color: gray + + TESTS:: + + sage: Polyomino([], dimension=2) + Polyomino: [], Color: gray + """ from sage.modules.free_module import FreeModule from sage.rings.integer_ring import ZZ @@ -513,10 +518,15 @@ def __init__(self, coords, color='gray'): if not isinstance(coords, (tuple,list)): coords = list(coords) - if not coords: - raise ValueError("Polyomino must be non empty") - self._dimension = ZZ(len(coords[0])) + if dimension is None: + if coords: + self._dimension = ZZ(len(coords[0])) + else: + raise ValueError("dimension(={}) must be provided for" + " the empty polyomino".format(dimension)) + else: + self._dimension = dimension self._free_module = FreeModule(ZZ, self._dimension) self._blocs = coords @@ -539,9 +549,14 @@ def _repr_(self): s += "Color: %s" % self._color return s - def color(self): + def color(self, color=None): r""" - Return the color of the polyomino. + Return or change the color of the polyomino. + + INPUT: + + - ``color`` -- string, RBG tuple or ``None`` (default: ``None``), + if ``None``, it returns the current color EXAMPLES:: @@ -550,7 +565,10 @@ def color(self): sage: p.color() 'blue' """ - return self._color + if color is None: + return self._color + else: + self._color = color def frozenset(self): r""" @@ -615,9 +633,8 @@ def bounding_box(self): sage: p.bounding_box() [[0, 0, 0], [1, 2, 1]] """ - zipped_coords = list(zip(*self)) - return [[min(_) for _ in zipped_coords], - [max(_) for _ in zipped_coords]] + return [[min(_) for _ in zip(*self)], + [max(_) for _ in zip(*self)]] def __hash__(self): r""" @@ -632,7 +649,7 @@ def __hash__(self): def __eq__(self, other): r""" - Return whether self is equal to other. + Return whether ``self`` is equal to ``other``. INPUT: @@ -657,7 +674,7 @@ def __eq__(self, other): def __ne__(self, other): r""" - Return whether self is not equal to other. + Return whether ``self`` is not equal to ``other``. INPUT: @@ -682,7 +699,7 @@ def __ne__(self, other): def __le__(self, other): r""" - Return whether self is inside of other. + Return whether ``self`` is inside of ``other``. INPUT: @@ -706,7 +723,7 @@ def __le__(self, other): def __ge__(self, other): r""" - Return whether self contains other. + Return whether ``self`` contains ``other``. INPUT: @@ -728,9 +745,84 @@ def __ge__(self, other): """ return isinstance(other, Polyomino) and self.frozenset() >= other.frozenset() + def __lt__(self, other): + r""" + Return whether ``self`` is strictly inside of ``other``. + + INPUT: + + - ``other`` - a polyomino + + OUTPUT: + + boolean + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0)]) + sage: b = Polyomino([(0,0), (0,1), (1,1), (2,1)]) + sage: p < b + True + sage: b < p + False + """ + return isinstance(other, Polyomino) and self.frozenset() < other.frozenset() + + def __gt__(self, other): + r""" + Return whether ``self`` strictly contains ``other``. + + INPUT: + + - ``other`` - a polyomino + + OUTPUT: + + boolean + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0)]) + sage: b = Polyomino([(0,0), (0,1), (1,1), (2,1)]) + sage: p > b + False + sage: b > p + True + """ + return isinstance(other, Polyomino) and self.frozenset() > other.frozenset() + + def intersection(self, other): + r""" + Return the intersection of ``self`` and ``other``. + + INPUT: + + - ``other`` - a polyomino + + OUTPUT: + + polyomino + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: a = Polyomino([(0,0)]) + sage: b = Polyomino([(0,0), (0,1), (1,1), (2,1)]) + sage: a.intersection(b) + Polyomino: [(0, 0)], Color: gray + sage: a.intersection(b+(1,1)) + Polyomino: [], Color: gray + """ + if not isinstance(other, Polyomino): + raise TypeError("other(={}) must be a polyomino".format(other)) + return Polyomino(self.frozenset() & other.frozenset(), + color=self._color, dimension=self._dimension) + def __sub__(self, v): r""" - Return a translated copy of self by the opposite of the + Return a translated copy of ``self`` by the opposite of the vector v. INPUT: @@ -753,7 +845,7 @@ def __sub__(self, v): def __add__(self, v): r""" - Return a translated copy of self by the vector v. + Return a translated copy of ``self`` by the vector v. INPUT: @@ -812,7 +904,7 @@ def __rmul__(self, m): def canonical(self): r""" - Return the translated copy of self having minimal and nonnegative + Return the translated copy of ``self`` having minimal and nonnegative coordinates EXAMPLES:: @@ -839,14 +931,14 @@ def canonical(self): def canonical_isometric_copies(self, orientation_preserving=True, mod_box_isometries=False): r""" - Return the list of image of self under isometries of the `n`-cube + Return the list of image of ``self`` under isometries of the `n`-cube where the coordinates are all nonnegative and minimal. INPUT: - - ``orientation_preserving`` -- bool (optional, default: ``True``), - If True, the group of isometries of the `n`-cube is restricted to - those that preserve the orientation, i.e. of determinant 1. + - ``orientation_preserving`` -- bool (optional, default: ``True``); + if ``True``, the group of isometries of the `n`-cube is restricted + to those that preserve the orientation, i.e. of determinant 1. - ``mod_box_isometries`` -- bool (default: ``False``), whether to quotient the group of isometries of the `n`-cube by the @@ -897,7 +989,7 @@ def canonical_isometric_copies(self, orientation_preserving=True, def translated_copies(self, box): r""" - Return an iterator over the translated images of self inside a + Return an iterator over the translated images of ``self`` inside a polyomino. INPUT: @@ -998,18 +1090,76 @@ def translated_copies(self, box): if translated <= box: yield translated + def translated_copies_intersection(self, box): + r""" + Return the set of non empty intersections of translated images of + ``self`` with a polyomino. + + INPUT: + + - ``box`` -- Polyomino or tuple of integers (size of a box) + + OUTPUT: + + set of 3d polyominoes + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0),(1,0)], color='deeppink') + sage: sorted(sorted(a.frozenset()) for a in p.translated_copies_intersection(box=(2,3))) + [[(0, 0)], + [(0, 0), (1, 0)], + [(0, 1)], + [(0, 1), (1, 1)], + [(0, 2)], + [(0, 2), (1, 2)], + [(1, 0)], + [(1, 1)], + [(1, 2)]] + + Using a Polyomino as input:: + + sage: b = Polyomino([(0,0), (0,1), (0,2), (1,0), (2,0)]) + sage: p = Polyomino([(0,0), (1,0)]) + sage: sorted(sorted(a.frozenset()) for a in p.translated_copies_intersection(b)) + [[(0, 0)], [(0, 0), (1, 0)], [(0, 1)], [(0, 2)], [(1, 0), (2, 0)], [(2, 0)]] + + """ + if not isinstance(box, Polyomino): + ranges = [range(a) for a in box] + box = Polyomino(itertools.product(*ranges)) + if not box._dimension == self._dimension: + raise ValueError("Dimension of input box must match the " + "dimension of the polyomino") + minxyz, maxxyz = self.bounding_box() + minxyz, maxxyz = vector(minxyz), vector(maxxyz) + size = maxxyz - minxyz + boxminxyz, boxmaxxyz = box.bounding_box() + ranges = [range(a-c, b+1) for (a,b,c) in zip(boxminxyz, + boxmaxxyz, + size)] + S = set() + cano = self.canonical() + for v in itertools.product(*ranges): + translated = cano + v + intersected = translated.intersection(box) + if intersected: + S.add(intersected) + return S + def isometric_copies(self, box, orientation_preserving=True, mod_box_isometries=False): r""" - Return the translated and isometric images of self that lies in the box. + Return the translated and isometric images of ``self`` that lies in the box. INPUT: - ``box`` -- Polyomino or tuple of integers (size of a box) - - ``orientation_preserving`` -- bool (optional, default: ``True``), - If True, the group of isometries of the `n`-cube is restricted to - those that preserve the orientation, i.e. of determinant 1. + - ``orientation_preserving`` -- bool (optional, default: ``True``); + If ``True``, the group of isometries of the `n`-cube is restricted + to those that preserve the orientation, i.e. of determinant 1. - ``mod_box_isometries`` -- bool (default: ``False``), whether to quotient the group of isometries of the `n`-cube by the @@ -1056,9 +1206,9 @@ def isometric_copies(self, box, orientation_preserving=True, raise ValueError("Dimension of input box must match the " "dimension of the polyomino") box_min_coords, box_max_coords = box.bounding_box() - if mod_box_isometries and len(set(b-a for (a,b) in zip(box_min_coords, + if mod_box_isometries and len(set(b-a for (a,b) in zip(box_min_coords, box_max_coords))) < box._dimension: - raise NotImplementedError("The code below assumes that the" + raise NotImplementedError("The code below assumes that the" " sizes of the box (={}) are all distinct when" " argument `mod_box_isometries` is True.".format(box)) all_distinct_cano = self.canonical_isometric_copies(orientation_preserving, @@ -1067,6 +1217,44 @@ def isometric_copies(self, box, orientation_preserving=True, for t in cano.translated_copies(box=box): yield t + def isometric_copies_intersection(self, box, orientation_preserving=True): + r""" + Return the set of non empty intersections of isometric images of + ``self`` with a polyomino. + + INPUT: + + - ``box`` -- Polyomino or tuple of integers (size of a box) + + - ``orientation_preserving`` -- bool (optional, default: ``True``); + if ``True``, the group of isometries of the `n`-cube is restricted + to those that preserve the orientation, i.e. of determinant 1. + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0),(1,0)], color='deeppink') + sage: sorted(sorted(a.frozenset()) for a in p.isometric_copies_intersection(box=(2,3))) + [[(0, 0)], + [(0, 0), (0, 1)], + [(0, 0), (1, 0)], + [(0, 1)], + [(0, 1), (0, 2)], + [(0, 1), (1, 1)], + [(0, 2)], + [(0, 2), (1, 2)], + [(1, 0)], + [(1, 0), (1, 1)], + [(1, 1)], + [(1, 1), (1, 2)], + [(1, 2)]] + + """ + all_distinct_cano = self.canonical_isometric_copies(orientation_preserving, + mod_box_isometries=False) + return set([t for cano in all_distinct_cano + for t in cano.translated_copies_intersection(box=box)]) + def neighbor_edges(self): r""" Return an iterator over the pairs of neighbor coordinates inside of @@ -1172,10 +1360,10 @@ def boundary(self): vertical[(x+1, y)] -= 1 edges = [] h = 0.5 - for (x, y), coeff in iteritems(horizontal): + for (x, y), coeff in horizontal.items(): if coeff: edges.append(((x-h, y-h), (x+h, y-h))) - for (x, y), coeff in iteritems(vertical): + for (x, y), coeff in vertical.items(): if coeff: edges.append(((x-h, y-h), (x-h, y+h))) return edges @@ -1250,6 +1438,90 @@ def show2d(self, size=0.7, color='black', thickness=1): return G + def self_surrounding(self, radius, remove_incomplete_copies=True, + ncpus=None): + r""" + Return a list of isometric copies of ``self`` surrounding it with an + annulus of given radius. + + INPUT: + + - ``self`` - a polyomino of dimension 2 + - ``radius`` - integer + - ``remove_incomplete_copies`` -- bool (default: ``True``), whether + to keep only complete copies of ``self`` in the output + - ``ncpus`` -- integer (default: ``None``), maximal number of + subprocesses to use at the same time. If ``None``, it detects the + number of effective CPUs in the system using + :func:`sage.parallel.ncpus.ncpus()`. + If ``ncpus=1``, the first solution is searched serially. + + OUTPUT: + + list of polyominoes + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: H = Polyomino([(-1, 1), (-1, 4), (-1, 7), (0, 0), (0, 1), (0, 2), + ....: (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (1, 1), (1, 2), + ....: (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (2, 0), (2, 2), + ....: (2, 3), (2, 5), (2, 6), (2, 8)]) + sage: solution = H.self_surrounding(8) + sage: G = sum([p.show2d() for p in solution], Graphics()) + + :: + + sage: solution = H.self_surrounding(8, remove_incomplete_copies=False) + sage: G = sum([p.show2d() for p in solution], Graphics()) + + """ + # Define the box to tile + minxyz, maxxyz = self.bounding_box() + minxyz, maxxyz = vector(minxyz), vector(maxxyz) + v = vector([radius for _ in range(self._dimension)]) + ranges = [range(a,b) for a,b in zip(minxyz-v, maxxyz+v)] + box = Polyomino(itertools.product(*ranges)) + + # Get the rows for this problem + T = TilingSolver([self], box=box, reusable=True, + reflection=True, rotation=True, outside=True) + rows = T.rows() + + # Add one row to force the placement of the central tile + coord_to_int = T.coord_to_int_dict() + new_row = [coord_to_int[coord] for coord in self] + new_row.append(len(coord_to_int)) # to force this row in the solution + forced_row_number = len(rows) + rows.append(new_row) + + # Construct the dancing links solver + from sage.combinat.matrices.dancing_links import dlx_solver + d = dlx_solver(rows) + + # Solve + solution = d.one_solution(ncpus=ncpus) + if solution is None: + raise ValueError('No solution was found with radius={}, ' + 'this tile can not be surrounded by itself'.format(radius)) + + # Recover the polyominoes + assert forced_row_number in solution + solution.remove(forced_row_number) + polyominoes = [T.row_to_polyomino(v) for v in solution] + if remove_incomplete_copies: + polyominoes = [p for p in polyominoes if len(p) == len(self)] + + # Recolor randomly the polyominoes + from sage.plot.colors import Color + from random import random + for p in polyominoes: + random_color = Color(tuple(random() for _ in range(3))) + p.color(random_color) + + return polyominoes + + ####################### # General tiling solver ####################### @@ -1257,9 +1529,8 @@ class TilingSolver(SageObject): r""" Tiling solver - Solve the problem of tiling a rectangular box with a certain number - of pieces, called polyominoes, where each polyomino must be used - exactly once. + Solve the problem of tiling a polyomino with a certain number + of polyominoes. INPUT: @@ -1271,6 +1542,9 @@ class TilingSolver(SageObject): reflections - ``reusable`` -- bool (optional, default: ``False``), whether to allow the pieces to be reused + - ``outside`` -- bool (optional, default: ``False``), whether to allow + pieces to partially go outside of the box (all non-empty intersection + of the pieces with the box are considered) EXAMPLES: @@ -1318,7 +1592,7 @@ class TilingSolver(SageObject): NotImplementedError: When reflection is allowed and rotation is not allowed """ def __init__(self, pieces, box, rotation=True, - reflection=False, reusable=False): + reflection=False, reusable=False, outside=False): r""" Constructor. @@ -1348,6 +1622,7 @@ def __init__(self, pieces, box, rotation=True, raise NotImplementedError("When reflection is allowed and " "rotation is not allowed") self._reusable = reusable + self._outside = outside def _repr_(self): r""" @@ -1576,7 +1851,11 @@ def rows_for_piece(self, i, mod_box_isometries=False): orientation_preserving = False else: orientation_preserving = True - it = p.isometric_copies(self._box, + if self._outside: + it = p.isometric_copies_intersection(self._box, + orientation_preserving=orientation_preserving) + else: + it = p.isometric_copies(self._box, orientation_preserving=orientation_preserving, mod_box_isometries=mod_box_isometries) else: @@ -1584,7 +1863,10 @@ def rows_for_piece(self, i, mod_box_isometries=False): raise NotImplementedError("Reflection allowed, Rotation not " "allowed is not implemented") else: - it = p.translated_copies(self._box) + if self._outside: + it = p.translated_copies_intersection(self._box) + else: + it = p.translated_copies(self._box) coord_to_int = self.coord_to_int_dict() rows = [] for q in it: diff --git a/src/sage/combinat/tools.py b/src/sage/combinat/tools.py index 1950b952be8..63c611581c8 100644 --- a/src/sage/combinat/tools.py +++ b/src/sage/combinat/tools.py @@ -1,5 +1,5 @@ r""" -Transitive ideal closure tool. +Transitive ideal closure tool """ # **************************************************************************** # Copyright (C) 2007 Mike Hansen , @@ -13,7 +13,7 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # **************************************************************************** diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index 05308b2cc58..a5bb1f31de2 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -222,7 +222,7 @@ author, which contains more than 190000 sequences of integers:: sage: oeis([1,1,2,5,14]) # optional -- internet - 0: A000108: Catalan numbers: C(n) = binomial(2n,n)/(n+1) = (2n)!/(n!(n+1)!). Also called Segner numbers. + 0: A000108: Catalan numbers: ... 1: ... 2: ... @@ -1731,8 +1731,8 @@ sage: oeis(L) # optional -- internet 0: A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. - 1: A212804: Expansion of (1-x)/(1-x-x^2). - 2: A132636: a(n) = Fibonacci(n) mod n^3. + 1: ... + 2: ... This is an immediate consequence of the recurrence relation. One can also generate immediately all the Fibonacci words of a given length, diff --git a/src/sage/combinat/words/abstract_word.py b/src/sage/combinat/words/abstract_word.py index 0c35589693e..6168f6cb854 100644 --- a/src/sage/combinat/words/abstract_word.py +++ b/src/sage/combinat/words/abstract_word.py @@ -31,9 +31,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from six.moves import range - -from builtins import zip from sage.structure.sage_object import SageObject from sage.combinat.words.word_options import word_options diff --git a/src/sage/combinat/words/alphabet.py b/src/sage/combinat/words/alphabet.py index 70f0a3528bf..0219633ed9a 100644 --- a/src/sage/combinat/words/alphabet.py +++ b/src/sage/combinat/words/alphabet.py @@ -32,8 +32,6 @@ # http://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six.moves import range -from six import integer_types from sage.categories.sets_cat import Sets @@ -217,12 +215,12 @@ def build_alphabet(data=None, names=None, name=None): raise ValueError("name cannot be specified with any other argument") # Swap arguments if we need to try and make sure we have "good" user input - if isinstance(names, integer_types + (Integer,)) or names == Infinity \ + if isinstance(names, (int, Integer)) or names == Infinity \ or (data is None and names is not None): data, names = names, data # data is an integer - if isinstance(data, integer_types + (Integer,)): + if isinstance(data, (int, Integer)): if names is None: from sage.sets.integer_range import IntegerRange return IntegerRange(Integer(data)) diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index a3bdb3d4339..dcf0df931cf 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -216,10 +216,6 @@ #***************************************************************************** from __future__ import print_function, absolute_import -from builtins import zip - -from six import iteritems -from six.moves import range from collections import defaultdict from itertools import islice, cycle from sage.combinat.words.abstract_word import Word_class @@ -3551,7 +3547,7 @@ def critical_exponent(self): current_exp = QQ((current_pos+1, current_pos+1-m)) if current_exp > best_exp: best_exp = current_exp - for ((i,j),u) in iteritems(st._transition_function[v]): + for ((i,j),u) in st._transition_function[v].items(): if j is None: j = self.length() queue.append((u, i, j, l+j-i+1)) @@ -4751,8 +4747,8 @@ def is_quasiperiodic(self): return False for i in range(1, l - 1): return_lengths = [x.length() for x in self.return_words(self[:i])] - if return_lengths != []: - if (max(return_lengths) <= i and self[l-i:l] == self[:i]): + if return_lengths: + if max(return_lengths) <= i and self[l-i:l] == self[:i]: return True return False @@ -4868,7 +4864,7 @@ def evaluation_sparse(self): sage: sorted(Word("abcaccab").evaluation_sparse()) [('a', 3), ('b', 2), ('c', 3)] """ - return list(iteritems(self.evaluation_dict())) + return list(self.evaluation_dict().items()) def evaluation_partition(self): r""" @@ -6762,8 +6758,8 @@ def colored_vector(self, x=0, y=0, width='default', height=1, cmap='hsv', thickn if key_error or not isinstance(mpl_cmap, C): possibilities = ', '.join(str(x) for x, val in cm.__dict__.items() if isinstance(val, C)) - import sage.misc.misc - sage.misc.misc.verbose("The possible color maps include: %s" % possibilities, level=0) + import sage.misc.verbose + sage.misc.verbose.verbose("The possible color maps include: %s" % possibilities, level=0) raise RuntimeError("Color map %s not known" % cmap) #Drawing the colored vector... diff --git a/src/sage/combinat/words/lyndon_word.py b/src/sage/combinat/words/lyndon_word.py index 26c9f32268c..a0539d49685 100644 --- a/src/sage/combinat/words/lyndon_word.py +++ b/src/sage/combinat/words/lyndon_word.py @@ -12,15 +12,13 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import -from six.moves import builtins from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.combinat.composition import Composition, Compositions from sage.rings.all import Integer -from sage.arith.all import factorial, divisors, gcd, moebius -from sage.misc.all import prod +from sage.arith.all import divisors, gcd, moebius, multinomial from sage.combinat.necklace import _sfc from sage.combinat.words.words import FiniteWords @@ -29,24 +27,26 @@ def LyndonWords(e=None, k=None): """ - Returns the combinatorial class of Lyndon words. + Return the combinatorial class of Lyndon words. A Lyndon word `w` is a word that is lexicographically less than all of its rotations. Equivalently, whenever `w` is split into two non-empty substrings, `w` is lexicographically less than the right substring. + See :wikipedia:`Lyndon_word` + INPUT: - no input at all or - - ``e`` - integer, size of alphabet - - ``k`` - integer, length of the words + - ``e`` -- integer, size of alphabet + - ``k`` -- integer, length of the words or - - ``e`` - a composition + - ``e`` -- a composition OUTPUT: @@ -95,15 +95,16 @@ def LyndonWords(e=None, k=None): raise TypeError("e must be a positive integer or a composition") + def LyndonWord(data, check=True): r""" Construction of a Lyndon word. INPUT: - - ``data`` - list - - ``check`` - bool (optional, default: True) if True, a - verification that the input data represent a Lyndon word. + - ``data`` -- list + - ``check`` -- bool (optional, default: ``True``) if ``True``, + check that the input data represents a Lyndon word. OUTPUT: @@ -120,13 +121,14 @@ def LyndonWord(data, check=True): ... ValueError: not a Lyndon word - If check is False, then no verification is done:: + If ``check`` is ``False``, then no verification is done:: sage: LyndonWord([2,1,2,3], check=False) word: 2123 """ return LyndonWords()(data, check=check) + class LyndonWords_class(UniqueRepresentation, Parent): r""" The set of all Lyndon words. @@ -186,6 +188,7 @@ def __contains__(self, w): w = self._words(w) return w.is_lyndon() + class LyndonWords_evaluation(UniqueRepresentation, Parent): r""" The set of Lyndon words on a fixed multiset of letters. @@ -223,7 +226,7 @@ def __repr__(self): sage: repr(LyndonWords([2,1,1])) 'Lyndon words with evaluation [2, 1, 1]' """ - return "Lyndon words with evaluation %s"%self._e + return "Lyndon words with evaluation %s" % self._e def __call__(self, *args, **kwds): r""" @@ -265,7 +268,7 @@ def __contains__(self, x): def cardinality(self): """ - Returns the number of Lyndon words with the evaluation e. + Return the number of Lyndon words with the evaluation e. EXAMPLES:: @@ -277,9 +280,7 @@ def cardinality(self): 30 Check to make sure that the count matches up with the number of - Lyndon words generated. - - :: + Lyndon words generated:: sage: comps = [[],[2,2],[3,2,7],[4,2]] + Compositions(4).list() sage: lws = [LyndonWords(comp) for comp in comps] @@ -287,13 +288,12 @@ def cardinality(self): True """ evaluation = self._e - le = builtins.list(evaluation) - if len(evaluation) == 0: - return 0 - + le = list(evaluation) + if not evaluation: + return Integer(0) n = sum(evaluation) - - return sum([moebius(j)*factorial(n/j) / prod([factorial(ni/j) for ni in evaluation]) for j in divisors(gcd(le))])/n + return sum(moebius(j) * multinomial([ni // j for ni in evaluation]) + for j in divisors(gcd(le))) // n def __iter__(self): """ @@ -331,19 +331,16 @@ def __iter__(self): """ if not self._e: return - k = 0 while self._e[k] == 0: k += 1 - for z in _sfc(self._e[k:], equality=True): - yield self._words([i+k+1 for i in z], check=False) + yield self._words([i + k + 1 for i in z], check=False) class LyndonWords_nk(UniqueRepresentation, Parent): r""" - Lyndon words of fixed length `k` over the alphabet - `\{1, 2, \ldots, n\}`. + Lyndon words of fixed length `k` over the alphabet `\{1, 2, \ldots, n\}`. INPUT: @@ -392,7 +389,7 @@ def __repr__(self): sage: repr(LyndonWords(2, 3)) 'Lyndon words from an alphabet of size 2 of length 3' """ - return "Lyndon words from an alphabet of size %s of length %s"%(self._n, self._k) + return "Lyndon words from an alphabet of size %s of length %s" % (self._n, self._k) def __call__(self, *args, **kwds): r""" @@ -445,8 +442,8 @@ def cardinality(self): else: s = Integer(0) for d in divisors(self._k): - s += moebius(d)*(self._n**(self._k/d)) - return s/self._k + s += moebius(d) * self._n**(self._k // d) + return s // self._k def __iter__(self): """ @@ -469,14 +466,17 @@ def __iter__(self): """ W = self._words._element_classes['list'] for lw in lyndon_word_iterator(self._n, self._k): - yield W(self._words, [i+1 for i in lw]) + yield W(self._words, [i + 1 for i in lw]) + def StandardBracketedLyndonWords(n, k): """ - Returns the combinatorial class of standard bracketed Lyndon words - from [1, ..., n] of length k. These are in one to one - correspondence with the Lyndon words and form a basis for the - subspace of degree k of the free Lie algebra of rank n. + Return the combinatorial class of standard bracketed Lyndon words + from [1, ..., n] of length k. + + These are in one to one correspondence with the Lyndon words and + form a basis for the subspace of degree k of the free Lie algebra + of rank n. EXAMPLES:: @@ -581,4 +581,3 @@ def standard_bracketing(lw): for i in range(1, len(lw)): if lw[i:] in LyndonWords(): return [standard_bracketing(lw[:i]), standard_bracketing(lw[i:])] - diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index e9185d24973..7c5128f9f7f 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -90,8 +90,7 @@ # **************************************************************************** from __future__ import print_function -from six.moves import range -import itertools +from itertools import chain from sage.misc.callable_dict import CallableDict from sage.structure.sage_object import SageObject @@ -1793,7 +1792,7 @@ def _fixed_point_iterator(self, letter): yield a else: next_w = next(w) - w = itertools.chain([next_w], w, self.image(next_w)) + w = chain([next_w], w, self.image(next_w)) except StopIteration: return diff --git a/src/sage/combinat/words/shuffle_product.py b/src/sage/combinat/words/shuffle_product.py index 41cad2aad39..2f9bf62ed2c 100644 --- a/src/sage/combinat/words/shuffle_product.py +++ b/src/sage/combinat/words/shuffle_product.py @@ -57,18 +57,18 @@ def __init__(self, w1, w2): sage: from sage.combinat.words.shuffle_product import ShuffleProduct_w1w2 sage: W = Words([1,2,3,4]) sage: s = ShuffleProduct_w1w2(W([1,2]),W([3,4])) - sage: sorted(list(s)) + sage: sorted(s) [word: 1234, word: 1324, word: 1342, word: 3124, word: 3142, word: 3412] sage: s == loads(dumps(s)) True sage: s = ShuffleProduct_w1w2(W([1,4,3]),W([2])) - sage: sorted(list(s)) + sage: sorted(s) [word: 1243, word: 1423, word: 1432, word: 2143] sage: s = ShuffleProduct_w1w2(W([1,4,3]),W([])) - sage: sorted(list(s)) + sage: sorted(s) [word: 143] """ self._w1 = w1 diff --git a/src/sage/combinat/words/suffix_trees.py b/src/sage/combinat/words/suffix_trees.py index 325c33220f6..9a61e4e4b36 100644 --- a/src/sage/combinat/words/suffix_trees.py +++ b/src/sage/combinat/words/suffix_trees.py @@ -10,8 +10,6 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from six.moves import range -from six import iteritems from itertools import chain from sage.structure.sage_object import SageObject @@ -208,7 +206,7 @@ def node_to_word(self, state=0): if state == 0: return Words(self._alphabet)() # We first invert the transition function - tf_inv = {b: a for a, b in iteritems(self._transition_function)} + tf_inv = {b: a for a, b in self._transition_function.items()} # Starting from the active state, # read labels along the unique path to the root. @@ -451,7 +449,7 @@ def to_digraph(self): [0 0 0 0 0 0] """ dag = {} - for ((u, letter), v) in iteritems(self._transition_function): + for ((u, letter), v) in self._transition_function.items(): dag.setdefault(u, {})[v] = letter return DiGraph(dag) @@ -756,7 +754,7 @@ def _find_transition(self, state, letter): return ((0, 0), 0) else: if state in self._transition_function: - for ((k,p),s) in iteritems(self._transition_function[state]): + for ((k,p),s) in self._transition_function[state].items(): if self._letters[k-1] == letter: return ((k,p), s) return None @@ -835,12 +833,12 @@ def to_digraph(self, word_labels=False): sage: t.to_digraph() Digraph on 8 vertices """ - if self._letters == []: - d = {0:{}} + if not self._letters: + d = {0: {}} return DiGraph(d) d = self.transition_function_dictionary() for u in d: - for (v, (i, j)) in iteritems(d[u]): + for (v, (i, j)) in d[u].items(): if word_labels: d[u][v] = self._word[i:j] elif j is None: @@ -1124,7 +1122,7 @@ def edge_iterator(self): queue = [0] while queue: v = queue.pop() - for ((i,j),u) in iteritems(self._transition_function[v]): + for ((i,j),u) in self._transition_function[v].items(): yield (v,u,(i-1,j)) queue.append(u) @@ -1204,7 +1202,7 @@ def number_of_factors(self,n=None): num_factors += 1 if l < n: if self._transition_function[v] != {}: - for ((i,j),u) in iteritems(self._transition_function[v]): + for ((i,j),u) in self._transition_function[v].items(): if j is None: j = self.word().length() if j - i >= n - l: @@ -1260,7 +1258,7 @@ def factor_iterator(self,n=None): (v,i,j,l) = queue.pop() for k in range(i,j+1): yield w[j-l:k] - for ((i,j),u) in iteritems(self._transition_function[v]): + for ((i,j),u) in self._transition_function[v].items(): if j is None: j = wlen queue.append((u,i,j, l+j-i+1)) @@ -1271,7 +1269,7 @@ def factor_iterator(self,n=None): if l == n: yield w[j-l:j] if l < n: - for ((i,j),u) in iteritems(self._transition_function[v]): + for ((i,j),u) in self._transition_function[v].items(): if j is None: j = wlen if j - i >= n - l: @@ -1547,8 +1545,8 @@ def trie_type_dict(self): """ d = {} new_node = len(self._transition_function) - for (u, dd) in iteritems(self._transition_function): - for (sl, v) in iteritems(dd): + for (u, dd) in self._transition_function.items(): + for (sl, v) in dd.items(): w = self._word[sl[0]-1:sl[1]] if w.length() == 1: d[u,w] = v diff --git a/src/sage/combinat/words/word.py b/src/sage/combinat/words/word.py index b3d0fdf5957..21c442fe4ba 100644 --- a/src/sage/combinat/words/word.py +++ b/src/sage/combinat/words/word.py @@ -511,7 +511,7 @@ class InfiniteWord_iter_with_caching(WordDatatype_iter_with_caching, InfiniteWor sage: dumps(w) Traceback (most recent call last): ... - TypeError: can't pickle generator objects + TypeError: can...t...pickle...generator...object... """ pass @@ -549,7 +549,7 @@ class InfiniteWord_iter(WordDatatype_iter, InfiniteWord_class): sage: dumps(w) Traceback (most recent call last): ... - TypeError: can't pickle generator objects + TypeError: can...t...pickle...generator...object... """ pass @@ -648,7 +648,7 @@ class Word_iter_with_caching(WordDatatype_iter_with_caching, Word_class): sage: dumps(w) Traceback (most recent call last): ... - TypeError: can't pickle generator objects + TypeError: can...t...pickle...generator...object... """ pass @@ -684,7 +684,7 @@ class Word_iter(WordDatatype_iter, Word_class): sage: dumps(w) Traceback (most recent call last): ... - TypeError: can't pickle generator objects + TypeError: can...t...pickle...generator...object... """ pass diff --git a/src/sage/combinat/words/word_generators.py b/src/sage/combinat/words/word_generators.py index 9e16021c9f1..6161961bda1 100644 --- a/src/sage/combinat/words/word_generators.py +++ b/src/sage/combinat/words/word_generators.py @@ -55,8 +55,6 @@ # **************************************************************************** from __future__ import print_function -from six.moves import range - from itertools import cycle, count from random import randint from sage.misc.cachefunc import cached_method diff --git a/src/sage/combinat/words/word_infinite_datatypes.py b/src/sage/combinat/words/word_infinite_datatypes.py index 25eaab9b686..078f00644d6 100644 --- a/src/sage/combinat/words/word_infinite_datatypes.py +++ b/src/sage/combinat/words/word_infinite_datatypes.py @@ -11,7 +11,6 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from sage.combinat.words.word_datatypes import WordDatatype from sage.rings.all import Infinity diff --git a/src/sage/combinat/yang_baxter_graph.py b/src/sage/combinat/yang_baxter_graph.py index f24ac2f9923..b3e56f7eead 100644 --- a/src/sage/combinat/yang_baxter_graph.py +++ b/src/sage/combinat/yang_baxter_graph.py @@ -15,7 +15,6 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** -from six.moves import range from sage.graphs.digraph import DiGraph from sage.structure.sage_object import SageObject diff --git a/src/sage/cpython/getattr.pyx b/src/sage/cpython/getattr.pyx index 0e4f9a49698..dae36199416 100644 --- a/src/sage/cpython/getattr.pyx +++ b/src/sage/cpython/getattr.pyx @@ -319,7 +319,7 @@ cpdef getattr_from_other_class(self, cls, name): Traceback (most recent call last): ... TypeError: descriptor '__weakref__' for 'A' objects doesn't apply - to 'sage.rings.integer.Integer' object + to ...'sage.rings.integer.Integer' object When this occurs, an ``AttributeError`` is raised:: @@ -411,7 +411,7 @@ cpdef getattr_from_other_class(self, cls, name): raise AttributeError(dummy_error_message) -def dir_with_other_class(self, cls): +def dir_with_other_class(self, *cls): r""" Emulates ``dir(self)``, as if self was also an instance ``cls``, right after ``caller_class`` in the method resolution order @@ -432,6 +432,10 @@ def dir_with_other_class(self, cls): sage: from sage.cpython.getattr import dir_with_other_class sage: dir_with_other_class(x, B) [..., 'a', 'b', 'c', 'd', 'e'] + sage: class C(object): + ....: f = 6 + sage: dir_with_other_class(x, B, C) + [..., 'a', 'b', 'c', 'd', 'e', 'f'] Check that objects without dicts are well handled:: @@ -459,6 +463,7 @@ def dir_with_other_class(self, cls): ret.update(dir(self.__class__)) if hasattr(self, "__dict__"): ret.update(list(self.__dict__)) - if not isinstance(self, cls): - ret.update(dir(cls)) + for c in cls: + if not isinstance(self, c): + ret.update(dir(c)) return sorted(ret) diff --git a/src/sage/cpython/string.pxd b/src/sage/cpython/string.pxd index d1eb6b502b9..3e4ce2a9e7e 100644 --- a/src/sage/cpython/string.pxd +++ b/src/sage/cpython/string.pxd @@ -10,8 +10,6 @@ from __future__ import absolute_import -from cpython.version cimport PY_MAJOR_VERSION - cdef extern from "string_impl.h": str _cstr_to_str(const char* c, encoding, errors) @@ -32,19 +30,14 @@ cpdef inline str bytes_to_str(b, encoding=None, errors=None): r""" Convert ``bytes`` to ``str``. - On Python 2 this is a no-op since ``bytes is str``. On Python 3 - this decodes the given ``bytes`` to a Python 3 unicode ``str`` using - the specified encoding. + This decodes the given ``bytes`` to a Python 3 unicode ``str`` using + the specified encoding. It is a no-op on ``str`` input. EXAMPLES:: - sage: import six sage: from sage.cpython.string import bytes_to_str sage: s = bytes_to_str(b'\xcf\x80') - sage: if six.PY2: - ....: s == b'\xcf\x80' - ....: else: - ....: s == u'π' + sage: s == u'π' True sage: bytes_to_str([]) Traceback (most recent call last): @@ -56,31 +49,20 @@ cpdef inline str bytes_to_str(b, encoding=None, errors=None): if type(b) is not bytes: raise TypeError(f"expected bytes, {type(b).__name__} found") - if PY_MAJOR_VERSION <= 2: - return b - else: - return _cstr_to_str(b, encoding, errors) + return _cstr_to_str(b, encoding, errors) cpdef inline bytes str_to_bytes(s, encoding=None, errors=None): r""" Convert ``str`` or ``unicode`` to ``bytes``. - On Python 3 this encodes the given ``str`` to a Python 3 ``bytes`` - using the specified encoding. - - On Python 2 this is a no-op on ``str`` input since ``str is bytes``. - However, this function also accepts Python 2 ``unicode`` objects and - treats them the same as Python 3 unicode ``str`` objects. + It encodes the given ``str`` to a Python 3 ``bytes`` + using the specified encoding. It is a no-op on ``bytes`` input. EXAMPLES:: - sage: import six sage: from sage.cpython.string import str_to_bytes - sage: if six.PY2: - ....: bs = [str_to_bytes('\xcf\x80'), str_to_bytes(u'π')] - ....: else: - ....: bs = [str_to_bytes(u'π')] + sage: bs = [str_to_bytes(u'π')] sage: all(b == b'\xcf\x80' for b in bs) True sage: str_to_bytes([]) diff --git a/src/sage/cpython/string_impl.h b/src/sage/cpython/string_impl.h index 6e3a48dca37..c0f78966908 100644 --- a/src/sage/cpython/string_impl.h +++ b/src/sage/cpython/string_impl.h @@ -15,9 +15,6 @@ static inline PyObject* _cstr_to_str(const char* c, PyObject* encoding, PyObject* errors) { -#if PY_MAJOR_VERSION <= 2 - return PyBytes_FromString(c); -#else const char* err = NULL; // Default: strict const char* enc = NULL; // Default: utf-8 @@ -32,27 +29,14 @@ static inline PyObject* _cstr_to_str(const char* c, PyObject* encoding, PyObject } return PyUnicode_Decode(c, strlen(c), enc, err); -#endif } static inline PyObject* _str_to_bytes(PyObject* s, PyObject* encoding, PyObject* errors) { -#if PY_MAJOR_VERSION <= 2 - /* On Python 2, we accept bytes == str as input */ - if (PyBytes_CheckExact(s)) { - Py_INCREF(s); - return s; - } -#endif - if (!PyUnicode_Check(s)) { PyErr_Format(PyExc_TypeError, -#if PY_MAJOR_VERSION >= 3 "expected str, %s found", -#else - "expected str or unicode, %s found", -#endif Py_TYPE(s)->tp_name); return NULL; } @@ -60,20 +44,6 @@ static inline PyObject* _str_to_bytes(PyObject* s, PyObject* encoding, PyObject* const char* err = NULL; // Default: strict const char* enc = NULL; // Default: utf-8 -#if PY_MAJOR_VERSION <= 2 - if (errors != Py_None) { - err = PyString_AsString(errors); - if (!err) return NULL; - } - - if (encoding != Py_None) { - enc = PyString_AsString(encoding); - if (!enc) return NULL; - } - else { - enc = "utf-8"; - } -#else if (errors != Py_None) { err = PyUnicode_AsUTF8(errors); if (!err) return NULL; @@ -83,7 +53,6 @@ static inline PyObject* _str_to_bytes(PyObject* s, PyObject* encoding, PyObject* enc = PyUnicode_AsUTF8(encoding); if (!enc) return NULL; } -#endif return PyUnicode_AsEncodedString(s, enc, err); } diff --git a/src/sage/cpython/wrapperdescr.pyx b/src/sage/cpython/wrapperdescr.pyx index c25d955fa03..30f6771d504 100644 --- a/src/sage/cpython/wrapperdescr.pyx +++ b/src/sage/cpython/wrapperdescr.pyx @@ -79,7 +79,7 @@ def wrapperdescr_call(slotwrapper, self, *args, **kwds): sage: wrapperdescr_call(Integer.__mul__, 1, 2, 3) Traceback (most recent call last): ... - TypeError: expected 1 arguments, got 2 + TypeError: expected 1 arg..., got 2 sage: wrapperdescr_call(Integer.__mul__, 6, other=9) Traceback (most recent call last): ... diff --git a/src/sage/crypto/block_cipher/des.py b/src/sage/crypto/block_cipher/des.py index 44f8b510b8c..0e6ecb06e22 100644 --- a/src/sage/crypto/block_cipher/des.py +++ b/src/sage/crypto/block_cipher/des.py @@ -78,7 +78,6 @@ from sage.modules.free_module_element import vector from sage.rings.finite_rings.finite_field_constructor import GF from sage.modules.vector_mod2_dense import Vector_mod2_dense -from six import integer_types from sage.rings.integer import Integer from sage.crypto.sboxes import DES_S1_1, DES_S1_2, DES_S1_3, DES_S1_4 from sage.crypto.sboxes import DES_S2_1, DES_S2_2, DES_S2_3, DES_S2_4 @@ -514,7 +513,7 @@ def encrypt(self, plaintext, key): """ if isinstance(plaintext, (list, tuple, Vector_mod2_dense)): inputType = 'vector' - elif isinstance(plaintext, integer_types + (Integer,)): + elif isinstance(plaintext, (Integer, int)): inputType = 'integer' state = convert_to_vector(plaintext, self._blocksize) key = convert_to_vector(key, self._keySize) @@ -571,7 +570,7 @@ def decrypt(self, ciphertext, key): """ if isinstance(ciphertext, (list, tuple, Vector_mod2_dense)): inputType = 'vector' - elif isinstance(ciphertext, integer_types + (Integer,)): + elif isinstance(ciphertext, (Integer, int)): inputType = 'integer' state = convert_to_vector(ciphertext, 64) key = convert_to_vector(key, self._keySize) @@ -871,7 +870,7 @@ def __call__(self, key): """ if isinstance(key, (list, tuple, Vector_mod2_dense)): inputType = 'vector' - elif isinstance(key, integer_types + (Integer,)): + elif isinstance(key, (Integer, int)): inputType = 'integer' key = convert_to_vector(key, self._keySize) roundKeys = [] diff --git a/src/sage/crypto/block_cipher/miniaes.py b/src/sage/crypto/block_cipher/miniaes.py index 003bca78b20..4dff1a9ea00 100644 --- a/src/sage/crypto/block_cipher/miniaes.py +++ b/src/sage/crypto/block_cipher/miniaes.py @@ -25,7 +25,6 @@ # # http://www.gnu.org/licenses/ ########################################################################### -from six.moves import range from sage.matrix.matrix_dense import Matrix_dense from sage.matrix.matrix_space import MatrixSpace diff --git a/src/sage/crypto/block_cipher/present.py b/src/sage/crypto/block_cipher/present.py index 4584945375a..fe72cb6c983 100644 --- a/src/sage/crypto/block_cipher/present.py +++ b/src/sage/crypto/block_cipher/present.py @@ -65,7 +65,6 @@ from sage.rings.finite_rings.finite_field_constructor import GF from sage.crypto.sboxes import PRESENT as PRESENTSBOX from sage.modules.vector_mod2_dense import Vector_mod2_dense -from six import integer_types def _smallscale_present_linearlayer(nsboxes=16): @@ -420,7 +419,7 @@ def encrypt(self, plaintext, key): """ if isinstance(plaintext, (list, tuple, Vector_mod2_dense)): inputType = 'vector' - elif isinstance(plaintext, integer_types + (Integer,)): + elif isinstance(plaintext, (Integer, int)): inputType = 'integer' state = convert_to_vector(plaintext, 64) key = convert_to_vector(key, self._keysize) @@ -476,7 +475,7 @@ def decrypt(self, ciphertext, key): """ if isinstance(ciphertext, (list, tuple, Vector_mod2_dense)): inputType = 'vector' - elif isinstance(ciphertext, integer_types + (Integer,)): + elif isinstance(ciphertext, (Integer, int)): inputType = 'integer' state = convert_to_vector(ciphertext, 64) key = convert_to_vector(key, self._keysize) @@ -776,7 +775,7 @@ def __call__(self, K): """ if isinstance(K, (list, tuple, Vector_mod2_dense)): inputType = 'vector' - elif isinstance(K, integer_types + (Integer,)): + elif isinstance(K, (Integer, int)): inputType = 'integer' K = convert_to_vector(K, self._keysize) roundKeys = [] diff --git a/src/sage/crypto/block_cipher/sdes.py b/src/sage/crypto/block_cipher/sdes.py index 3c6e240dd84..02a7a14772c 100644 --- a/src/sage/crypto/block_cipher/sdes.py +++ b/src/sage/crypto/block_cipher/sdes.py @@ -26,7 +26,6 @@ # # http://www.gnu.org/licenses/ ########################################################################### -from six.moves import range from sage.monoids.string_monoid import BinaryStrings from sage.structure.sage_object import SageObject diff --git a/src/sage/crypto/classical.py b/src/sage/crypto/classical.py index a6850ffa9a1..99aeca320c3 100644 --- a/src/sage/crypto/classical.py +++ b/src/sage/crypto/classical.py @@ -42,7 +42,6 @@ #***************************************************************************** from __future__ import print_function from __future__ import absolute_import -from six.moves import range # TODO: check off this todo list: # - methods to cryptanalyze the Hill, substitution, transposition, and diff --git a/src/sage/crypto/classical_cipher.py b/src/sage/crypto/classical_cipher.py index fb55dd9b2d0..6969d33a287 100644 --- a/src/sage/crypto/classical_cipher.py +++ b/src/sage/crypto/classical_cipher.py @@ -10,7 +10,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from .cipher import SymmetricKeyCipher from sage.monoids.string_monoid_element import StringMonoidElement diff --git a/src/sage/crypto/lwe.py b/src/sage/crypto/lwe.py index 8a27529053b..73ab9bd491e 100644 --- a/src/sage/crypto/lwe.py +++ b/src/sage/crypto/lwe.py @@ -86,7 +86,6 @@ - [CGW2013]_ """ -from six.moves import range from sage.functions.log import log from sage.functions.other import sqrt, floor, ceil diff --git a/src/sage/crypto/mq/rijndael_gf.py b/src/sage/crypto/mq/rijndael_gf.py index c57679e4693..90b207a2f67 100644 --- a/src/sage/crypto/mq/rijndael_gf.py +++ b/src/sage/crypto/mq/rijndael_gf.py @@ -422,7 +422,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, division -from six import string_types from sage.matrix.constructor import matrix from sage.matrix.constructor import column_matrix @@ -488,10 +487,10 @@ def __init__(self, Nb, Nk, state_chr='a', key_chr='k'): if Nk not in range(4, 9): msg = "Key length Nk must be in the range 4 - 8, not {0}" raise ValueError(msg.format(Nk)) - if not isinstance(state_chr, string_types): + if not isinstance(state_chr, str): msg = "state_chr must be a string, not {0}" raise TypeError(msg.format(state_chr)) - if not isinstance(key_chr, string_types): + if not isinstance(key_chr, str): msg = "key_chr must be a string, not {0}" raise TypeError(msg.format(key_chr)) @@ -708,7 +707,7 @@ def _hex_to_GF(self, H, matrix=True): sage: rgf._hex_to_GF('1a2b0f', matrix=False) [x^4 + x^3 + x, x^5 + x^3 + x + 1, x^3 + x^2 + x + 1] """ - if not isinstance(H, string_types) or \ + if not isinstance(H, str) or \ any(c not in '0123456789abcdefABCDEF' for c in H): raise TypeError("keyword 'H' must be a hex string") @@ -836,7 +835,7 @@ def _bin_to_GF(self, B, matrix=True): x^7 + x^6 + x^4 + x^2 + x + 1, x^5 + x^4 + x^2 + 1] """ - if not isinstance(B, string_types) or any(c not in '01' for c in B): + if not isinstance(B, str) or any(c not in '01' for c in B): raise TypeError("keyword 'B' must be a binary string") def bn_to_gf(b): @@ -954,13 +953,13 @@ def encrypt(self, plain, key, format='hex'): True """ if format == 'hex': - if not isinstance(plain, string_types) or \ + if not isinstance(plain, str) or \ any(c not in '0123456789abcdefABCDEF' for c in plain): raise TypeError("'plain' keyword must be a hex string") if len(plain) != 8 * self._Nb: msg = "'plain' keyword\'s length must be {0}, not{1}" raise ValueError(msg.format(8 * self._Nb, len(plain))) - if not isinstance(key, string_types) or \ + if not isinstance(key, str) or \ any(c not in '0123456789abcdefABCDEF' for c in key): raise TypeError("'key' keyword must be a hex string") if len(key) != 8 * self._Nk: @@ -970,13 +969,13 @@ def encrypt(self, plain, key, format='hex'): key_state = self._hex_to_GF(key) roundKeys = self.expand_key(key_state) elif format == 'binary': - if not isinstance(plain, string_types) or \ + if not isinstance(plain, str) or \ any(c not in '01' for c in plain): raise TypeError("'plain' keyword must be a binary string") if len(plain) != 32 * self._Nb: msg = "'plain' keyword's length must be {0}, not {1}" raise ValueError(msg.format(32 * self._Nb, len(plain))) - if not isinstance(key, string_types) or \ + if not isinstance(key, str) or \ any(c not in '01' for c in key): raise TypeError("'key' keyword must be a binary string") if len(key) != 32 * self._Nk: @@ -1044,13 +1043,13 @@ def decrypt(self, ciphertext, key, format='hex'): True """ if format == 'hex': - if not isinstance(ciphertext, string_types) or \ + if not isinstance(ciphertext, str) or \ any(c not in '0123456789abcdefABCDEF' for c in ciphertext): raise TypeError("'ciphertext' keyword must be a hex string") if len(ciphertext) != 8 * self._Nb: msg = "'ciphertext' keyword's length must be {0}, not{1}" raise ValueError(msg.format(8 * self._Nb, len(ciphertext))) - if not isinstance(key, string_types) or \ + if not isinstance(key, str) or \ any(c not in '0123456789abcdefABCDEF' for c in key): raise TypeError("'key' keyword must be a hex string") if len(key) != 8 * self._Nk: @@ -1060,14 +1059,14 @@ def decrypt(self, ciphertext, key, format='hex'): key_state = self._hex_to_GF(key) roundKeys = self.expand_key(key_state) elif format == 'binary': - if not isinstance(ciphertext, string_types) or \ + if not isinstance(ciphertext, str) or \ any(c not in '01' for c in ciphertext): raise TypeError(("'ciphertext' keyword must be a binary " "string")) if len(ciphertext) != 32 * self._Nb: msg = "'ciphertext' keyword's length must be {0}, not {1}" raise ValueError(msg.format(32 * self._Nb, len(ciphertext))) - if not isinstance(key, string_types) or \ + if not isinstance(key, str) or \ any(c not in '01' for c in key): raise TypeError("'key' keyword must be a binary string") if len(key) != 32 * self._Nk: @@ -2249,7 +2248,6 @@ def __init__(self, polynomial_constr, rgf, round_component_name=None): sage: def my_poly_constr(row, col, algorithm='encrypt'): ....: return x * rgf._F.one() # example body with no checks - ....: sage: rcpc = RijndaelGF.Round_Component_Poly_Constr( ....: my_poly_constr, rgf, "My Poly Constr") sage: rcpc(-1, 2) diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index 0124f776d0f..0b501095e25 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -306,8 +306,6 @@ """ # python3 from __future__ import division, print_function, absolute_import -from six.moves import range -from six import integer_types from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.rings.integer_ring import ZZ @@ -317,7 +315,7 @@ from sage.matrix.constructor import Matrix, random_matrix from sage.matrix.matrix_space import MatrixSpace -from sage.misc.misc import get_verbose +from sage.misc.verbose import get_verbose from sage.misc.flatten import flatten from sage.modules.vector_modn_dense import Vector_modn_dense @@ -1265,6 +1263,7 @@ def __call__(self, P, K): sage: k = sr.base_ring() sage: plain = '3243f6a8885a308d313198a2e0370734' sage: key = '2b7e151628aed2a6abf7158809cf4f3c' + sage: from sage.misc.verbose import set_verbose sage: set_verbose(2) sage: cipher = sr(plain, key) R[01].start 193DE3BEA0F4E22B9AC68D2AE9F84808 @@ -2110,7 +2109,7 @@ def polynomial_system(self, P=None, K=None, C=None): if d is None: data.append( None ) elif isinstance(d, (tuple, list)): - if isinstance(d[0], integer_types): + if isinstance(d[0], int): d = [GF(2)(_) for _ in d] if len(d) == r*c*e and (d[0].parent() is R or d[0].parent() == R): data.append( Matrix(R,r*c*e,1,d) ) diff --git a/src/sage/crypto/public_key/blum_goldwasser.py b/src/sage/crypto/public_key/blum_goldwasser.py index 954b6565531..d841e69699a 100644 --- a/src/sage/crypto/public_key/blum_goldwasser.py +++ b/src/sage/crypto/public_key/blum_goldwasser.py @@ -27,7 +27,6 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from operator import xor diff --git a/src/sage/crypto/sbox.py b/src/sage/crypto/sbox.py index e722c2ef647..71c615f7f21 100644 --- a/src/sage/crypto/sbox.py +++ b/src/sage/crypto/sbox.py @@ -2,8 +2,6 @@ S-Boxes and Their Algebraic Representations """ from __future__ import print_function, division -from six.moves import range -from six import integer_types from sage.combinat.integer_vector import IntegerVectors from sage.crypto.boolean_function import BooleanFunction @@ -356,7 +354,7 @@ def __call__(self, X): sage: S([0,0,0]) [1, 1] """ - if isinstance(X, integer_types + (Integer,)): + if isinstance(X, (Integer, int)): return self._S[ZZ(X)] try: diff --git a/src/sage/crypto/stream.py b/src/sage/crypto/stream.py index b25b73d5581..a973634d4e5 100644 --- a/src/sage/crypto/stream.py +++ b/src/sage/crypto/stream.py @@ -12,7 +12,6 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from .cryptosystem import SymmetricKeyCryptosystem from .stream_cipher import LFSRCipher, ShrinkingGeneratorCipher diff --git a/src/sage/crypto/util.py b/src/sage/crypto/util.py index daf427bfc61..7541dd65b51 100644 --- a/src/sage/crypto/util.py +++ b/src/sage/crypto/util.py @@ -20,7 +20,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from six.moves import range from sage.monoids.string_monoid import BinaryStrings from sage.arith.all import is_prime, lcm, primes, random_prime diff --git a/src/sage/data_structures/bitset.pyx b/src/sage/data_structures/bitset.pyx index 231792138b5..708eae1cb05 100644 --- a/src/sage/data_structures/bitset.pyx +++ b/src/sage/data_structures/bitset.pyx @@ -278,7 +278,7 @@ cdef class FrozenBitset: sage: FrozenBitset("110110", capacity=-2) Traceback (most recent call last): ... - OverflowError: can't convert negative value to mp_bitcnt_t + OverflowError: can...t convert negative value to mp_bitcnt_t """ def __cinit__(self, iter=None, capacity=None): """ diff --git a/src/sage/data_structures/bounded_integer_sequences.pyx b/src/sage/data_structures/bounded_integer_sequences.pyx index 2f1da5274eb..ded2429a2ea 100644 --- a/src/sage/data_structures/bounded_integer_sequences.pyx +++ b/src/sage/data_structures/bounded_integer_sequences.pyx @@ -587,7 +587,7 @@ cdef class BoundedIntegerSequence: sage: BoundedIntegerSequence(16, [2, 7, -20]) Traceback (most recent call last): ... - OverflowError: can't convert negative value to size_t + OverflowError: can...t convert negative value to size_t sage: BoundedIntegerSequence(1, [0, 0, 0]) <0, 0, 0> sage: BoundedIntegerSequence(1, [0, 1, 0]) diff --git a/src/sage/databases/conway.py b/src/sage/databases/conway.py index a5a96f64059..6d3e585973d 100644 --- a/src/sage/databases/conway.py +++ b/src/sage/databases/conway.py @@ -19,11 +19,9 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from six import iteritems -from six.moves import cPickle as pickle - import collections import os +import pickle from sage.env import CONWAY_POLYNOMIALS_DATA_DIR @@ -182,7 +180,7 @@ def __iter__(self): sage: next(itr) # random (65537, 4) """ - for a, b in iteritems(self._store): + for a, b in self._store.items(): for c in b: yield a, c diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index a2c596085f6..abd8e65effc 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -172,8 +172,6 @@ def increasing_tree_shape(elt, compare=min): # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from six.moves import range -from six import iteritems, add_metaclass, string_types from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.structure.element import Element @@ -185,7 +183,7 @@ def increasing_tree_shape(elt, compare=min): from sage.structure.sage_object import SageObject from sage.structure.richcmp import richcmp -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.rings.integer import Integer from sage.databases.oeis import FancyTuple @@ -198,10 +196,9 @@ def increasing_tree_shape(elt, compare=min): import json import cgi -# import compatible with py2 and py3 -from six.moves.urllib.parse import urlencode -from six.moves.urllib.request import Request, urlopen -from six.moves.urllib.error import HTTPError +from urllib.parse import urlencode +from urllib.request import Request, urlopen +from urllib.error import HTTPError # Combinatorial collections from sage.combinat.alternating_sign_matrix import AlternatingSignMatrix, AlternatingSignMatrices @@ -534,7 +531,7 @@ def query_by_dict(query, collection=None): we expect a dictionary from objects or strings to integers """ - l = iteritems(query) + l = query.items() (key, value) = next(l) (collection, to_str) = get_collection(collection, key) @@ -1000,12 +997,12 @@ def _find_by_id(self): gf = self._raw[FINDSTAT_STATISTIC_GENERATING_FUNCTION] self._generating_functions_dict = { literal_eval(key): { literal_eval(inner_key): inner_value - for inner_key, inner_value in iteritems(value) } - for key, value in iteritems(gf) } + for inner_key, inner_value in value.items() } + for key, value in gf.items() } from_str = self._collection.from_string() # we want to keep FindStat's ordering here! - self._first_terms = [(from_str(obj), Integer(val)) for (obj, val) in iteritems(self._raw[FINDSTAT_STATISTIC_DATA])] + self._first_terms = [(from_str(obj), Integer(val)) for (obj, val) in self._raw[FINDSTAT_STATISTIC_DATA].items()] return self ###################################################################### @@ -1465,8 +1462,8 @@ def generating_functions(self, style="polynomial"): P = PolynomialRing(ZZ,"q") q = P.gen() return { level : sum( coefficient * q**exponent - for exponent,coefficient in iteritems(gen_dict) ) - for level, gen_dict in iteritems(gfs)} + for exponent,coefficient in gen_dict.items() ) + for level, gen_dict in gfs.items()} else: raise ValueError("The argument 'style' (='%s') must be 'dictionary', 'polynomial', or 'list'." % style) @@ -1860,7 +1857,7 @@ def submit(self, max_values=FINDSTAT_MAX_SUBMISSION_VALUES): f.write(FINDSTAT_NEWSTATISTIC_FORM_HEADER %FINDSTAT_URL_NEW) else: f.write(FINDSTAT_NEWSTATISTIC_FORM_HEADER %(FINDSTAT_URL_EDIT+self.id_str())) - for key, value in iteritems(args): + for key, value in args.items(): verbose("writing argument %s" % key, caller_name='FindStat') value_encoded = cgi.escape(str(value), quote=True) verbose("%s" % value_encoded, caller_name='FindStat') @@ -1908,8 +1905,7 @@ def _finite_irreducible_cartan_types_by_rank(n): return cartan_types -@add_metaclass(InheritComparisonClasscallMetaclass) -class FindStatCollection(Element): +class FindStatCollection(Element, metaclass=InheritComparisonClasscallMetaclass): r""" A FindStat collection. @@ -2517,7 +2513,7 @@ def __init__(self): c[1] = j[FINDSTAT_COLLECTION_NAME_PLURAL] c[2] = j[FINDSTAT_COLLECTION_NAME_WIKI] c[5] = {literal_eval(key):value for key,value in - iteritems(j[FINDSTAT_COLLECTION_LEVELS])} + j[FINDSTAT_COLLECTION_LEVELS].items()} Parent.__init__(self, category=Sets()) @@ -2574,7 +2570,7 @@ def _element_constructor_(self, entry): if isinstance(entry, FindStatCollection): return entry - if isinstance(entry, string_types): + if isinstance(entry, str): # find by name in _findstat_collections for id, c in self._findstat_collections.items(): if entry.upper() in (c[0].upper(), c[1].upper(), c[2].upper()): @@ -2651,8 +2647,7 @@ def __iter__(self): Element = FindStatCollection -@add_metaclass(InheritComparisonClasscallMetaclass) -class FindStatMap(Element): +class FindStatMap(Element, metaclass=InheritComparisonClasscallMetaclass): r""" A FindStat map. @@ -3003,7 +2998,7 @@ def _element_constructor_(self, entry): elif entry in self._findstat_maps: return self.element_class(self, entry) - elif isinstance(entry, string_types): + elif isinstance(entry, str): # find by name in _findstat_maps for c in self._findstat_maps: if entry.upper() == c[FINDSTAT_MAP_NAME].upper(): diff --git a/src/sage/databases/odlyzko.py b/src/sage/databases/odlyzko.py index 690877c3185..547baad0625 100644 --- a/src/sage/databases/odlyzko.py +++ b/src/sage/databases/odlyzko.py @@ -24,7 +24,6 @@ from sage.misc.persist import load from sage.env import SAGE_SHARE -from sage.misc.all import verbose def zeta_zeros(): r""" @@ -54,6 +53,7 @@ def zeta_zeros(): sage: len(zz) # optional - database_odlyzko_zeta 2001052 """ + from sage.misc.verbose import verbose sobj = os.path.join(SAGE_SHARE, 'odlyzko', 'zeros.sobj') verbose("Loading Odlyzko database from " + sobj) return load(sobj) diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index 552d2368e8a..688de468bb6 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -90,7 +90,7 @@ :: - sage: p.cross_references(fetch=True) # optional -- internet + sage: p.cross_references(fetch=True) # optional -- internet # random 0: A000798: Number of different quasi-orders (or topologies, or transitive digraphs) with n labeled elements. 1: A001035: Number of partially ordered sets ("posets") with n labeled elements (or labeled acyclic transitive digraphs). 2: A001930: Number of topologies, or transitive digraphs with n unlabeled nodes. @@ -105,7 +105,7 @@ ... -What does the Taylor expansion of the `e^(e^x-1)`` function have to do with +What does the Taylor expansion of the `e^{e^x-1}` function have to do with primes ? :: @@ -158,19 +158,22 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six.moves.urllib.request import urlopen -from six.moves.urllib.parse import urlencode +from urllib.request import urlopen +from urllib.parse import urlencode from sage.structure.sage_object import SageObject from sage.structure.unique_representation import UniqueRepresentation from sage.cpython.string import bytes_to_str from sage.rings.integer import Integer -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.misc.cachefunc import cached_method from sage.misc.flatten import flatten +from sage.misc.temporary_file import tmp_filename from sage.misc.unknown import Unknown from sage.misc.misc import embedded from sage.misc.html import HtmlFragment +from sage.repl.preparse import preparse + from collections import defaultdict import re @@ -183,7 +186,7 @@ def _fetch(url): INPUT: - - ``url`` - a string corresponding to the URL to be fetched. + - ``url`` -- a string corresponding to the URL to be fetched. OUTPUT: @@ -214,7 +217,7 @@ def _urls(html_string): INPUT: - - ``html_string`` - a string representing some HTML code. + - ``html_string`` -- a string representing some HTML code. OUTPUT: @@ -399,10 +402,10 @@ def find_by_id(self, ident, fetch=False): INPUT: - - ``ident`` - a string representing the A-number of the sequence + - ``ident`` -- a string representing the A-number of the sequence or an integer representing its number. - - ``fetch`` - (bool, default: ``False``) whether to force fetching the + - ``fetch`` -- (bool, default: ``False``) whether to force fetching the content of the sequence on the internet. OUTPUT: @@ -427,7 +430,7 @@ def find_by_entry(self, entry): INPUT: - - ``entry`` - a string corresponding to an entry in the internal format + - ``entry`` -- a string corresponding to an entry in the internal format of the OEIS. OUTPUT: @@ -452,13 +455,13 @@ def find_by_description(self, description, max_results=3, first_result=0): INPUT: - - ``description`` - (string) the description the searched sequences. + - ``description`` -- (string) the description the searched sequences. - - ``max_results`` - (integer, default: 3) the maximum number of results + - ``max_results`` -- (integer, default: 3) the maximum number of results we want. In any case, the on-line encyclopedia will not return more than 100 results. - - ``first_result`` - (integer, default: 0) allow to skip the + - ``first_result`` -- (integer, default: 0) allow to skip the ``first_result`` first results in the search, to go further. This is useful if you are looking for a sequence that may appear after the 100 first found sequences. @@ -472,25 +475,23 @@ def find_by_description(self, description, max_results=3, first_result=0): EXAMPLES:: sage: oeis.find_by_description('prime gap factorization') # optional -- internet - 0: A073491: Numbers having no prime gaps in their factorization. - 1: A073485: Product of any number of consecutive primes; squarefree numbers with no gaps in their prime factorization. - 2: A073490: Number of prime gaps in factorization of n. + 0: A...: ... + 1: A...: ... + 2: A...: ... sage: prime_gaps = _[2] ; prime_gaps # optional -- internet A073490: Number of prime gaps in factorization of n. - :: - sage: oeis('beaver') # optional -- internet - 0: A028444: Busy Beaver sequence, or Rado's sigma function: ... - 1: A060843: Busy Beaver problem: a(n) = maximal number of steps ... - 2: A131956: Busy Beaver variation: maximum number of steps for ... + 0: A...: ...eaver... + 1: A...: ...eaver... + 2: A...: ...eaver... sage: oeis('beaver', max_results=4, first_result=2) # optional -- internet - 0: A131956: Busy Beaver variation: maximum number of steps for ... - 1: A141475: Number of Turing machines with n states following ... - 2: A131957: Busy Beaver sigma variation: maximum number of 1's ... - 3: A052200: Number of n-state, 2-symbol, d+ in {LEFT, RIGHT}, ... + 0: A...: ...eaver... + 1: A...: ...eaver... + 2: A...: ...eaver... + 3: A...: ...eaver... """ options = {'q': description, 'n': str(max_results), @@ -506,11 +507,11 @@ def find_by_subsequence(self, subsequence, max_results=3, first_result=0): INPUT: - - ``subsequence`` - a list of integers. + - ``subsequence`` -- a list of integers. - - ``max_results`` - (integer, default: 3), the maximum of results requested. + - ``max_results`` -- (integer, default: 3), the maximum of results requested. - - ``first_result`` - (integer, default: 0) allow to skip the + - ``first_result`` -- (integer, default: 0) allow to skip the ``first_result`` first results in the search, to go further. This is useful if you are looking for a sequence that may appear after the 100 first found sequences. @@ -551,9 +552,9 @@ def _imaginary_entry(self, ident='A999999', keywords=''): INPUT: - - ``ident`` - a string representing the A-number of the sequence. + - ``ident`` -- a string representing the A-number of the sequence. - - ``keywords`` - a string corresponding to the keyword field of the + - ``keywords`` -- a string corresponding to the keyword field of the sequence. OUTPUT: @@ -595,7 +596,7 @@ def _imaginary_entry(self, ident='A999999', keywords=''): '%o ' + ident + ' def ' + ident + '(n):\n' '%o ' + ident + ' assert(isinstance(n, (int, Integer))), "n must be an integer."\n' '%o ' + ident + ' if n < 38:\n' - '%o ' + ident + ' raise ValueError("The value %s is not accepted." %str(n)))\n' + '%o ' + ident + ' raise ValueError("The value %s is not accepted." %str(n))\n' '%o ' + ident + ' elif n == 42:\n' '%o ' + ident + ' return 2\n' '%o ' + ident + ' else:\n' @@ -611,9 +612,9 @@ def _imaginary_sequence(self, ident='A999999', keywords='sign,easy'): INPUT: - - ``ident`` - a string representing the A-number of the sequence. + - ``ident`` -- a string representing the A-number of the sequence. - - ``keywords`` - string (default: 'sign,easy'), a list of words + - ``keywords`` -- string (default: 'sign,easy'), a list of words separated by commas. OUTPUT: @@ -632,6 +633,7 @@ def _imaginary_sequence(self, ident='A999999', keywords='sign,easy'): """ return self.find_by_entry(entry=self._imaginary_entry(ident=ident, keywords=keywords)) + class OEISSequence(SageObject, UniqueRepresentation): r""" The class of OEIS sequences. @@ -688,7 +690,7 @@ def __init__(self, ident): INPUT: - - ``ident`` - a string representing the A-number of the sequence or an + - ``ident`` -- a string representing the A-number of the sequence or an integer representing its number. TESTS:: @@ -749,7 +751,7 @@ def id(self, format='A'): INPUT: - - ``format`` - (string, default: 'A'). + - ``format`` -- (string, default: 'A'). OUTPUT: @@ -1076,7 +1078,7 @@ def natural_object(self): sage: s.natural_object().universe() Integer Ring """ - if 'cofr' in self.keywords() and not 'frac' in self.keywords(): + if 'cofr' in self.keywords() and 'frac' not in self.keywords(): from sage.rings.continued_fraction import continued_fraction return continued_fraction(self.first_terms()) elif 'cons' in self.keywords(): @@ -1099,7 +1101,7 @@ def is_dead(self, warn_only=False): INPUT: - - warn_only - (bool, default: ``False``), whether to warn when the + - warn_only -- (bool, default: ``False``), whether to warn when the sequence is dead instead of returning a boolean. EXAMPLES: @@ -1240,7 +1242,7 @@ def first_terms(self, number=None): INPUT: - - ``number`` - (integer or ``None``, default: ``None``) the number of + - ``number`` -- (integer or ``None``, default: ``None``) the number of terms returned (if less than the number of available terms). When set to None, returns all the known terms. @@ -1303,7 +1305,7 @@ def __call__(self, k): INPUT: - - ``k`` - integer. + - ``k`` -- integer. OUTPUT: @@ -1367,7 +1369,7 @@ def __getitem__(self, i): INPUT: - - ``i`` - integer. + - ``i`` -- integer. OUTPUT: @@ -1470,7 +1472,7 @@ def __iter__(self): """ for x in self.first_terms(): yield x - if not self.is_full(): + if not self.is_full() is True: raise LookupError("Future values not provided by OEIS.") def references(self): @@ -1510,10 +1512,10 @@ def links(self, browse=None, format='guess'): INPUT: - - ``browse`` - an integer, a list of integers, or the word 'all' + - ``browse`` -- an integer, a list of integers, or the word 'all' (default: ``None``) : which links to open in a web browser. - - ``format`` - string (default: 'guess') : how to display the links. + - ``format`` -- string (default: 'guess') : how to display the links. OUTPUT: @@ -1612,7 +1614,7 @@ def cross_references(self, fetch=False): INPUT: - - ``fetch`` - boolean (default: ``False``). + - ``fetch`` -- boolean (default: ``False``). OUTPUT: @@ -1832,55 +1834,185 @@ def show(self): print(re.sub('_', ' ', s).upper()) print(str(result) + '\n') - def programs(self, language='other'): + def programs(self, language='all', preparsing=True, keep_comments=False): r""" - Return programs implementing the sequence ``self`` in the given ``language``. + Return programs for the sequence ``self`` in the given ``language``. INPUT: - - ``language`` - string (default: 'other') - the language of the - program. Current values are: 'maple', 'mathematica' and 'other'. + - ``language`` -- string (default: 'all'), the chosen language. + Possible values are 'all' for the full list, or + any language name, for example 'sage', 'maple', 'mathematica', etc. + + Some further optional input is specific to sage code treatment: + + - ``preparsing`` -- boolean (default: ``True``) whether to preparse + sage code + - ``keep_comments`` -- boolean (default: ``False``) whether to keep + comments in sage code OUTPUT: - - tuple of strings (with fancy formatting). + If ``language`` is ``'all'``, this returns a sorted list of pairs + (language, code), where every language can appear several times. - .. TODO:: ask OEIS to add a "Sage program" field in the database ;) + Otherwise, this returns a list of programs in the ``language``, + each program being a tuple of strings (with fancy formatting). EXAMPLES:: sage: ee = oeis('A001113') ; ee # optional -- internet A001113: Decimal expansion of e. - sage: ee.programs()[0] # optional -- internet - '(PARI) default(realprecision, 50080); x=exp(1); for (n=1, 50000, d=floor(x); x=(x-d)*10; write("b001113.txt", n, " ", d)); \\\\ _Harry J. Smith_, Apr 15 2009' + sage: ee.programs('pari')[0] # optional -- internet + 0: default(realprecision, 50080); x=exp(1); for (n=1, 50000, d=floor(x); x=(x-d)*10; write("b001113.txt", n, " ", d)); \\ _Harry J. Smith_, Apr 15 2009 + + sage: G = oeis.find_by_id('A27642') # optional -- internet + sage: G.programs('all') # optional -- internet + [('haskell', ...), + ('magma', ...), + ... + ('python', ...), + ('sage', ...)] TESTS:: sage: s = oeis._imaginary_sequence() sage: s.programs() - 0: (Python) - 1: def A999999(n): - 2: assert(isinstance(n, (int, Integer))), "n must be an integer." - 3: if n < 38: - 4: raise ValueError("The value %s is not accepted." %str(n))) - 5: elif n == 42: - 6: return 2 - 7: else: - 8: return 1 - - sage: s.programs('maple') + [('maple', ...), + ('mathematica', ...), + ('python', + 0: def A999999(n): + 1: assert(isinstance(n, (int, Integer))), "n must be an integer." + 2: if n < 38: + 3: raise ValueError("The value %s is not accepted." %str(n)) + 4: elif n == 42: + 5: return 2 + 6: else: + 7: return 1)] + + sage: s.programs('maple')[0] 0: Do not even try, Maple is not able to produce such a sequence. - sage: s.programs('mathematica') + sage: s.programs('mathematica')[0] 0: Mathematica neither. """ + language = language.lower() if language == "maple": - return FancyTuple(self._field('p')) + return [FancyTuple(self._field('p'))] elif language == "mathematica": - return FancyTuple(self._field('t')) + return [FancyTuple(self._field('t'))] + if language == 'sagemath': + language = 'sage' + if language == 'all': + table = [('maple', FancyTuple(self._field('p'))), + ('mathematica', FancyTuple(self._field('t')))] else: - return FancyTuple(self._field('o')) + table = [] + + def is_starting_line(line): + """ + Help to split the big OEIS code block into blocks by language. + + This returns ``None`` if ``line`` is not a starting line. + """ + if not line.startswith('('): + return None + if ')' not in line: + return None + end = line.index(')') + language = line[1:end].lower() # to handle (Sage) versus (sage) + if '(' in language: + return None + if language == 'sagemath': + language = 'sage' + if language == 'c#' or language == 'c++': + language = 'c' + if language.replace(' ', '').isalnum() or language.startswith('scheme'): + # to cope with many wrong (Scheme xxx) separators in the OEIS + return (language, end) + return None + + def filter_sage(lines): + """ + Remove comments and preparse if required, only for sage code. + + This is an iterator. + """ + for line in lines: + if keep_comments or not line.strip().startswith('#'): + if preparsing: + yield preparse(line) + else: + yield line + + def flush_to_table(language, code_lines): + """ + Put a list of code lines into the appropriate box of the table. + + With special treatment for sage code blocks. + """ + if language == 'sage': + table.append((language, FancyTuple(filter_sage(code_lines)))) + elif language is not None: + table.append((language, FancyTuple(code_lines))) + + programs = FancyTuple(self._field('o')) + code_lines = [] + old_language = None + for line in programs: + new_language = is_starting_line(line) + if new_language is not None: + # flush the stock of code lines if any + flush_to_table(old_language, code_lines) + # start new stock of code lines + old_language, end = new_language + rest = line[end + 1:].strip() + code_lines = [rest] if rest else [] + else: + code_lines.append(line) + flush_to_table(old_language, code_lines) + + if language == 'all': + return sorted(table) + return sorted(prog for la, prog in table if la == language) + + def test_compile_sage_code(self): + """ + Try to compile the extracted sage code, if there is any. + + If there are several sage code fields, they are all considered. + + Dead sequences are considered to compile correctly by default. + + This returns ``True`` if the code compiles, and raises an error + otherwise. + + EXAMPLES: + + One correct sequence:: + + sage: s = oeis.find_by_id('A027642') # optional -- internet + sage: s.test_compile_sage_code() # optional -- internet + True + + One dead sequence:: + + sage: s = oeis.find_by_id('A000154') # optional -- internet + sage: s.test_compile_sage_code() # optional -- internet + doctest:warning + ... + RuntimeWarning: This sequence is dead: ... + True + """ + if self.is_dead(): + return True + filt = self.programs(language='sage') + if filt: + for v in filt: + tp = tmp_filename(ext='.sage') + _ = compile('\n'.join(v), tp, 'exec') + return True class FancyTuple(tuple): diff --git a/src/sage/databases/sloane.py b/src/sage/databases/sloane.py index 6235e51ca72..2535f0f62a2 100644 --- a/src/sage/databases/sloane.py +++ b/src/sage/databases/sloane.py @@ -86,10 +86,9 @@ import os import re -# import compatible with py2 and py3 -from six.moves.urllib.request import urlretrieve +from urllib.request import urlretrieve -from sage.misc.all import verbose +from sage.misc.verbose import verbose from sage.env import SAGE_SHARE from sage.rings.integer_ring import ZZ diff --git a/src/sage/databases/stein_watkins.py b/src/sage/databases/stein_watkins.py index eeb2bcb5981..00fe7181c82 100644 --- a/src/sage/databases/stein_watkins.py +++ b/src/sage/databases/stein_watkins.py @@ -134,7 +134,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from six.moves import range import bz2 import os @@ -298,11 +297,11 @@ def iter_levels(self): try: E = next(it) except StopIteration: - if C != []: + if C: yield C return if E.conductor != N: - if C != []: + if C: yield C C = [E] N = E.conductor diff --git a/src/sage/docs/conf.py b/src/sage/docs/conf.py index 23cf5d67922..f5a9031689e 100644 --- a/src/sage/docs/conf.py +++ b/src/sage/docs/conf.py @@ -3,11 +3,11 @@ import sage.version from sage.misc.sagedoc import extlinks import dateutil.parser -from six import iteritems from docutils import nodes from docutils.transforms import Transform from sphinx.ext.doctest import blankline_re from sphinx import highlighting +import sphinx.ext.intersphinx as intersphinx from IPython.lib.lexers import IPythonConsoleLexer, IPyLexer # If your extensions are in another directory, add it here. @@ -33,6 +33,10 @@ # through matplotlib, so that it will be displayed in the HTML doc plot_html_show_source_link = False plot_pre_code = """ +# Set locale to prevent having commas in decimal numbers +# in tachyon input (see https://trac.sagemath.org/ticket/28971) +import locale +locale.setlocale(locale.LC_NUMERIC, 'C') def sphinx_plot(graphics, **kwds): import matplotlib.image as mpimg import matplotlib.pyplot as plt @@ -169,13 +173,8 @@ def sphinx_plot(graphics, **kwds): # Cross-links to other project's online documentation. python_version = sys.version_info.major -intersphinx_mapping = { - 'python': ('https://docs.python.org/', - os.path.join(SAGE_DOC_SRC, "common", - "python{}.inv".format(python_version))), - 'pplpy': (PPLPY_DOCS, None)} -def set_intersphinx_mappings(app): +def set_intersphinx_mappings(app, config): """ Add precompiled inventory (the objects.inv) """ @@ -186,7 +185,11 @@ def set_intersphinx_mappings(app): app.config.intersphinx_mapping = {} return - app.config.intersphinx_mapping = intersphinx_mapping + app.config.intersphinx_mapping = { + 'python': ('https://docs.python.org/', + os.path.join(SAGE_DOC_SRC, "common", + "python{}.inv".format(python_version))), + 'pplpy': (PPLPY_DOCS, None)} # Add master intersphinx mapping dst = os.path.join(invpath, 'objects.inv') @@ -201,6 +204,7 @@ def set_intersphinx_mappings(app): dst = os.path.join(invpath, directory, 'objects.inv') app.config.intersphinx_mapping[src] = dst + intersphinx.normalize_intersphinx_mapping(app, config) # By default document are not master. multidocs_is_master = True @@ -422,6 +426,32 @@ def set_intersphinx_mappings(app): \DeclareUnicodeCharacter{2309}{\rceil} \DeclareUnicodeCharacter{22C5}{\ensuremath{\cdot}} + \DeclareUnicodeCharacter{2070}{\ensuremath{{}^0}} + \DeclareUnicodeCharacter{00B9}{\ensuremath{{}^1}} + \DeclareUnicodeCharacter{00B2}{\ensuremath{{}^2}} + \DeclareUnicodeCharacter{00B3}{\ensuremath{{}^3}} + \DeclareUnicodeCharacter{2074}{\ensuremath{{}^4}} + \DeclareUnicodeCharacter{2075}{\ensuremath{{}^5}} + \DeclareUnicodeCharacter{2076}{\ensuremath{{}^6}} + \DeclareUnicodeCharacter{2077}{\ensuremath{{}^7}} + \DeclareUnicodeCharacter{2078}{\ensuremath{{}^8}} + \DeclareUnicodeCharacter{2079}{\ensuremath{{}^9}} + \DeclareUnicodeCharacter{207A}{\ensuremath{{}^+}} + \DeclareUnicodeCharacter{207B}{\ensuremath{{}^-}} + \DeclareUnicodeCharacter{141F}{\ensuremath{{}^/}} + \DeclareUnicodeCharacter{2080}{\ensuremath{{}_0}} + \DeclareUnicodeCharacter{2081}{\ensuremath{{}_1}} + \DeclareUnicodeCharacter{2082}{\ensuremath{{}_2}} + \DeclareUnicodeCharacter{2083}{\ensuremath{{}_3}} + \DeclareUnicodeCharacter{2084}{\ensuremath{{}_4}} + \DeclareUnicodeCharacter{2085}{\ensuremath{{}_5}} + \DeclareUnicodeCharacter{2086}{\ensuremath{{}_6}} + \DeclareUnicodeCharacter{2087}{\ensuremath{{}_7}} + \DeclareUnicodeCharacter{2088}{\ensuremath{{}_8}} + \DeclareUnicodeCharacter{2089}{\ensuremath{{}_9}} + \DeclareUnicodeCharacter{208A}{\ensuremath{{}_+}} + \DeclareUnicodeCharacter{208B}{\ensuremath{{}_-}} + \newcommand{\sageMexSymbol}[1] {{\fontencoding{OMX}\fontfamily{cmex}\selectfont\raisebox{0.75em}{\symbol{#1}}}} \DeclareUnicodeCharacter{239B}{\sageMexSymbol{"30}} % parenlefttp @@ -452,6 +482,13 @@ def set_intersphinx_mappings(app): \DeclareUnicodeCharacter{23AE}{\ensuremath{\|}} % integral extenison \DeclareUnicodeCharacter{2571}{/} % Box drawings light diagonal upper right to lower left + + \DeclareUnicodeCharacter{25CF}{\ensuremath{\bullet}} % medium black circle + \DeclareUnicodeCharacter{26AC}{\ensuremath{\circ}} % medium small white circle + \DeclareUnicodeCharacter{256D}{+} + \DeclareUnicodeCharacter{256E}{+} + \DeclareUnicodeCharacter{256F}{+} + \DeclareUnicodeCharacter{2570}{+} \fi \let\textLaTeX\LaTeX @@ -552,7 +589,7 @@ def check_nested_class_picklability(app, what, name, obj, skip, options): # Check picklability of nested classes. Adapted from # sage.misc.nested_class.modify_for_nested_pickle. module = sys.modules[obj.__module__] - for (nm, v) in iteritems(obj.__dict__): + for (nm, v) in obj.__dict__.items(): if (isinstance(v, type) and v.__name__ == nm and v.__module__ == module.__name__ and @@ -616,7 +653,6 @@ def process_dollars(app, what, name, obj, options, docstringlines): See sage.misc.sagedoc.process_dollars for more information. """ if len(docstringlines) and name.find("process_dollars") == -1: - from six.moves import range from sage.misc.sagedoc import process_dollars as sagedoc_dollars s = sagedoc_dollars("\n".join(docstringlines)) lines = s.split("\n") @@ -665,11 +701,11 @@ def call_intersphinx(app, env, node, contnode): sage: for line in open(thematic_index).readlines(): # optional - dochtml ....: if "padics" in line: ....: _ = sys.stdout.write(line) -
  • Introduction to the p-adics
  • +
  • Introduction to the p-adics

  • """ debug_inf(app, "???? Trying intersphinx for %s" % node['reftarget']) builder = app.builder - res = sphinx.ext.intersphinx.missing_reference( + res = intersphinx.missing_reference( app, env, node, contnode) if res: # Replace absolute links to $SAGE_DOC by relative links: this @@ -852,11 +888,10 @@ def setup(app): if app.srcdir.startswith(SAGE_DOC_SRC): app.add_config_value('intersphinx_mapping', {}, False) app.add_config_value('intersphinx_cache_limit', 5, False) + app.connect('config-inited', set_intersphinx_mappings) + app.connect('builder-inited', intersphinx.load_mappings) # We do *not* fully initialize intersphinx since we call it by hand # in find_sage_dangling_links. # app.connect('missing-reference', missing_reference) app.connect('missing-reference', find_sage_dangling_links) - import sphinx.ext.intersphinx - app.connect('builder-inited', set_intersphinx_mappings) - app.connect('builder-inited', sphinx.ext.intersphinx.load_mappings) app.connect('builder-inited', nitpick_patch_config) diff --git a/src/sage/docs/instancedoc.pyx b/src/sage/docs/instancedoc.pyx index 0cf1176ae2b..13fc059b06c 100644 --- a/src/sage/docs/instancedoc.pyx +++ b/src/sage/docs/instancedoc.pyx @@ -74,12 +74,11 @@ docstring:: ....: "Metaclass doc" ....: def _instancedoc_(self): ....: return "Docstring for {}".format(self) - sage: from six import with_metaclass - sage: class T(with_metaclass(Meta, object)): + sage: class T(metaclass=Meta): ....: pass sage: print(T.__doc__) Docstring for - sage: class U(with_metaclass(Meta, object)): + sage: class U(metaclass=Meta): ....: "Special doc for U" sage: print(U.__doc__) Special doc for U diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index d408b18efea..a03f6cb08ee 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -7,8 +7,7 @@ - David Roe (2012-03-27) -- initial version, based on Robert Bradshaw's code. """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 David Roe # Robert Bradshaw # William Stein @@ -18,12 +17,17 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from __future__ import absolute_import, division, print_function -import random, os, sys, time, json, re, types -import six +import random +import os +import sys +import time +import json +import re +import types import sage.misc.flatten from sage.structure.sage_object import SageObject from sage.env import DOT_SAGE, SAGE_LIB, SAGE_SRC, SAGE_LOCAL, SAGE_EXTCODE @@ -35,13 +39,12 @@ from .reporting import DocTestReporter from .util import Timer, count_noun, dict_difference from .external import external_software, available_software -from sage.features import PythonModule nodoctest_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*nodoctest') optionaltag_regex = re.compile(r'^\w+$') # Optional tags which are always automatically added -auto_optional_tags = set(['py2' if six.PY2 else 'py3']) +auto_optional_tags = set(['py3']) class DocTestDefaults(SageObject): @@ -90,12 +93,13 @@ def __init__(self, **kwds): self.memlimit = 0 self.all = False self.logfile = None - self.sagenb = False self.long = False self.warn_long = None self.randorder = None + self.random_seed = 0 self.global_iterations = 1 # sage-runtests default is 0 self.file_iterations = 1 # sage-runtests default is 0 + self.environment = "sage.repl.ipython_kernel.all_jupyter" self.initial = False self.exitfirst = False self.force_lib = False @@ -191,7 +195,7 @@ def skipdir(dirname): sage: skipdir(os.path.join(sage.env.SAGE_SRC, "sage", "doctest", "tests")) True """ - if os.path.exists(os.path.join(dirname, "nodoctest.py")): + if os.path.exists(os.path.join(dirname, "nodoctest.py")) or os.path.exists(os.path.join(dirname, "nodoctest")): return True return False @@ -322,7 +326,7 @@ def __init__(self, options, args): options.timeout *= 2 if options.nthreads == 0: options.nthreads = int(os.getenv('SAGE_NUM_THREADS_PARALLEL',1)) - if options.failed and not (args or options.new or options.sagenb): + if options.failed and not (args or options.new): # If the user doesn't specify any files then we rerun all failed files. options.all = True if options.global_iterations == 0: @@ -341,7 +345,7 @@ def __init__(self, options, args): if options.verbose: options.show_skipped = True - if isinstance(options.optional, six.string_types): + if isinstance(options.optional, str): s = options.optional.lower() options.optional = set(s.split(',')) if "all" in options.optional: @@ -356,7 +360,7 @@ def __init__(self, options, args): options.optional.discard('optional') from sage.misc.package import list_packages for pkg in list_packages('optional', local=True).values(): - if pkg['installed_version'] == pkg['remote_version']: + if pkg['installed'] and pkg['installed_version'] == pkg['remote_version']: options.optional.add(pkg['name']) # Check that all tags are valid @@ -408,6 +412,9 @@ def __init__(self, options, args): self.load_stats(options.stats_path) self._init_warn_long() + if self.options.random_seed is None: + self.options.random_seed = 0 + def __del__(self): if getattr(self, 'logfile', None) is not None: self.logfile.close() @@ -494,6 +501,25 @@ def _repr_(self): """ return "DocTest Controller" + def load_environment(self): + """ + Return the module that provides the global environment. + + EXAMPLES:: + + sage: from sage.doctest.control import DocTestDefaults, DocTestController + sage: DC = DocTestController(DocTestDefaults(), []) + sage: 'BipartiteGraph' in DC.load_environment().__dict__ + True + sage: DC = DocTestController(DocTestDefaults(environment='sage.doctest.all'), []) + sage: 'BipartiteGraph' in DC.load_environment().__dict__ + False + sage: 'run_doctests' in DC.load_environment().__dict__ + True + """ + from importlib import import_module + return import_module(self.options.environment) + def load_stats(self, filename): """ Load stats from the most recent run(s). @@ -613,18 +639,6 @@ def test_safe_directory(self, dir=None): """ Test that the given directory is safe to run Python code from. - We use the check added to Python for this, which gives a - warning when the current directory is considered unsafe. We promote - this warning to an error with ``-Werror``. See - ``sage/tests/cmdline.py`` for a doctest that this works, see - also :trac:`13579`. - - .. NOTE:: - - This is only relevant with Python 2, because Sage's Python - 2 is patched to give a warning when the current directory - is unsafe, but Python 3 is not. - TESTS:: sage: from sage.doctest.control import DocTestDefaults, DocTestController @@ -634,20 +648,19 @@ def test_safe_directory(self, dir=None): sage: d = os.path.join(tmp_dir(), "test") sage: os.mkdir(d) sage: os.chmod(d, 0o777) - sage: DC.test_safe_directory(d) # py2 + sage: DC.test_safe_directory(d) Traceback (most recent call last): ... RuntimeError: refusing to run doctests... """ - import subprocess - with open(os.devnull, 'w') as dev_null: - if subprocess.call([sys.executable, '-Werror', '-c', ''], - stdout=dev_null, stderr=dev_null, cwd=dir) != 0: - raise RuntimeError( - "refusing to run doctests from the current " - "directory '{}' since untrusted users could put files in " - "this directory, making it unsafe to run Sage code from" - .format(os.getcwd())) + import stat + is_world_writeable = bool(os.stat(dir or os.getcwd()).st_mode & stat.S_IWOTH) + if is_world_writeable: + raise RuntimeError( + "refusing to run doctests from the current " + "directory '{}' since untrusted users could put files in " + "this directory, making it unsafe to run Sage code from" + .format(os.getcwd())) def create_run_id(self): """ @@ -665,7 +678,7 @@ def create_run_id(self): def add_files(self): r""" - Checks for the flags '--all', '--new' and '--sagenb'. + Checks for the flags '--all' and '--new'. For each one present, this function adds the appropriate directories and files to the todo list. @@ -688,15 +701,6 @@ def add_files(self): sage: DC = DocTestController(DD, []) sage: DC.add_files() Doctesting ... - - :: - - sage: DD = DocTestDefaults(sagenb = True) - sage: DC = DocTestController(DD, []) - sage: DC.add_files() # py2 # optional - sagenb - Doctesting the Sage notebook. - sage: DC.files[0][-6:] # py2 # optional - sagenb - 'sagenb' """ opj = os.path.join from sage.env import SAGE_SRC, SAGE_DOC_SRC, SAGE_ROOT, SAGE_ROOT_GIT @@ -715,7 +719,6 @@ def all_files(): if have_git: self.files.append(opj(SAGE_SRC, 'sage_setup')) self.files.append(SAGE_DOC_SRC) - self.options.sagenb = True if self.options.all or (self.options.new and not have_git): self.log("Doctesting entire Sage library.") @@ -741,18 +744,6 @@ def all_files(): filename.endswith(".pyx") or filename.endswith(".rst"))): self.files.append(os.path.relpath(opj(SAGE_ROOT,filename))) - if self.options.sagenb: - if six.PY3 or not PythonModule('sagenb').is_present(): - if not self.options.all: - self.log("Skipping doctesting of the Sage notebook: " - "not installed on Python 3") - return - - if not self.options.all: - self.log("Doctesting the Sage notebook.") - from pkg_resources import Requirement, working_set - sagenb_loc = working_set.find(Requirement.parse('sagenb')).location - self.files.append(opj(sagenb_loc, 'sagenb')) def expand_files_into_sources(self): r""" @@ -1034,10 +1025,9 @@ def _assemble_cmd(self): """ cmd = "sage-runtests --serial " opt = dict_difference(self.options.__dict__, DocTestDefaults().__dict__) - for o in ("all", "sagenb"): - if o in opt: - raise ValueError("You cannot run gdb/valgrind on the whole sage%s library"%("" if o == "all" else "nb")) - for o in ("all", "sagenb", "long", "force_lib", "verbose", "failed", "new"): + if "all" in opt: + raise ValueError("You cannot run gdb/valgrind on the whole sage library") + for o in ("all", "long", "force_lib", "verbose", "failed", "new"): if o in opt: cmd += "--%s "%o for o in ("timeout", "memlimit", "randorder", "stats_path"): @@ -1135,7 +1125,8 @@ def run_val_gdb(self, testing=False): os.putenv('CYSIGNALS_CRASH_LOGS', tmp_dir("crash_logs_")) init_cysignals() - import signal, subprocess + import signal + import subprocess p = subprocess.Popen(cmd, shell=True) if opt.timeout > 0: signal.alarm(opt.timeout) @@ -1287,7 +1278,7 @@ def stringify(x): return [base] else: return [os.path.join(base, file) + ext] - elif isinstance(x, six.string_types): + elif isinstance(x, str): return [os.path.abspath(x)] F = stringify(module) if options is None: diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py index e19c9891b9c..b0db07b47a2 100644 --- a/src/sage/doctest/external.py +++ b/src/sage/doctest/external.py @@ -27,6 +27,9 @@ from multiprocessing import Array +import urllib.error +from urllib.request import Request, urlopen + # Functions in this module whose name is of the form 'has_xxx' tests if the # software xxx is available to Sage. prefix = 'has_' @@ -44,8 +47,6 @@ def has_internet(): sage: has_internet() # random, optional -- internet True """ - from six.moves import urllib - from six.moves.urllib.request import Request, urlopen req = Request("http://www.sagemath.org",headers={"User-Agent":"sage-doctest"}) try: urlopen(req,timeout=1) @@ -67,10 +68,10 @@ def has_latex(): from sage.misc.temporary_file import tmp_filename try: f = tmp_filename(ext='.tex') - O = open(f, 'w') + O = open(f, 'w') O.write(_latex_file_('2+3')) - O.close() - _run_latex_(f) + O.close() + _run_latex_(f) return True except Exception: return False @@ -301,7 +302,7 @@ def external_software(): def _lookup(software): """ Test if the software is available on the system. - + EXAMPLES:: sage: sage.doctest.external._lookup('internet') # random, optional - internet @@ -353,8 +354,8 @@ def __init__(self): sage: S.seen() # random [] """ - # For multiprocessing of doctests, the data self._seen should be - # shared among subprocesses. Thus we use Array class from the + # For multiprocessing of doctests, the data self._seen should be + # shared among subprocesses. Thus we use Array class from the # multiprocessing module. self._seen = Array('i', len(external_software)) # initialized to zeroes @@ -402,7 +403,7 @@ def issuperset(self, other): def seen(self): """ Return the list of detected external software. - + EXAMPLES:: sage: from sage.doctest.external import available_software diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index c91a49c06be..bd7860a3c2e 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -49,8 +49,9 @@ import traceback import tempfile from dis import findlinestarts +from queue import Empty import gc -import six +import IPython.lib.pretty import sage.misc.randstate as randstate from .util import Timer, RecordingDict, count_noun @@ -68,28 +69,53 @@ MANDATORY_COMPILE_FLAGS = __future__.print_function.compiler_flag -if not six.PY2: - # These lists are used on Python 3+ only for backwards compatibility with - # Python 2 in traceback parsing - # These exceptions in Python 2 have been rolled into OSError on Python 3; - # see https://docs.python.org/3/library/exceptions.html#OSError - _OSError_ALIASES = [ - 'IOError', 'EnvironmentError', 'socket.error', 'select.error', - 'mmap.error' - ] - # This list is sort of the opposite case: these are new built-in exceptions - # in Python 3 that are subclasses of OSError; see - # https://docs.python.org/3/library/exceptions.html#os-exceptions - import builtins - _OSError_SUBCLASSES = [ - exc.__name__ for exc in vars(builtins).values() - if isinstance(exc, type) and issubclass(exc, OSError) and - exc is not OSError - ] - - - -def init_sage(): +# These lists are used on Python 3+ only for backwards compatibility with +# Python 2 in traceback parsing +# These exceptions in Python 2 have been rolled into OSError on Python 3; +# see https://docs.python.org/3/library/exceptions.html#OSError +_OSError_ALIASES = [ + 'IOError', 'EnvironmentError', 'socket.error', 'select.error', + 'mmap.error' +] +# This list is sort of the opposite case: these are new built-in exceptions +# in Python 3 that are subclasses of OSError; see +# https://docs.python.org/3/library/exceptions.html#os-exceptions +import builtins +_OSError_SUBCLASSES = [ + exc.__name__ for exc in vars(builtins).values() + if isinstance(exc, type) and issubclass(exc, OSError) and + exc is not OSError +] + +def _sorted_dict_pprinter_factory(start, end): + """ + Modified version of :func:`IPython.lib.pretty._dict_pprinter_factory` + that sorts the keys of dictionaries for printing. + + EXAMPLES:: + + sage: {2: 0, 1: 0} # indirect doctest + {1: 0, 2: 0} + """ + def inner(obj, p, cycle): + if cycle: + return p.text('{...}') + step = len(start) + p.begin_group(step, start) + keys = obj.keys() + keys = IPython.lib.pretty._sorted_for_pprint(keys) + for idx, key in p._enumerate(keys): + if idx: + p.text(',') + p.breakable() + p.pretty(key) + p.text(': ') + p.pretty(obj[key]) + p.end_group(step, end) + return inner + + +def init_sage(controller=None): """ Import the Sage library. @@ -169,8 +195,16 @@ def init_sage(): from sage.cpython._py2_random import Random sage.misc.randstate.DEFAULT_PYTHON_RANDOM = Random - import sage.repl.ipython_kernel.all_jupyter - sage.interfaces.quit.invalidate_all() + if controller is None: + import sage.repl.ipython_kernel.all_jupyter + else: + controller.load_environment() + + try: + from sage.interfaces.quit import invalidate_all + invalidate_all() + except ModuleNotFoundError: + pass # Disable cysignals debug messages in doctests: this is needed to # make doctests pass when cysignals was built with debugging enabled @@ -186,11 +220,11 @@ def init_sage(): # IPython's pretty printer sorts the repr of dicts by their keys by default # (or their keys' str() if they are not otherwise orderable). However, it # disables this for CPython 3.6+ opting to instead display dicts' "natural" - # insertion order, which is preserved in those versions). This makes for - # inconsistent results with Python 2 tests that return dicts, so here we - # force the Python 2 style dict printing - import IPython.lib.pretty - IPython.lib.pretty.DICT_IS_ORDERED = False + # insertion order, which is preserved in those versions). + # However, this order is random in some instances. + # Also modifications of code may affect the order. + # So here we fore sorted dict printing. + IPython.lib.pretty.for_type(dict, _sorted_dict_pprinter_factory('{', '}')) # Switch on extra debugging from sage.structure.debug_options import debug @@ -698,7 +732,7 @@ def compiler(example): self.debugger.set_continue() # ==== Example Finished ==== got = self._fakeout.getvalue() - if not isinstance(got, six.text_type): + if not isinstance(got, str): # On Python 3 got should already be unicode text, but on Python # 2 it is not. For comparison's sake we want the unicode text # decoded from UTF-8. If there was some error such that the @@ -721,7 +755,7 @@ def compiler(example): else: exc_msg = traceback.format_exception_only(*exception[:2])[-1] - if six.PY3 and example.exc_msg is not None: + if example.exc_msg is not None: # On Python 3 the exception repr often includes the # exception's full module name (for non-builtin # exceptions), whereas on Python 2 does not, so we @@ -755,25 +789,7 @@ def compiler(example): break if not quiet: - exc_tb = doctest._exception_traceback(exception) - if not isinstance(exc_tb, six.text_type): - # On Python 2, if the traceback contains non-ASCII - # text we can get a UnicodeDecodeError here if we - # don't explicitly decode it first; first try utf-8 - # and then fall back on latin-1. - try: - exc_tb = exc_tb.decode('utf-8') - except UnicodeDecodeError: - exc_tb = exc_tb.decode('latin-1') - - got += exc_tb - - if not isinstance(exc_msg, six.text_type): - # Same here as above - try: - exc_msg = exc_msg.decode('utf-8') - except UnicodeDecodeError: - exc_msg = exc_msg.decode('latin-1') + got += doctest._exception_traceback(exception) # If `example.exc_msg` is None, then we weren't expecting # an exception. @@ -867,7 +883,7 @@ def run(self, test, compileflags=None, out=None, clear_globs=True): TestResults(failed=0, attempted=4) """ self.setters = {} - randstate.set_random_seed(0) + randstate.set_random_seed(self.options.random_seed) warnings.showwarning = showwarning_with_traceback self.running_doctest_digest = hashlib.md5() self.test = test @@ -1645,7 +1661,7 @@ def __init__(self, controller): """ self.controller = controller - init_sage() + init_sage(controller) def serial_dispatch(self): """ @@ -1991,10 +2007,7 @@ def sel_exit(): # Hack to ensure multiprocessing leaves these processes # alone (in particular, it doesn't wait for them when we # exit). - if six.PY2: - p = multiprocessing.current_process() - else: - p = multiprocessing.process + p = multiprocessing.process assert hasattr(p, '_children') p._children = set() @@ -2037,7 +2050,7 @@ class DocTestWorker(multiprocessing.Process): """ The DocTestWorker process runs one :class:`DocTestTask` for a given source. It returns messages about doctest failures (or all tests if - verbose doctesting) though a pipe and returns results through a + verbose doctesting) through a pipe and returns results through a ``multiprocessing.Queue`` instance (both these are created in the :meth:`start` method). @@ -2277,7 +2290,6 @@ def save_result_output(self): This method is called from the parent process, not from the subprocess. """ - from six.moves.queue import Empty try: self.result = self.result_queue.get(block=False) except Empty: @@ -2405,19 +2417,17 @@ class DocTestTask(object): ['cputime', 'err', 'failures', 'optionals', 'tests', 'walltime', 'walltime_skips'] """ - if six.PY2: - extra_globals = {} - else: - extra_globals = {'long': int} + extra_globals = {} """ Extra objects to place in the global namespace in which tests are run. Normally this should be empty but there are special cases where it may be useful. - In particular, on Python 3 add ``long`` as an alias for ``int`` so that - tests that use the ``long`` built-in (of which there are many) still pass. - We do this so that the test suite can run on Python 3 while Python 2 is - still the default. + For example, in Sage versions 9.1 and earlier, on Python 3 add + ``long`` as an alias for ``int`` so that tests that use the + ``long`` built-in (of which there are many) still pass. We did + this so that the test suite could run on Python 3 while Python 2 + was still the default. """ def __init__(self, source): @@ -2530,12 +2540,10 @@ def _run(self, runner, options, results): """ Actually run the doctests with the right set of globals """ - if self.source.basename.startswith("sagenb."): - import sage.all_notebook as sage_all - else: - # Import Jupyter globals to doctest the Jupyter - # implementation of widgets and interacts - import sage.repl.ipython_kernel.all_jupyter as sage_all + # Import Jupyter globals to doctest the Jupyter + # implementation of widgets and interacts + from importlib import import_module + sage_all = import_module(options.environment) dict_all = sage_all.__dict__ # Remove '__package__' item from the globals since it is not # always in the globals in an actual Sage session. diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 2f00a37679c..a44b95fb146 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -23,9 +23,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from sage.misc.six import u -import six -from six import text_type import re import doctest @@ -39,6 +36,11 @@ float_regex = re.compile(r'\s*([+-]?\s*((\d*\.?\d+)|(\d+\.?))([eE][+-]?\d+)?)') optional_regex = re.compile(r'(py2|py3|long time|not implemented|not tested|known bug)|([^ a-z]\s*optional\s*[:-]*((\s|\w)*))') +# Version 4.65 of glpk prints the warning "Long-step dual simplex will +# be used" frequently. When Sage uses a system installation of glpk +# which has not been patched, we need to ignore that message. +# See :trac:`29317`. +glpk_simplex_warning_regex = re.compile(r'(Long-step dual simplex will be used)') find_sage_prompt = re.compile(r"^(\s*)sage: ", re.M) find_sage_continuation = re.compile(r"^(\s*)\.\.\.\.:", re.M) find_python_continuation = re.compile(r"^(\s*)\.\.\.([^\.])", re.M) @@ -51,21 +53,39 @@ backslash_replacer = re.compile(r"""(\s*)sage:(.*)\\\ * \ *(((\.){4}:)|((\.){3}))?\ *""") -# Use this real interval field for doctest tolerances. It allows large -# numbers like 1e1000, it parses strings with spaces like RIF(" - 1 ") -# out of the box and it carries a lot of precision. The latter is -# useful for testing libraries using arbitrary precision but not -# guaranteed rounding such as PARI. We use 1044 bits of precision, -# which should be good to deal with tolerances on numbers computed with -# 1024 bits of precision. -# -# The interval approach also means that we do not need to worry about -# rounding errors and it is also very natural to see a number with -# tolerance as an interval. -# We need to import from sage.all to avoid circular imports. -from sage.all import RealIntervalField -RIFtol = RealIntervalField(1044) +_RIFtol = None + +def RIFtol(*args): + """ + Create an element of the real interval field used for doctest tolerances. + + It allows large numbers like 1e1000, it parses strings with spaces + like ``RIF(" - 1 ")`` out of the box and it carries a lot of + precision. The latter is useful for testing libraries using + arbitrary precision but not guaranteed rounding such as PARI. We use + 1044 bits of precision, which should be good to deal with tolerances + on numbers computed with 1024 bits of precision. + + The interval approach also means that we do not need to worry about + rounding errors and it is also very natural to see a number with + tolerance as an interval. + + EXAMPLES:: + sage: from sage.doctest.parsing import RIFtol + sage: RIFtol(-1, 1) + 0.? + sage: RIFtol(" - 1 ") + -1 + sage: RIFtol("1e1000") + 1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000?e1000 + """ + global _RIFtol + if _RIFtol is None: + # We need to import from sage.all to avoid circular imports. + from sage.all import RealIntervalField + _RIFtol = RealIntervalField(1044) + return _RIFtol(*args) # This is the correct pattern to match ISO/IEC 6429 ANSI escape sequences: # @@ -114,7 +134,7 @@ def remove_unicode_u(string): sage: print(remu(euro)) '€' """ - stripped, replacements = cython_strip_string_literals(u(string), + stripped, replacements = cython_strip_string_literals(string, "__remove_unicode_u") string = stripped.replace('u"', '"').replace("u'", "'") for magic, literal in replacements.items(): @@ -179,13 +199,13 @@ def normalize_long_repr(s): representations of long objects from strings containing a long repr. EXAMPLES:: + sage: from sage.doctest.parsing import normalize_long_repr sage: normalize_long_repr('10L') '10' sage: normalize_long_repr('[10L, -10L, +10L, "ALL"]') '[10, -10, +10, "ALL"]' """ - return _long_repr_re.sub(lambda m: m.group(1), s) @@ -252,25 +272,19 @@ def subst(m): # application. # For example, on Python 3 we strip all u prefixes from unicode strings in the # expected output, because we never expect to see those on Python 3. -if six.PY2: - _repr_fixups = [ - (lambda g, w: ' """ got = self.human_readable_escape_sequences(got) + got = glpk_simplex_warning_regex.sub('', got) if isinstance(want, MarkedOutput): if want.random: return True diff --git a/src/sage/doctest/reporting.py b/src/sage/doctest/reporting.py index 60571e9044e..ec6da712056 100644 --- a/src/sage/doctest/reporting.py +++ b/src/sage/doctest/reporting.py @@ -184,6 +184,8 @@ def report_head(self, source): cmd += " --warn-long" if warnlong != 1.0: cmd += " %.1f"%(warnlong) + seed = self.controller.options.random_seed + cmd += " --random-seed={}".format(seed) cmd += " " + source.printpath return cmd diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index 7c950f2465a..db45e3e845a 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -561,7 +561,7 @@ def __iter__(self): sage: L = list(FDS) Traceback (most recent call last): ... - UnicodeDecodeError: 'utf...8' codec can't decode byte 0xf4 in position 18: invalid continuation byte + UnicodeDecodeError: 'utf...8' codec can...t decode byte 0xf4 in position 18: invalid continuation byte This works if we add a PEP 0263 encoding declaration:: @@ -769,7 +769,7 @@ def _test_enough_doctests(self, check_extras=True, verbose=True): ....: _, ext = os.path.splitext(F) ....: if ext in ('.py', '.pyx', '.pxd', '.pxi', '.sage', '.spyx', '.rst'): ....: filename = os.path.join(path, F) - ....: FDS = FileDocTestSource(filename, DocTestDefaults(long=True,optional=True)) + ....: FDS = FileDocTestSource(filename, DocTestDefaults(long=True, optional=True, force_lib=True)) ....: FDS._test_enough_doctests(verbose=False) There are 3 unexpected tests being run in sage/doctest/parsing.py There are 1 unexpected tests being run in sage/doctest/reporting.py @@ -808,13 +808,14 @@ def _test_enough_doctests(self, check_extras=True, verbose=True): actual = [] tests, _ = self.create_doctests({}) for dt in tests: - if len(dt.examples) > 0: + if dt.examples: for ex in dt.examples[:-1]: # the last entry is a sig_on_count() actual.append(dt.lineno + ex.lineno + 1) - shortfall = sorted(list(set(expected).difference(set(actual)))) - extras = sorted(list(set(actual).difference(set(expected)))) + shortfall = sorted(set(expected).difference(set(actual))) + extras = sorted(set(actual).difference(set(expected))) if len(actual) == len(expected): - if len(shortfall) == 0: return + if not shortfall: + return dif = extras[0] - shortfall[0] for e, s in zip(extras[1:],shortfall[1:]): if dif != e - s: @@ -834,6 +835,7 @@ def _test_enough_doctests(self, check_extras=True, verbose=True): if check_extras and extras: print(" Tests on lines %s seem extraneous" % (", ".join([str(n) for n in extras]))) + class SourceLanguage: """ An abstract class for functions that depend on the programming language of a doctest source. diff --git a/src/sage/doctest/test.py b/src/sage/doctest/test.py index df3ad0108b8..a08df8074ce 100644 --- a/src/sage/doctest/test.py +++ b/src/sage/doctest/test.py @@ -23,20 +23,20 @@ Check that :trac:`2235` has been fixed:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "longtime.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "longtime.rst"], **kwds) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 longtime.rst + sage -t --warn-long 0.0 --random-seed=0 longtime.rst [0 tests, ...s] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- ... 0 - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "-l", "longtime.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "-l", "longtime.rst"], **kwds) # long time Running doctests... Doctesting 1 file. - sage -t --long --warn-long 0.0 longtime.rst + sage -t --long --warn-long 0.0 --random-seed=0 longtime.rst [1 test, ...s] ---------------------------------------------------------------------- All tests passed! @@ -46,10 +46,10 @@ Check handling of tolerances:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "tolerance.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "tolerance.rst"], **kwds) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 tolerance.rst + sage -t --warn-long 0.0 --random-seed=0 tolerance.rst ********************************************************************** File "tolerance.rst", line ..., in sage.doctest.tests.tolerance Failed example: @@ -115,10 +115,10 @@ Test the ``--initial`` option:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "-i", "initial.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "-i", "initial.rst"], **kwds) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 initial.rst + sage -t --warn-long 0.0 --random-seed=0 initial.rst ********************************************************************** File "initial.rst", line 4, in sage.doctest.tests.initial Failed example: @@ -138,17 +138,17 @@ ********************************************************************** ... ---------------------------------------------------------------------- - sage -t --warn-long 0.0 initial.rst # 5 doctests failed + sage -t --warn-long 0.0 --random-seed=0 initial.rst # 5 doctests failed ---------------------------------------------------------------------- ... 1 Test the ``--exitfirst`` option:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--exitfirst", "initial.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "--exitfirst", "initial.rst"], **kwds) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 initial.rst + sage -t --warn-long 0.0 --random-seed=0 initial.rst ********************************************************************** File "initial.rst", line 4, in sage.doctest.tests.initial Failed example: @@ -160,7 +160,7 @@ ********************************************************************** ... ---------------------------------------------------------------------- - sage -t --warn-long 0.0 initial.rst # 1 doctest failed + sage -t --warn-long 0.0 --random-seed=0 initial.rst # 1 doctest failed ---------------------------------------------------------------------- ... 1 @@ -170,26 +170,26 @@ sage: from copy import deepcopy sage: kwds2 = deepcopy(kwds) sage: kwds2['env']['SAGE_TIMEOUT'] = "3" - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "99seconds.rst"], **kwds2) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "99seconds.rst"], **kwds2) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 99seconds.rst + sage -t --warn-long 0.0 --random-seed=0 99seconds.rst Timed out ********************************************************************** Tests run before process (pid=...) timed out: ... ---------------------------------------------------------------------- - sage -t --warn-long 0.0 99seconds.rst # Timed out + sage -t --warn-long 0.0 --random-seed=0 99seconds.rst # Timed out ---------------------------------------------------------------------- ... 4 Test handling of ``KeyboardInterrupt`` in doctests:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "keyboardinterrupt.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "keyboardinterrupt.rst"], **kwds) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 keyboardinterrupt.rst + sage -t --warn-long 0.0 --random-seed=0 keyboardinterrupt.rst ********************************************************************** File "keyboardinterrupt.rst", line 11, in sage.doctest.tests.keyboardinterrupt Failed example: @@ -201,14 +201,14 @@ ********************************************************************** ... ---------------------------------------------------------------------- - sage -t --warn-long 0.0 keyboardinterrupt.rst # 1 doctest failed + sage -t --warn-long 0.0 --random-seed=0 keyboardinterrupt.rst # 1 doctest failed ---------------------------------------------------------------------- ... 1 Interrupt the doctester:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "interrupt.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "interrupt.rst"], **kwds) # long time Running doctests... Doctesting 1 file. Killing test interrupt.rst @@ -253,10 +253,10 @@ Test a doctest failing with ``abort()``:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "abort.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "abort.rst"], **kwds) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 abort.rst + sage -t --warn-long 0.0 --random-seed=0 abort.rst Killed due to abort ********************************************************************** Tests run before process (pid=...) failed: @@ -269,17 +269,17 @@ ------------------------------------------------------------------------ ... ---------------------------------------------------------------------- - sage -t --warn-long 0.0 abort.rst # Killed due to abort + sage -t --warn-long 0.0 --random-seed=0 abort.rst # Killed due to abort ---------------------------------------------------------------------- ... 16 A different kind of crash:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "fail_and_die.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "fail_and_die.rst"], **kwds) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 fail_and_die.rst + sage -t --warn-long 0.0 --random-seed=0 fail_and_die.rst ********************************************************************** File "fail_and_die.rst", line 5, in sage.doctest.tests.fail_and_die Failed example: @@ -293,17 +293,17 @@ Tests run before process (pid=...) failed: ... ---------------------------------------------------------------------- - sage -t --warn-long 0.0 fail_and_die.rst # Killed due to kill signal + sage -t --warn-long 0.0 --random-seed=0 fail_and_die.rst # Killed due to kill signal ---------------------------------------------------------------------- ... 16 Test that ``sig_on_count`` is checked correctly:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "sig_on.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "sig_on.rst"], **kwds) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 sig_on.rst + sage -t --warn-long 0.0 --random-seed=0 sig_on.rst ********************************************************************** File "sig_on.rst", line 6, in sage.doctest.tests.sig_on Failed example: @@ -317,7 +317,7 @@ 1 of 5 in sage.doctest.tests.sig_on [3 tests, 1 failure, ...] ---------------------------------------------------------------------- - sage -t --warn-long 0.0 sig_on.rst # 1 doctest failed + sage -t --warn-long 0.0 --random-seed=0 sig_on.rst # 1 doctest failed ---------------------------------------------------------------------- ... 1 @@ -325,12 +325,12 @@ Test logfiles in serial and parallel mode (see :trac:`19271`):: sage: t = tmp_filename() - sage: subprocess.call(["sage", "-t", "--serial", "--warn-long", "0", "simple_failure.rst", "--logfile", t], stdout=open(os.devnull, "w"), **kwds) # long time + sage: subprocess.call(["sage", "-t", "--serial", "--warn-long", "0", "--random-seed=0", "simple_failure.rst", "--logfile", t], stdout=open(os.devnull, "w"), **kwds) # long time 1 sage: print(open(t).read()) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 simple_failure.rst + sage -t --warn-long 0.0 --random-seed=0 simple_failure.rst ********************************************************************** File "simple_failure.rst", line 7, in sage.doctest.tests.simple_failure Failed example: @@ -344,16 +344,16 @@ 1 of 5 in sage.doctest.tests.simple_failure [4 tests, 1 failure, ...] ---------------------------------------------------------------------- - sage -t --warn-long 0.0 simple_failure.rst # 1 doctest failed + sage -t --warn-long 0.0 --random-seed=0 simple_failure.rst # 1 doctest failed ---------------------------------------------------------------------- ... - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "simple_failure.rst", "--logfile", t], stdout=open(os.devnull, "w"), **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "simple_failure.rst", "--logfile", t], stdout=open(os.devnull, "w"), **kwds) # long time 1 sage: print(open(t).read()) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 simple_failure.rst + sage -t --warn-long 0.0 --random-seed=0 simple_failure.rst ********************************************************************** File "simple_failure.rst", line 7, in sage.doctest.tests.simple_failure Failed example: @@ -367,16 +367,16 @@ 1 of 5 in sage.doctest.tests.simple_failure [4 tests, 1 failure, ...] ---------------------------------------------------------------------- - sage -t --warn-long 0.0 simple_failure.rst # 1 doctest failed + sage -t --warn-long 0.0 --random-seed=0 simple_failure.rst # 1 doctest failed ---------------------------------------------------------------------- ... Test the ``--debug`` option:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--debug", "simple_failure.rst"], stdin=open(os.devnull), **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "--debug", "simple_failure.rst"], stdin=open(os.devnull), **kwds) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 simple_failure.rst + sage -t --warn-long 0.0 --random-seed=0 simple_failure.rst ********************************************************************** File "simple_failure.rst", line 7, in sage.doctest.tests.simple_failure Failed example: @@ -399,14 +399,14 @@ 1 of 5 in sage.doctest.tests.simple_failure [4 tests, 1 failure, ...] ---------------------------------------------------------------------- - sage -t --warn-long 0.0 simple_failure.rst # 1 doctest failed + sage -t --warn-long 0.0 --random-seed=0 simple_failure.rst # 1 doctest failed ---------------------------------------------------------------------- ... 1 Test running under gdb, without and with a timeout:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--gdb", "1second.rst"], stdin=open(os.devnull), **kwds) # long time, optional: gdb + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "--gdb", "1second.rst"], stdin=open(os.devnull), **kwds) # long time, optional: gdb exec gdb ... Running doctests... Doctesting 1 file... @@ -420,7 +420,7 @@ gdb might need a long time to start up, so we allow 30 seconds:: - sage: subprocess.call(["sage", "-t", "--gdb", "--warn-long", "0", "-T30", "99seconds.rst"], stdin=open(os.devnull), **kwds) # long time, optional: gdb + sage: subprocess.call(["sage", "-t", "--gdb", "--warn-long", "0", "--random-seed=0", "-T30", "99seconds.rst"], stdin=open(os.devnull), **kwds) # long time, optional: gdb exec gdb ... Running doctests... Timed out @@ -428,10 +428,10 @@ Test the ``--show-skipped`` option:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--show-skipped", "show_skipped.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "--show-skipped", "show_skipped.rst"], **kwds) # long time Running doctests ... Doctesting 1 file. - sage -t --warn-long 0.0 show_skipped.rst + sage -t --warn-long 0.0 --random-seed=0 show_skipped.rst 1 unlabeled test not run 2 tests not run due to known bugs 1 gap test not run @@ -447,10 +447,10 @@ Optional tests are run correctly:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--long", "--show-skipped", "--optional=sage,gap", "show_skipped.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--long", "--random-seed=0", "--show-skipped", "--optional=sage,gap", "show_skipped.rst"], **kwds) # long time Running doctests ... Doctesting 1 file. - sage -t --long --warn-long 0.0 show_skipped.rst + sage -t --long --warn-long 0.0 --random-seed=0 show_skipped.rst 1 unlabeled test not run 2 tests not run due to known bugs 1 not tested test not run @@ -462,10 +462,10 @@ ... 0 - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--long", "--show-skipped", "--optional=gAp", "show_skipped.rst"], **kwds) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--long", "--random-seed=0", "--show-skipped", "--optional=gAp", "show_skipped.rst"], **kwds) # long time Running doctests ... Doctesting 1 file. - sage -t --long --warn-long 0.0 show_skipped.rst + sage -t --long --warn-long 0.0 --random-seed=0 show_skipped.rst 1 unlabeled test not run 2 tests not run due to known bugs 1 not tested test not run @@ -480,7 +480,7 @@ Test an invalid value for ``--optional``:: - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--optional=bad-option", "show_skipped.rst"], **kwds) + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "--optional=bad-option", "show_skipped.rst"], **kwds) Traceback (most recent call last): ... ValueError: invalid optional tag 'bad-option' @@ -494,10 +494,10 @@ sage: from copy import deepcopy sage: kwds2 = deepcopy(kwds) sage: kwds2['env']['DOCTEST_DELETE_FILE'] = F - sage: subprocess.call(["sage", "-t", "--warn-long", "0", "atexit.rst"], **kwds2) # long time + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "atexit.rst"], **kwds2) # long time Running doctests... Doctesting 1 file. - sage -t --warn-long 0.0 atexit.rst + sage -t --warn-long 0.0 --random-seed=0 atexit.rst [3 tests, ... s] ---------------------------------------------------------------------- All tests passed! @@ -519,9 +519,44 @@ sage: ok = True sage: from sage.cpython.string import bytes_to_str sage: if system() == "Linux": - ....: P = subprocess.Popen(["sage", "-t", "--warn-long", "0", "--memlimit=2000", "memlimit.rst"], stdout=subprocess.PIPE, **kwds) + ....: P = subprocess.Popen(["sage", "-t", "--warn-long", "0", "--random-seed=0", "--memlimit=2000", "memlimit.rst"], stdout=subprocess.PIPE, **kwds) ....: out, err = P.communicate() ....: ok = ("MemoryError: failed to allocate" in bytes_to_str(out)) sage: ok or out True + +Test that random tests are reproducible:: + + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=0", "random_seed.rst"], **kwds) # long time + Running doctests... + Doctesting 1 file. + sage -t --warn-long 0.0 --random-seed=0 random_seed.rst + ********************************************************************** + File "random_seed.rst", line 3, in sage.doctest.tests.random_seed + Failed example: + randint(5, 10) + Expected: + 9 + Got: + 5 + ********************************************************************** + 1 item had failures: + 1 of 2 in sage.doctest.tests.random_seed + [1 test, 1 failure, ...s] + ---------------------------------------------------------------------- + sage -t --warn-long 0.0 --random-seed=0 random_seed.rst # 1 doctest failed + ---------------------------------------------------------------------- + ... + 1 + sage: subprocess.call(["sage", "-t", "--warn-long", "0", "--random-seed=1", "random_seed.rst"], **kwds) # long time + Running doctests... + Doctesting 1 file. + sage -t --warn-long 0.0 --random-seed=1 random_seed.rst + [1 test, ...s] + ---------------------------------------------------------------------- + All tests passed! + ---------------------------------------------------------------------- + ... + 0 + """ diff --git a/src/sage/doctest/tests/nodoctest.py b/src/sage/doctest/tests/nodoctest similarity index 100% rename from src/sage/doctest/tests/nodoctest.py rename to src/sage/doctest/tests/nodoctest diff --git a/src/sage/doctest/tests/random_seed.rst b/src/sage/doctest/tests/random_seed.rst new file mode 100644 index 00000000000..d2caec53d3a --- /dev/null +++ b/src/sage/doctest/tests/random_seed.rst @@ -0,0 +1,4 @@ +We test that random tests are reproducible:: + + sage: randint(5, 10) + 9 diff --git a/src/sage/doctest/util.py b/src/sage/doctest/util.py index f980982715e..cd1706e011d 100644 --- a/src/sage/doctest/util.py +++ b/src/sage/doctest/util.py @@ -19,7 +19,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six import iteritems from sage.misc.misc import walltime, cputime @@ -81,7 +80,7 @@ def dict_difference(self, other): {'foobar': 'hello', 'timeout': 100} """ D = dict() - for k, v in iteritems(self): + for k, v in self.items(): try: if other[k] == v: continue diff --git a/src/sage/dynamics/arithmetic_dynamics/affine_ds.py b/src/sage/dynamics/arithmetic_dynamics/affine_ds.py index 042706bb510..6aef7fe150e 100644 --- a/src/sage/dynamics/arithmetic_dynamics/affine_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/affine_ds.py @@ -233,6 +233,7 @@ def __classcall_private__(cls, morphism_or_polys, domain=None): ValueError: Number of polys does not match dimension of Affine Space of dimension 3 over Rational Field :: + sage: A. = AffineSpace(QQ,2) sage: f = DynamicalSystem_affine([CC.0*x^2, y^2], domain=A) Traceback (most recent call last): diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py index d501a2c4b1d..4584510aeb4 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py @@ -18,7 +18,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from copy import copy from sage.combinat.subset import Subsets @@ -524,21 +523,20 @@ def valid_automorphisms(automorphisms_CRT, rational_function, ht_bound, M, # multiply lift by appropriate scalar matrices and adjust (mod M) # to find an element of minimal height. These will have # coefficients in [-M/2, M/2) - for scalar in range(1, M): - if gcd(scalar, M) == 1: - new_lift = [scalar*x - (scalar*x/M).round()*M - for x in init_lift] - g = gcd(new_lift) - new_lift = [x // g for x in new_lift] - if all(abs(x) <= ht_bound for x in new_lift): - a, b, c, d = new_lift - f = (a*z + b) / (c*z + d) - if rational_function(f(z)) == f(rational_function(z)): - if return_functions: - valid_auto.append(f) - else: - valid_auto.append(matrix(ZZ,2,2,new_lift)) - break + for scalar in M.coprime_integers(M): + new_lift = [scalar*x - (scalar*x/M).round()*M + for x in init_lift] + g = gcd(new_lift) + new_lift = [x // g for x in new_lift] + if all(abs(x) <= ht_bound for x in new_lift): + a, b, c, d = new_lift + f = (a*z + b) / (c*z + d) + if rational_function(f(z)) == f(rational_function(z)): + if return_functions: + valid_auto.append(f) + else: + valid_auto.append(matrix(ZZ,2,2,new_lift)) + break return valid_auto @@ -1391,14 +1389,18 @@ def order_p_automorphisms(rational_function, pre_image): u = F(1) / (z - pt[0]) u_inv = pt[0] + F(1)/z for i in range(1,m): - if M[0] == [F(1),F(0)]: uy1 = 0 - else: uy1 = u(M[0][0]) - if M[i] == [F(1),F(0)]: uy2 = 0 - else: uy2 = u(M[i][0]) + if M[0] == [F(1),F(0)]: + uy1 = 0 + else: + uy1 = u(M[0][0]) + if M[i] == [F(1),F(0)]: + uy2 = 0 + else: + uy2 = u(M[i][0]) s = u_inv( u(z) + uy2 - uy1 ) if s(phi(z)) == phi(s(z)): automorphisms_p.append(s) - elif T==[]: + elif not T: # create the extension field generated by pre-images of the unique fixed point T_poly = pre_image[0][2] e = lcm([x[0].degree() for x in T_poly.factor()])*F.degree() diff --git a/src/sage/dynamics/arithmetic_dynamics/generic_ds.py b/src/sage/dynamics/arithmetic_dynamics/generic_ds.py index f0bea1c3fa0..10cd80b6da5 100644 --- a/src/sage/dynamics/arithmetic_dynamics/generic_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/generic_ds.py @@ -29,7 +29,6 @@ class initialization directly. from __future__ import absolute_import, print_function from sage.categories.homset import End -from six import add_metaclass from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.schemes.generic.morphism import SchemeMorphism_polynomial from sage.schemes.affine.affine_space import is_AffineSpace @@ -40,8 +39,8 @@ class initialization directly. from sage.rings.rational_field import QQ from copy import copy -@add_metaclass(InheritComparisonClasscallMetaclass) -class DynamicalSystem(SchemeMorphism_polynomial): +class DynamicalSystem(SchemeMorphism_polynomial, + metaclass=InheritComparisonClasscallMetaclass): r""" Base class for dynamical systems of schemes. @@ -525,7 +524,7 @@ def field_of_definition_periodic(self, n, formal=False, return_embedding=False, CR = space.coordinate_ring() if CR.is_field(): #want the polynomial ring not the fraction field - CR = CR.ring() + CR = CR.ring() x = CR.gen(0) if formal: poly = ds.dynatomic_polynomial(n) @@ -618,9 +617,9 @@ def field_of_definition_preimage(self, point, n, return_embedding=False, simplif except TypeError: raise TypeError('`point` must be in {}'.format(ds.domain())) if space.is_projective(): - ds = ds.dehomogenize(1) + ds = ds.dehomogenize(1) else: - point = (point[0],1) + point = (point[0],1) fn = ds.nth_iterate_map(n) f, g = fn[0].numerator(), fn[0].denominator() CR = space.coordinate_ring() diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 5b095894885..878ec1fff89 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -83,7 +83,7 @@ class initialization directly. from sage.rings.polynomial.multi_polynomial_ring_base import is_MPolynomialRing from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.polynomial.polynomial_ring import is_PolynomialRing -from sage.rings.qqbar import QQbar +from sage.rings.qqbar import QQbar, number_field_elements_from_algebraics from sage.rings.quotient_ring import QuotientRing_generic from sage.rings.rational_field import QQ from sage.rings.real_double import RDF @@ -2863,6 +2863,7 @@ def minimal_model(self, return_transformation=False, prime_list=None, algorithm= TypeError: affine minimality is only considered for maps not of the form f or 1/f for a polynomial f :: + sage: P. = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([2*x^2, y^2]) sage: f.minimal_model(prime_list=[0]) @@ -3282,6 +3283,83 @@ def critical_points(self, R=None): crit_points = [P(Q) for Q in X.rational_points()] return crit_points + def ramification_type(self, R=None, stable=True): + r""" + Return the ramification type of endomorphisms of `\mathbb{P}^1`. + + Only branch points defined over the ring ``R`` contribute to + the ramification type if specified, otherwise ``R`` is the + ring of definition for ``self``. + + Note that branch points defined over ``R`` may not be + geometric points if stable not set to ``True``. + + If ``R`` is specified, ``stable`` is ignored. + + If ``stable``, then this will return the ramification type + over an extension which splits the Galois orbits of critical + points. + + INPUT: + + - ``R`` -- ring or morphism (optional) + - ``split`` -- boolean (optional) + + OUTPUT: + + list of lists, each term being the list of ramification indices + in the pre-images of one critical value + + EXAMPLES:: + + sage: P. = ProjectiveSpace(QQ, 1) + sage: F = DynamicalSystem_projective([x^4, y^4]) + sage: F.ramification_type() + [[4], [4]] + + sage: P. = ProjectiveSpace(QQ, 1) + sage: F = DynamicalSystem_projective([x^3, 4*y^3 - 3*x^2*y]) + sage: F.ramification_type() + [[2], [2], [3]] + + sage: P. = ProjectiveSpace(QQ, 1) + sage: F = DynamicalSystem_projective([(x + y)^4, 16*x*y*(x-y)^2]) + sage: F.ramification_type() + [[2], [2, 2], [4]] + + sage: P. = ProjectiveSpace(QQ, 1) + sage: F = DynamicalSystem_projective([(x + y)*(x - y)^3, y*(2*x+y)^3]) + sage: F.ramification_type() + [[3], [3], [3]] + + sage: F = DynamicalSystem_projective([x^3-2*x*y^2 + 2*y^3, y^3]) + sage: F.ramification_type() + [[2], [2], [3]] + sage: F.ramification_type(R=F.base_ring()) + [[2], [3]] + + """ + # Change base ring if specified. + if R is None: + if stable: + L,phi = self.field_of_definition_critical(return_embedding=True) + F = self.change_ring(phi) + else: + F = self + else: + F = self.change_ring(R) + + C = F.critical_subscheme() + ram_type = {} + fc = C.defining_ideal().gens()[0] + for f, e in fc.factor(): + c = F(F.domain().subscheme(f)) # critical value + if c in ram_type: + ram_type[c].append(e + 1) + else: + ram_type[c] = [e + 1] + return sorted(ram_type.values()) + def is_postcritically_finite(self, err=0.01, use_algebraic_closure=True): r""" Determine if this dynamical system is post-critically finite. @@ -3299,7 +3377,7 @@ def is_postcritically_finite(self, err=0.01, use_algebraic_closure=True): - ``err`` -- (default: 0.01) positive real number - ``use_algebraic_closure`` -- boolean (default: True) -- If True uses the - algebraic closure. If False, uses the smalest extension of the base field + algebraic closure. If False, uses the smallest extension of the base field containing all the critical points. OUTPUT: boolean @@ -3409,7 +3487,7 @@ def critical_point_portrait(self, check=True, use_algebraic_closure=True): - ``check`` -- boolean (default: True) - ``use_algebraic_closure`` -- boolean (default: True) -- If True uses the - algebraic closure. If False, uses the smalest extension of the base field + algebraic closure. If False, uses the smallest extension of the base field containing all the critical points. OUTPUT: a digraph @@ -3532,7 +3610,7 @@ def critical_height(self, **kwds): - ``error_bound`` -- (optional) a positive real number - ``use_algebraic_closure`` -- boolean (default: True) -- If True uses the - algebraic closure. If False, uses the smalest extension of the base field + algebraic closure. If False, uses the smallest extension of the base field containing all the critical points. OUTPUT: real number @@ -4100,7 +4178,7 @@ def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closur per point or one per cycle - ``use_algebraic_closure`` -- boolean (default: True) -- If True uses the - algebraic closure. If False, uses the smalest extension of the base field + algebraic closure. If False, uses the smallest extension of the base field containing all the critical points. OUTPUT: a list of field elements @@ -4253,7 +4331,7 @@ def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closur points = [] - minfty = min([e[1] for e in F.exponents()]) # include the point at infinity with the right multiplicity + minfty = min(ex[1] for ex in F.exponents()) # include the point at infinity with the right multiplicity for i in range(minfty): points.append(PS([1,0])) @@ -4793,7 +4871,7 @@ def reduced_form(self, **kwds): ) """ if self.domain().ambient_space().dimension_relative() != 1: - return NotImplementedError('only implmeneted for dimension 1') + return NotImplementedError('only implemented for dimension 1') return_conjugation = kwds.get('return_conjugation', True) emb = kwds.get('emb', None) prec = kwds.get('prec', 300) @@ -4802,7 +4880,7 @@ def reduced_form(self, **kwds): dynatomic = algorithm = kwds.get('dynatomic', True) smallest_coeffs = kwds.get('smallest_coeffs', True) if smallest_coeffs: - if self.base_ring() not in [ZZ,QQ]: + if self.base_ring() not in [ZZ, QQ]: raise NotImplementedError("smallest coeff only over ZZ or QQ") check_min = kwds.get('check_minimal', True) from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import smallest_dynamical @@ -5210,8 +5288,7 @@ def all_periodic_points(self, **kwds): specifies modulo which prime to try and perform the lifting - ``period_degree_bounds`` -- (default: ``[4,4]``) a pair of positive integers - (max period, max degree) for which the dynatomic polynomial should be solved - for when in dimension 1 + (max period, max degree) for which the dynatomic polynomial should be solved for - ``algorithm`` -- (optional) specifies which algorithm to use; current options are `dynatomic` and `lifting`; defaults to solving the @@ -6887,6 +6964,112 @@ def reduce_base_field(self): """ return self.as_scheme_morphism().reduce_base_field().as_dynamical_system() + def is_newton(self, return_conjugation=False): + r""" + Return whether ``self`` is a Newton map. + + A map `g` is *Newton* if it is conjugate to a map of the form + `f(z) = z - \frac{p(z)}{p'(z)}` after dehomogenization, + where `p(z)` is a squarefree polynomial. + + INPUT: + + - ``return_conjugation`` -- (default: ``False``) if the map is Newton + and ``True``, then return the conjugation that moves this map to + the above form + + OUTPUT: + + A Boolean. If ``return_conjugation`` is ``True``, then this also + returns the conjugation as a matrix if ``self`` is Newton or ``None`` + otherwise. + + The conjugation may be defined over an extension if the map has + fixed points not defined over the base field. + + EXAMPLES:: + + sage: A. = AffineSpace(QQ, 1) + sage: f = DynamicalSystem_affine([z - (z^2 + 1)/(2*z)]) + sage: F = f.homogenize(1) + sage: F.is_newton(return_conjugation=True) + ( + [1 0] + True, [0 1] + ) + + :: + + sage: A. = AffineSpace(QQ, 1) + sage: f = DynamicalSystem_affine([z^2 + 1]) + sage: F = f.homogenize(1) + sage: F.is_newton() + False + sage: F.is_newton(return_conjugation=True) + (False, None) + + :: + + sage: PP. = ProjectiveSpace(QQ, 1) + sage: F = DynamicalSystem_projective([-4*x^3 - 3*x*y^2, -2*y^3]) + sage: F.is_newton(return_conjugation=True)[1] + [ 0 1] + [-4*a 2*a] + + :: + + sage: K. = CyclotomicField(2*4) + sage: A. = AffineSpace(K, 1) + sage: f = DynamicalSystem_affine(z-(z^3+zeta*z)/(3*z^2+zeta)) + sage: F = f.homogenize(1) + sage: F.is_newton() + True + """ + if self.degree() == 1: + raise NotImplementedError("degree one Newton maps are trivial") + if not self.base_ring() in NumberFields(): + raise NotImplementedError("only implemented over number fields") + # check if Newton map + sigma_1 = self.sigma_invariants(1) + d = ZZ(self.degree()) + Newton_sigma = [d/(d-1)] + [0] * d # almost Newton + if sigma_1 != Newton_sigma: + if return_conjugation: + return False, None + else: + return False + Fbar = self.change_ring(QQbar) + Pbar = Fbar.domain() + fixed = Fbar.periodic_points(1) + for Q in fixed: + if Fbar.multiplier(Q, 1) != 0: + inf = Q + break + if inf != Pbar([1,0]): + # need to move to inf to infinity + fixed.remove(inf) + source = [inf] + fixed[:2] + target = [Pbar([1, 0]), Pbar([0, 1]), Pbar([1, 1])] + M = Pbar.point_transformation_matrix(source, target) + M = M.inverse() + Newton = Fbar.conjugate(M) + K, el, psi = number_field_elements_from_algebraics([t for r in M for t in r]) + M = matrix(M.nrows(), M.ncols(), el) + Newton = Newton._number_field_from_algebraics() + else: + Newton = self + M = matrix(QQ, 2, 2, [1,0,0,1]) + N_aff = Newton.dehomogenize(1) + z = N_aff.domain().gen(0) + Npoly = (z - N_aff[0]).numerator() + if return_conjugation: + if Npoly.derivative(z) == (z - N_aff[0]).denominator(): + return True, M + else: + return False, None + else: + return Npoly.derivative(z) == (z - N_aff[0]).denominator() + class DynamicalSystem_projective_finite_field(DynamicalSystem_projective_field, SchemeMorphism_polynomial_projective_space_finite_field): diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx b/src/sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx index 547044ecb17..e3769c89f54 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx @@ -268,12 +268,12 @@ cpdef _normalize_coordinates(list point, int prime, int len_points): for coefficient in xrange(len_points): point[coefficient] = (point[coefficient] * mod_inverse) % prime - + cpdef _all_periodic_points(self): """ Find all periodic points over a finite field. - EXAMPELS:: + EXAMPLES:: sage: from sage.dynamics.arithmetic_dynamics.projective_ds_helper import _all_periodic_points sage: P. = ProjectiveSpace(GF(7), 1) @@ -298,4 +298,3 @@ cpdef _all_periodic_points(self): if next_element in path: periodic_points += path[path.index(next_element):] return periodic_points - diff --git a/src/sage/dynamics/cellular_automata/all.py b/src/sage/dynamics/cellular_automata/all.py index bee92f850db..792411f5b75 100644 --- a/src/sage/dynamics/cellular_automata/all.py +++ b/src/sage/dynamics/cellular_automata/all.py @@ -1,3 +1,6 @@ +import sage.dynamics.cellular_automata.catalog as cellular_automata + from sage.misc.lazy_import import lazy_import lazy_import("sage.dynamics.cellular_automata.solitons", ["SolitonCellularAutomata", "PeriodicSolitonCellularAutomata"]) + diff --git a/src/sage/dynamics/cellular_automata/catalog.py b/src/sage/dynamics/cellular_automata/catalog.py new file mode 100644 index 00000000000..480f073bf7a --- /dev/null +++ b/src/sage/dynamics/cellular_automata/catalog.py @@ -0,0 +1,33 @@ +r""" +Catalog of Cellular Automata + +The ``cellular_automata`` object may be used to access examples of various +cellular automata currently implemented in Sage. Using tab-completion on +this object is an easy way to discover and quickly create the cellular +automata that are available (as listed here). + +Let ```` indicate pressing the tab key. So begin by typing +``cellular_automata.`` to the see the currently implemented +named cellular automata. + +- :class:`cellular_automata.Elementary + ` +- :class:`cellular_automata.GraftalLace + ` +- :class:`cellular_automata.PeriodicSoliton + ` +- :class:`cellular_automata.Soliton + ` +""" + +from sage.misc.lazy_import import lazy_import +lazy_import('sage.dynamics.cellular_automata.elementary', + 'ElementaryCellularAutomata', 'Elementary',) +lazy_import('sage.dynamics.cellular_automata.glca', + 'GraftalLaceCellularAutomata', 'GraftalLace',) +lazy_import('sage.dynamics.cellular_automata.solitons', + 'SolitonCellularAutomata', 'Soliton') +lazy_import('sage.dynamics.cellular_automata.solitons', + 'PeriodicSolitonCellularAutomata', 'PeriodicSoliton') + +del lazy_import # We remove the object from here so it doesn't appear under tab completion diff --git a/src/sage/dynamics/cellular_automata/elementary.py b/src/sage/dynamics/cellular_automata/elementary.py new file mode 100644 index 00000000000..754012c7df8 --- /dev/null +++ b/src/sage/dynamics/cellular_automata/elementary.py @@ -0,0 +1,609 @@ +# -*- encoding: utf-8 -*- +""" +Elementary Cellular Automata + +AUTHORS: + +- Travis Scrimshaw (2018-07-07): Initial version +""" + +#***************************************************************************** +# Copyright (C) 2018 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.sage_object import SageObject +from sage.typeset.ascii_art import AsciiArt +from sage.typeset.unicode_art import UnicodeArt +from sage.rings.integer_ring import ZZ +from sage.matrix.constructor import matrix +from sage.plot.matrix_plot import matrix_plot +from sage.misc.constant_function import ConstantFunction + +class ElementaryCellularAutomata(SageObject): + r""" + Elementary cellular automata. + + An *elementary cellular automaton* is a 1-dimensional cellular + deterministic automaton with two possible values: `X := \{0,1\}`. + A *state* is therefore a sequence `s \in X^n`, and the *evolution* + of a state `s \to s'` is given for `s'_i` by looking at the values + at positions `s_{i-1}, s_i, s_{i+1}` and is determined by the + *rule* `0 \leq r \leq 255` as follows. Consider the binary + representation `r = b_7 b_6 b_5 b_4 b_3 b_2 b_1 b_0`. Then, we + define `s'_i = b_j`, where `j = s_{i-1} s_i s_{i+1}` is the + corresponding binary representation. In other words, the value + `s'_i` is given according to the following table: + + .. MATH:: + + \begin{array}{cccccccc} + 111 & 110 & 101 & 100 & 011 & 010 & 001 & 000 \\ + b_7 & b_6 & b_5 & b_4 & b_3 & b_2 & b_1 & b_0 + \end{array} + + We consider the boundary values of `s_0 = s_{n+1} = 0`. + + INPUT: + + - ``rule`` -- an integer between 0 and 255 + - ``width`` -- (optional) the width of the ECA + - ``initial_state`` -- (optional) the initial state given + as a list of ``0`` and ``1`` + - ``boundary`` -- (default: ``(0, 0)``) a tuple of the left and right + boundary conditions respectively or ``None`` for periodic boundary + conditions + + Either ``width`` or ``initial_state`` must be given. If ``width`` + is less than the length of ``initial_state``, then ``initial_state`` + has ``0`` prepended so the resulting list has length ``width``. + If only ``width`` is given, then the initial state is constructed + randomly. + + The boundary conditions can either be ``0``, ``1``, or a function that + takes an integer ``n`` corresponding to the state and outputs either + ``0`` or ``1``. + + EXAMPLES: + + We construct an example with rule `r = 90` using `n = 20`. The + initial state consists of a single `1` in the rightmost entry:: + + sage: ECA = cellular_automata.Elementary(90, width=20, initial_state=[1]) + sage: ECA.evolve(20) + sage: ascii_art(ECA) + X + X + X X + X + X X + X X + X X X X + X + X X + X X + X X X X + X X + X X X X + X X X X + X X X X X X X X + X + X X + X X + X X X X + X X + X X X + + We now construct it with different boundary conditions. The first is + with the left boundary being `1` (instead of `0`):: + + sage: ECA = cellular_automata.Elementary(90, width=20, initial_state=[1], boundary=(1,0)) + sage: ECA.evolve(20) + sage: ascii_art(ECA) + X + X X + XX X X + XX X + XXX X X + X XX X X + XXX X X X X + X XX XX X + X XX XXX X X + X XX X XX X X + X XX XX X X X + X XXX XXXXX X + X X X X XX X X + X X XXXX X X + XX X X X X X X + XX X XX XX XX + XXXX XX XX XXX + X XXXXX XX X XX + XXX X XX XXX + XXX XX X XXX XX XX + X XX XXX X XX XXX + + Now we consider the right boundary as being `1` on every third value:: + + sage: def rbdry(n): return 1 if n % 3 == 0 else 0 + sage: ECA = cellular_automata.Elementary(90, width=20, initial_state=[1], boundary=(0,rbdry)) + sage: ECA.evolve(20) + sage: ascii_art(ECA) + X + X + X X + X X + X XX + X XXX + X XXX + X X XX + X XX XXX + X XXXXX + X XXX XX + X X XX XXXX + X XX XX X + X XXXXXX X + X XXX XXX X + X X XX XX X + X XX XXXXXX X + X XXXXX XXX X + X XXX XX XX X + X X XX XXXXXXX X + XX XX X XXX X + + Lastly we consider it with periodic boundary condition:: + + sage: ECA = cellular_automata.Elementary(90, width=20, initial_state=[1], boundary=None) + sage: ECA.evolve(20) + sage: ascii_art(ECA) + X + X X + X X + X X X X + X X + X X X X + X X X X + X X X X X X X X + X X + X X X X + X X + X X X X + X X X X + X X X X X X X X + X X + X X X X + X X + X X X X + X X X X + X X X X X X X X + X X + + We show the local evolution rules for rule `110`:: + + sage: for t in cartesian_product([[0,1],[0,1],[0,1]]): + ....: ECA = cellular_automata.Elementary(110, list(t)) + ....: ECA.print_states(2) + ....: print('#') + + + # + X + XX + # + X + XX + # + XX + XXX + # + X + X + # + X X + XXX + # + XX + XX + # + XXX + X X + # + + We construct an elementary cellular automaton with a random initial + state with `n = 15` and see the state after `50` evolutions:: + + sage: ECA = cellular_automata.Elementary(26, width=25) + sage: ECA.print_state(50) # random + X X X X X X + + We construct and plot a larger example with rule `60`:: + + sage: ECA = cellular_automata.Elementary(60, width=200) + sage: ECA.evolve(200) + sage: ECA.plot() + Graphics object consisting of 1 graphics primitive + + .. PLOT:: + + ECA = cellular_automata.Elementary(60, width=200) + ECA.evolve(200) + P = ECA.plot() + sphinx_plot(P) + + With periodic boundary condition for rule `90`:: + + sage: ECA = cellular_automata.Elementary(90, initial_state=[1]+[0]*254+[1], boundary=None) + sage: ECA.evolve(256) + sage: ECA.plot() + Graphics object consisting of 1 graphics primitive + + .. PLOT:: + + ECA = cellular_automata.Elementary(90, initial_state=[1]+[0]*254+[1], boundary=None) + ECA.evolve(256) + P = ECA.plot() + sphinx_plot(P) + + REFERENCES: + + :wikipedia:`Elementary_cellular_automaton` + """ + def __init__(self, rule, width=None, initial_state=None, boundary=(0, 0)): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: ECA = cellular_automata.Elementary(110, width=300) + sage: TestSuite(ECA).run() + """ + if rule not in ZZ or rule < 0 or rule > 255: + raise ValueError("invalid rule") + self._rule = ZZ(rule).binary() + # We reverse the rule to make it easier to work with + self._rule = [ZZ(x) for x in reversed('0'*(8-len(self._rule)) + self._rule)] + if isinstance(width, list): + initial_state = width + width = len(initial_state) + if initial_state is None: + self._width = width + initial_state = [ZZ.random_element(0,2) for d in range(width)] + else: + if not all(d in [0,1] for d in initial_state): + raise ValueError("invalid initial state") + initial_state = list(initial_state) # make sure it is a list and a copy + if width is None: + self._width = len(initial_state) + elif width >= len(initial_state): + self._width = width + initial_state = ([0]*(width - len(initial_state)) + + initial_state) + else: + raise ValueError("the width must be at least the length of" + " the initial state") + self._states = [initial_state] + if boundary is not None: + self._bdry = tuple(boundary) + self._lbdry = ConstantFunction(boundary[0]) if boundary[0] in [0,1] else boundary[0] + self._rbdry = ConstantFunction(boundary[1]) if boundary[1] in [0,1] else boundary[1] + else: + self._bdry = boundary + + def __eq__(self, other): + """ + Check equality. + + Two ECAs are equal when they have the same rule, width, + initial state, and boundary conditions. + + TESTS:: + + sage: ECA1 = cellular_automata.Elementary(110, [1,0,0,1,1,0,0,1,0,1]) + sage: ECA2 = cellular_automata.Elementary(101, [1,0,0,1,1,0,0,1,0,1]) + sage: ECA3 = cellular_automata.Elementary(110, [1,0,0,1,1,0,0,1,1,0]) + sage: ECA4 = cellular_automata.Elementary(110, [0,1,0,0,1,1,0,0,1,0,1]) + sage: ECA5 = cellular_automata.Elementary(110, [1,0,0,1,1,0,0,1,0,1]) + sage: ECA6 = cellular_automata.Elementary(110, [1,0,0,1,1,0,0,1,0,1], boundary=(1, 1)) + sage: ECA1 == ECA5 + True + sage: ECA1 == ECA2 + False + sage: ECA1 == ECA3 + False + sage: ECA1 == ECA4 + False + sage: ECA1 == ECA6 + False + """ + return (isinstance(other, ElementaryCellularAutomata) + and self._rule == other._rule + and self._width == other._width + and self._states[0] == other._states[0] + and self._bdry == other._bdry) + + def __ne__(self, other): + """ + Check non equality. + + TESTS:: + + sage: ECA1 = cellular_automata.Elementary(110, [1,0,0,1,1,0,0,1,0,1]) + sage: ECA2 = cellular_automata.Elementary(101, [1,0,0,1,1,0,0,1,0,1]) + sage: ECA3 = cellular_automata.Elementary(110, [1,0,0,1,1,0,0,1,1,0]) + sage: ECA4 = cellular_automata.Elementary(110, [0,1,0,0,1,1,0,0,1,0,1]) + sage: ECA5 = cellular_automata.Elementary(110, [1,0,0,1,1,0,0,1,0,1]) + sage: ECA6 = cellular_automata.Elementary(110, [1,0,0,1,1,0,0,1,0,1], boundary=(1, 1)) + sage: ECA1 != ECA2 + True + sage: ECA1 != ECA3 + True + sage: ECA1 != ECA4 + True + sage: ECA1 != ECA5 + False + sage: ECA1 != ECA6 + True + """ + return not (self == other) + + # Evolution functions + # ------------------- + + def evolve(self, number=None): + r""" + Evolve ``self``. + + INPUT: + + - ``number`` -- (optional) the number of times to perform + the evolution + + EXAMPLES:: + + sage: ECA = cellular_automata.Elementary(110, [1,0,0,1,1,0,0,1,0,1]) + sage: ascii_art(ECA) + X XX X X + sage: ECA.evolve() + sage: ascii_art(ECA) + X XX X X + X XXX XXXX + sage: ECA.evolve(10) + sage: ascii_art(ECA) + X XX X X + X XXX XXXX + XXX XXX X + X XXX X XX + XXX XXXXXX + X XXX X + XXX X XX + X XXX XXX + XXX X XX X + X XXXXXXXX + XXX X + X X XX + """ + if number is not None: + for k in range(number): + self.evolve() + return + + prev_state = self._states[-1] + next_state = [None] * self._width + def to_int(triple): + return ZZ(list(reversed(triple)), base=2) + if self._bdry is None: + next_state[0] = self._rule[to_int([prev_state[-1]] + prev_state[:2])] + next_state[-1] = self._rule[to_int(prev_state[-2:] + [prev_state[0]])] + else: + n = len(self._states) + next_state[0] = self._rule[to_int([self._lbdry(n)] + prev_state[:2])] + next_state[-1] = self._rule[to_int(prev_state[-2:] + [self._rbdry(n)])] + + for i in range(1, self._width-1): + next_state[i] = self._rule[to_int(prev_state[i-1:i+2])] + self._states.append(next_state) + + # Output functions + # ---------------- + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: ECA = cellular_automata.Elementary(123, width=10, initial_state=[1]) + sage: ECA + Elementary cellular automata with rule 123 and initial state + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] + """ + return "Elementary cellular automata with rule {} and initial state {}".format( + ZZ(self._rule, base=2), self._states[0]) + + def print_state(self, number=None): + r""" + Print the state ``number``. + + INPUT: + + - ``number`` -- (default: the current state) the state to print + + EXAMPLES:: + + sage: ECA = cellular_automata.Elementary(110, width=10, + ....: initial_state=[1,0,0,1,1,0,1]) + sage: ECA.print_state(15) + X X XXXXX + sage: ECA.print_state(10) + X X XX + sage: ECA.print_state(20) + X XXX + sage: for i in range(11): + ....: ECA.print_state(i) + X XX X + XX XXXXX + XXXXX X + XX X XX + XX XX XXX + XX XXXXX X + XXXX XXX + X X XX X + X XX XXXXX + XXXXXX X + X X XX + """ + if number is None: + number = len(self._states) - 1 + if number + 1 > len(self._states): + for dummy in range(number + 1 - len(self._states)): + self.evolve() + + state = self._states[number] + print(''.join('X' if x else ' ' for x in state)) + + def print_states(self, number=None): + r""" + Print the first ``num`` states of ``self``. + + .. NOTE:: + + If the number of states computed for ``self`` is less than + ``num``, then this evolves the system using the default + time evolution. + + INPUT: + + - ``number`` -- the number of states to print + + EXAMPLES:: + + sage: ECA = cellular_automata.Elementary(110, width=10, + ....: initial_state=[1,0,0,1,1,0,1]) + sage: ECA.print_states(10) + X XX X + XX XXXXX + XXXXX X + XX X XX + XX XX XXX + XX XXXXX X + XXXX XXX + X X XX X + X XX XXXXX + XXXXXX X + """ + for i in range(number): + self.print_state(i) + + def _ascii_art_(self): + r""" + Return an ascii art representation of ``self``. + + EXAMPLES:: + + sage: ECA = cellular_automata.Elementary(22, width=30, initial_state=[1]) + sage: ECA.evolve(30) + sage: ascii_art(ECA) + X + XX + X + XXX + X X + XXX XX + X + XXX + X X + XXX XXX + X X + XXX XXX + X X X X + XXX XXX XXX XX + X + XXX + X X + XXX XXX + X X + XXX XXX + X X X X + XXX XXX XXX XXX + X X + XXX XXX + X X X X + XXX XXX XXX XXX + X X X X + XXX XXX XXX XXX + X X X X X X X X + XXX XXX XXX XXX XXX XXX XXX XX + """ + return AsciiArt([''.join('X' if x else ' ' for x in state) + for state in self._states]) + + def _unicode_art_(self): + r""" + Return a unicode art representation of ``self``. + + EXAMPLES:: + + sage: ECA = cellular_automata.Elementary(22, width=30, initial_state=[1]) + sage: ECA.evolve(30) + sage: unicode_art(ECA) + █ + ██ + █ + ███ + █ █ + ███ ██ + █ + ███ + █ █ + ███ ███ + █ █ + ███ ███ + █ █ █ █ + ███ ███ ███ ██ + █ + ███ + █ █ + ███ ███ + █ █ + ███ ███ + █ █ █ █ + ███ ███ ███ ███ + █ █ + ███ ███ + █ █ █ █ + ███ ███ ███ ███ + █ █ █ █ + ███ ███ ███ ███ + █ █ █ █ █ █ █ █ + ███ ███ ███ ███ ███ ███ ███ ██ + """ + return UnicodeArt([u''.join(u'█' if x else u' ' for x in state) + for state in self._states]) + + def plot(self, number=None): + r""" + Return a plot of ``self``. + + INPUT: + + - ``number`` -- the number of states to plot + + EXAMPLES:: + + sage: ECA = cellular_automata.Elementary(110, width=256) + sage: ECA.evolve(256) + sage: ECA.plot() + Graphics object consisting of 1 graphics primitive + """ + if number is None: + number = len(self._states) + if number > len(self._states): + for dummy in range(number - len(self._states)): + self.evolve() + M = matrix(self._states[:number]) + return matrix_plot(M) + diff --git a/src/sage/dynamics/cellular_automata/glca.py b/src/sage/dynamics/cellular_automata/glca.py new file mode 100644 index 00000000000..af47f66d360 --- /dev/null +++ b/src/sage/dynamics/cellular_automata/glca.py @@ -0,0 +1,478 @@ +# -*- encoding: utf-8 -*- +""" +Graftal Lace Cellular Automata + +AUTHORS: + +- Travis Scrimshaw (2020-04-30): Initial version +""" + +#***************************************************************************** +# Copyright (C) 2020 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.structure.sage_object import SageObject +from sage.typeset.ascii_art import AsciiArt +from sage.typeset.unicode_art import UnicodeArt + +class GraftalLaceCellularAutomata(SageObject): + r""" + Graftal Lace Cellular Automata (GLCA). + + A GLCA is a deterministic cellular automaton whose rule is given + by an 8-digit octal number `r_7 \cdots r_0`. For a node `s_i`, let + `b_k`, for `k = -1,0,1` denote if there is an edge from `s_i` to + `s'_{i+k}`, where `s'_j` is the previous row. We determine the value + at `t_{i+k}` by considering the value of `r_m`, where the binary + representation of `m` is `b_{-1} b_0 b_1`. If `r_m` has a binary + representation of b'_1 b'_0 b'_{-1}`, then we add `b'_k` to `t_{i+k}`. + + INPUT: + + - ``rule`` -- a list of length 8 with integer entries `0 \leq x < 8` + + EXAMPLES:: + + sage: G = cellular_automata.GraftalLace([0,2,5,4,7,2,3,3]) + sage: G.evolve(3) + sage: ascii_art(G) + o + | + o + | + o o o + /| |/| + o o o o o + /| |/|\|/| + o o o o o o o + + sage: G = cellular_automata.GraftalLace([3,0,3,4,7,6,3,1]) + sage: G.evolve(3) + sage: ascii_art(G) + o + | + o + |\ + o o o + / |\ \ + o o o o o + /|/ |\ \ \ + o o o o o o o + + sage: G = cellular_automata.GraftalLace([2,0,3,3,6,0,2,7]) + sage: G.evolve(20) + sage: G.plot() + Graphics object consisting of 842 graphics primitives + + .. PLOT:: + + G = cellular_automata.GraftalLace([2,0,3,3,6,0,2,7]) + G.evolve(20) + P = G.plot() + sphinx_plot(P) + + REFERENCES: + + - [Kas2018]_ + """ + def __init__(self, rule): + """ + Initialize ``self``. + + TESTS:: + + sage: G = cellular_automata.GraftalLace([5,1,2,5,4,5,5,0]) + sage: TestSuite(G).run() + + sage: G = cellular_automata.GraftalLace([5,1,2,5,4,5,5]) + Traceback (most recent call last): + ... + ValueError: invalid rule + sage: G = cellular_automata.GraftalLace([5,1,2,5,4,5,5,0,5]) + Traceback (most recent call last): + ... + ValueError: invalid rule + sage: G = cellular_automata.GraftalLace([5,1,2,5,-1,5,5,0]) + Traceback (most recent call last): + ... + ValueError: invalid rule + sage: G = cellular_automata.GraftalLace([8,5,1,2,5,4,5,5]) + Traceback (most recent call last): + ... + ValueError: invalid rule + + """ + if len(rule) != 8 or any(x not in range(8) for x in rule): + raise ValueError("invalid rule") + # We reverse the rule to make the evolution easier to compute + self._rule = tuple(reversed(rule)) + self._states = [[2]] + + def __eq__(self, other): + """ + Check equality. + + Two GLCAs are equal if and only if they have the same rule. + + TESTS:: + + sage: G1 = cellular_automata.GraftalLace([5,1,2,5,4,5,5,0]) + sage: G2 = cellular_automata.GraftalLace([0,5,5,4,5,2,1,5]) + sage: G3 = cellular_automata.GraftalLace([5,1,2,5,4,5,5,0]) + sage: G1 == G2 + False + sage: G1 == G3 + True + sage: G1 is G3 + False + """ + return (isinstance(other, GraftalLaceCellularAutomata) + and self._rule == other._rule) + + def __ne__(self, other): + """ + Check non equality. + + TESTS:: + + sage: G1 = cellular_automata.GraftalLace([5,1,2,5,4,5,5,0]) + sage: G2 = cellular_automata.GraftalLace([0,5,5,4,5,2,1,5]) + sage: G3 = cellular_automata.GraftalLace([5,1,2,5,4,5,5,0]) + sage: G1 != G2 + True + sage: G1 != G3 + False + sage: G1 is G3 + False + """ + return not (self == other) + + # Evolution functions + # ------------------- + + def evolve(self, number=None): + r""" + Evolve ``self``. + + INPUT: + + - ``number`` -- (default: 1) the number of times to perform + the evolution + + EXAMPLES:: + + sage: G = cellular_automata.GraftalLace([5,1,2,5,4,5,5,0]) + sage: ascii_art(G) + o + | + o + sage: G.evolve(2) + sage: ascii_art(G) + o + | + o + / \ + o o o + / \ / \ + o o o o o + + sage: G = cellular_automata.GraftalLace([0,2,1,4,7,2,3,0]) + sage: G.evolve(3) + sage: ascii_art(G) + o + | + o + | + o o o + | + o o o o o + | + o o o o o o o + """ + if number is not None: + for k in range(number): + self.evolve() + return + + prev_state = self._states[-1] + next_state = [0] * (len(prev_state) + 2) + + for i,val in enumerate(prev_state): + next_state[i] += self._rule[val] & 0x1 + next_state[i+1] += self._rule[val] & 0x2 + next_state[i+2] += self._rule[val] & 0x4 + self._states.append(next_state) + + # Output functions + # ---------------- + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: cellular_automata.GraftalLace([5,1,2,5,4,5,5,0]) + Graftal Lace Cellular Automata with rule 51254550 + """ + rule = ''.join(map(str, reversed(self._rule))) + return "Graftal Lace Cellular Automata with rule {}".format(rule) + + def print_states(self, number=None, use_unicode=False): + r""" + Print the first ``num`` states of ``self``. + + .. NOTE:: + + If the number of states computed for ``self`` is less than + ``num``, then this evolves the system using the default + time evolution. + + INPUT: + + - ``number`` -- the number of states to print + + EXAMPLES:: + + sage: G = cellular_automata.GraftalLace([5,1,2,5,4,5,5,0]) + sage: G.evolve(2) + sage: G.print_states() + o + | + o + / \ + o o o + / \ / \ + o o o o o + sage: G.evolve(20) + sage: G.print_states(3) + o + | + o + / \ + o o o + / \ / \ + o o o o o + """ + if number is None: + number = len(self._states) + if number > len(self._states): + for dummy in range(number - len(self._states)): + self.evolve() + if use_unicode: + print(self._unicode_art_(number)) + else: + print(self._ascii_art_(number)) + + def _ascii_art_(self, number=None): + r""" + Return an ascii art representation of ``self``. + + EXAMPLES:: + + sage: G = cellular_automata.GraftalLace([5,1,2,5,4,5,5,0]) + sage: G.evolve(10) + sage: ascii_art(G) + o + | + o + / \ + o o o + / \ / \ + o o o o o + / \ | / \ + o o o o o o o + / \ / X X \ / \ + o o o o o o o o o + / \ |/ \|/ \| / \ + o o o o o o o o o o o + / \ / \ \ / \ / / \ / \ + o o o o o o o o o o o o o + / \ | / \| |/ \ | / \ + o o o o o o o o o o o o o o o + / \ / X X \ / \ / X X \ / \ + o o o o o o o o o o o o o o o o o + / \ |/ \|/ \| |/ \|/ \| / \ + o o o o o o o o o o o o o o o o o o o + / \ / \ \ / \ / \ / \ / / \ / \ + o o o o o o o o o o o o o o o o o o o o o + """ + if number is None: + number = len(self._states) + + space = len(self._states[:number]) * 2 - 1 + ret = AsciiArt([' '*space + 'o']) + space += 1 + for i,state in enumerate(self._states[:number]): + temp = ' '*(space-2) + last = ' ' + for x in state: + if x & 0x4: + if last == '/': + temp += 'X' + else: + temp += '\\' + else: + temp += last + temp += '|' if x & 0x2 else ' ' + last = '/' if x & 0x1 else ' ' + ret *= AsciiArt([temp + last]) + space -= 1 + ret *= AsciiArt([' '*space + ' '.join('o' for dummy in range(2*i+1))]) + space -= 1 + return ret + + def _unicode_art_(self, number=None): + r""" + Return a unicode art representation of ``self``. + + EXAMPLES:: + + sage: G = cellular_automata.GraftalLace([5,1,2,5,4,5,5,0]) + sage: G.evolve(10) + sage: unicode_art(G) + ◾ + │ + ◾ + ╱ ╲ + ◾ ◾ ◾ + ╱ ╲ ╱ ╲ + ◾ ◾ ◾ ◾ ◾ + ╱ ╲ │ ╱ ╲ + ◾ ◾ ◾ ◾ ◾ ◾ ◾ + ╱ ╲ ╱ ╳ ╳ ╲ ╱ ╲ + ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ + ╱ ╲ │╱ ╲│╱ ╲│ ╱ ╲ + ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ + ╱ ╲ ╱ ╲ ╲ ╱ ╲ ╱ ╱ ╲ ╱ ╲ + ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ + ╱ ╲ │ ╱ ╲│ │╱ ╲ │ ╱ ╲ + ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ + ╱ ╲ ╱ ╳ ╳ ╲ ╱ ╲ ╱ ╳ ╳ ╲ ╱ ╲ + ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ + ╱ ╲ │╱ ╲│╱ ╲│ │╱ ╲│╱ ╲│ ╱ ╲ + ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ + ╱ ╲ ╱ ╲ ╲ ╱ ╲ ╱ ╲ ╱ ╲ ╱ ╱ ╲ ╱ ╲ + ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ ◾ + """ + if number is None: + number = len(self._states) + + space = len(self._states[:number]) * 2 - 1 + ret = UnicodeArt([u' '*space + u'◾']) + space += 1 + for i,state in enumerate(self._states[:number]): + temp = u' '*(space-2) + last = u' ' + for x in state: + if x & 0x4: + if last == u'╱': + temp += u'╳' + else: + temp += u'╲' + else: + temp += last + temp += u'│' if x & 0x2 else ' ' + last = u'╱' if x & 0x1 else ' ' + ret *= UnicodeArt([temp + last]) + space -= 1 + ret *= UnicodeArt([u' '*space + u' '.join(u'◾' for dummy in range(2*i+1))]) + space -= 1 + return ret + + def plot(self, number=None): + r""" + Return a plot of ``self``. + + INPUT: + + - ``number`` -- the number of states to plot + + EXAMPLES:: + + sage: G = cellular_automata.GraftalLace([5,1,2,5,4,5,5,0]) + sage: G.evolve(20) + sage: G.plot() + Graphics object consisting of 865 graphics primitives + """ + if number is None: + number = len(self._states) + if number > len(self._states): + for dummy in range(number - len(self._states)): + self.evolve() + + from sage.plot.circle import circle + from sage.plot.line import line + + x = len(self._states[:number]) + rad = 0.1 + ret = circle((x, 1), rad, fill=True) + for i,state in enumerate(self._states[:number]): + for j,val in enumerate(state): + if val & 0x4: + ret += line([(x+j,-i), (x+j-1,-i+1)]) + if val & 0x2: + ret += line([(x+j,-i), (x+j,-i+1)]) + if val & 0x1: + ret += line([(x+j,-i), (x+j+1,-i+1)]) + for j in range(2*i+1): + ret += circle((x+j, -i), rad, fill=True) + x -= 1 + ret.set_aspect_ratio(1) + ret.axes(False) + return ret + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: G = cellular_automata.GraftalLace([5,1,2,5,4,5,5,0]) + sage: G.evolve(2) + sage: latex(G) + \begin{tikzpicture} + \fill (3,1) circle (2pt); + \draw[-] (3,0) -- (3,1); + \fill (3,0) circle (2pt); + \draw[-] (2,-1) -- (3,0); + \draw[-] (4,-1) -- (3,0); + \fill (2,-1) circle (2pt); + \fill (3,-1) circle (2pt); + \fill (4,-1) circle (2pt); + \draw[-] (1,-2) -- (2,-1); + \draw[-] (3,-2) -- (2,-1); + \draw[-] (3,-2) -- (4,-1); + \draw[-] (5,-2) -- (4,-1); + \fill (1,-2) circle (2pt); + \fill (2,-2) circle (2pt); + \fill (3,-2) circle (2pt); + \fill (4,-2) circle (2pt); + \fill (5,-2) circle (2pt); + \end{tikzpicture} + """ + ret = "\\begin{tikzpicture}\n" + + x = len(self._states) + rad = 2 + ret += "\\fill ({},{}) circle ({}pt);\n".format(x, 1, rad) + for i,state in enumerate(self._states): + for j,val in enumerate(state): + if val & 0x4: + ret += "\\draw[-] ({},{}) -- ({},{});\n".format(x+j,-i, x+j-1,-i+1) + if val & 0x2: + ret += "\\draw[-] ({},{}) -- ({},{});\n".format(x+j,-i, x+j,-i+1) + if val & 0x1: + ret += "\\draw[-] ({},{}) -- ({},{});\n".format(x+j,-i, x+j+1,-i+1) + for j in range(2*i+1): + ret += "\\fill ({},{}) circle ({}pt);\n".format(x+j, -i, rad) + x -= 1 + ret += "\\end{tikzpicture}" + return ret + diff --git a/src/sage/dynamics/finite_dynamical_system.py b/src/sage/dynamics/finite_dynamical_system.py index 5febefc74dd..b7710f0e8ae 100644 --- a/src/sage/dynamics/finite_dynamical_system.py +++ b/src/sage/dynamics/finite_dynamical_system.py @@ -87,14 +87,12 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** -from six import add_metaclass from sage.categories.sets_cat import Sets from sage.structure.sage_object import SageObject from sage.misc.classcall_metaclass import ClasscallMetaclass, typecall -@add_metaclass(ClasscallMetaclass) -class DiscreteDynamicalSystem(SageObject): +class DiscreteDynamicalSystem(SageObject, metaclass=ClasscallMetaclass): r""" A discrete dynamical system. diff --git a/src/sage/env.py b/src/sage/env.py index 738e95fa113..1ddfc7cfb95 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -5,14 +5,16 @@ - \R. Andrew Ohana (2012): Initial version. -Verify that Sage can be started without any ``SAGE_`` environment -variables:: +Verify that importing ``sage.all`` works in Sage's Python without any ``SAGE_`` +environment variables, and has the same ``SAGE_ROOT`` and ``SAGE_LOCAL`` +(see also :trac:`29446`):: sage: env = {k:v for (k,v) in os.environ.items() if not k.startswith("SAGE_")} - sage: import subprocess - sage: cmd = "from sage.all import SAGE_ROOT; print(SAGE_ROOT)" - sage: res = subprocess.call([sys.executable, "-c", cmd], env=env) # long time - None + sage: from subprocess import check_output + sage: cmd = "from sage.all import SAGE_ROOT, SAGE_LOCAL; print((SAGE_ROOT, SAGE_LOCAL))" + sage: out = check_output([sys.executable, "-c", cmd], env=env).decode().strip() # long time + sage: out == repr((SAGE_ROOT, SAGE_LOCAL)) # long time + True """ # **************************************************************************** @@ -164,6 +166,7 @@ def var(key, *fallbacks, **kwds): var('SAGE_DOC', join(SAGE_SHARE, 'doc', 'sage')) var('SAGE_SPKG_INST', join(SAGE_LOCAL, 'var', 'lib', 'sage', 'installed')) var('SAGE_LIB', os.path.dirname(os.path.dirname(sage.__file__))) +var('SAGE_EXTCODE', join(SAGE_LIB, 'sage', 'ext_data')) var('SAGE_ROOT') # no fallback for SAGE_ROOT var('SAGE_SRC', join(SAGE_ROOT, 'src'), SAGE_LIB) @@ -175,7 +178,6 @@ def var(key, *fallbacks, **kwds): var('SAGE_STARTUP_FILE', join(DOT_SAGE, 'init.sage')) # installation directories for various packages -var('SAGE_EXTCODE', join(SAGE_SHARE, 'sage', 'ext')) var('CONWAY_POLYNOMIALS_DATA_DIR', join(SAGE_SHARE, 'conway_polynomials')) var('GRAPHS_DATA_DIR', join(SAGE_SHARE, 'graphs')) var('ELLCURVE_DATA_DIR', join(SAGE_SHARE, 'ellcurves')) @@ -194,7 +196,8 @@ def var(key, *fallbacks, **kwds): var('PPLPY_DOCS', join(SAGE_SHARE, 'doc', 'pplpy')) var('MAXIMA', 'maxima') var('MAXIMA_FAS') -var('SAGE_NAUTY_BINS_PREFIX', '') +var('SAGE_NAUTY_BINS_PREFIX', '') +var('ARB_LIBRARY', 'arb') # misc var('SAGE_BANNER', '') @@ -203,9 +206,9 @@ def var(key, *fallbacks, **kwds): def _get_shared_lib_filename(libname, *additional_libnames): """ - Return the full path to a shared library file installed in the standard - location for the system within the ``LIBDIR`` prefix (or - ``$SAGE_LOCAL/lib`` in the case of manual build of Sage). + Return the full path to a shared library file installed in + ``$SAGE_LOCAL/lib`` or the directories associated with the + Python sysconfig. This can also be passed more than one library name (e.g. for cases where some library may have multiple names depending on the platform) in which @@ -242,11 +245,17 @@ def _get_shared_lib_filename(libname, *additional_libnames): for libname in (libname,) + additional_libnames: if sys.platform == 'cygwin': - bindir = sysconfig.get_config_var('BINDIR') + # Later down we take the last matching DLL found, so search + # SAGE_LOCAL second so that it takes precedence + bindirs = [ + sysconfig.get_config_var('BINDIR'), + os.path.join(SAGE_LOCAL, 'bin') + ] pats = ['cyg{}.dll'.format(libname), 'cyg{}-*.dll'.format(libname)] filenames = [] - for pat in pats: - filenames += glob.glob(os.path.join(bindir, pat)) + for bindir in bindirs: + for pat in pats: + filenames += glob.glob(os.path.join(bindir, pat)) # Note: This is not very robust, since if there are multi DLL # versions for the same library this just selects one more or less @@ -260,10 +269,13 @@ def _get_shared_lib_filename(libname, *additional_libnames): else: ext = 'so' - libdirs = [sysconfig.get_config_var('LIBDIR')] + libdirs = [ + os.path.join(SAGE_LOCAL, 'lib'), + sysconfig.get_config_var('LIBDIR') + ] multilib = sysconfig.get_config_var('MULTILIB') if multilib: - libdirs.insert(0, os.path.join(libdirs[0], multilib)) + libdirs.insert(1, os.path.join(libdirs[0], multilib)) for libdir in libdirs: basename = 'lib{}.{}'.format(libname, ext) @@ -331,8 +343,7 @@ def sage_include_directories(use_sources=False): sage: import sage.env sage: sage.env.sage_include_directories() - ['.../include', - '.../python.../site-packages/sage/ext', + ['.../python.../site-packages/sage/ext', '.../include/python...', '.../python.../numpy/core/include'] @@ -353,8 +364,7 @@ def sage_include_directories(use_sources=False): TOP = SAGE_SRC if use_sources else SAGE_LIB - return [SAGE_INC, - TOP, + return [TOP, os.path.join(TOP, 'sage', 'ext'), distutils.sysconfig.get_python_inc(), numpy.get_include()] @@ -372,41 +382,51 @@ def cython_aliases(): {...} sage: sorted(cython_aliases().keys()) ['ARB_LIBRARY', - 'FFLASFFPACK_CFLAGS', - 'FFLASFFPACK_INCDIR', - 'FFLASFFPACK_LIBDIR', - 'FFLASFFPACK_LIBRARIES', - 'GIVARO_CFLAGS', - 'GIVARO_INCDIR', - 'GIVARO_LIBDIR', - 'GIVARO_LIBRARIES', - 'GSL_CFLAGS', - 'GSL_INCDIR', - 'GSL_LIBDIR', - 'GSL_LIBRARIES', - 'LINBOX_CFLAGS', - 'LINBOX_INCDIR', - 'LINBOX_LIBDIR', - 'LINBOX_LIBRARIES', - 'SINGULAR_CFLAGS', - 'SINGULAR_INCDIR', - 'SINGULAR_LIBDIR', - 'SINGULAR_LIBRARIES'] + 'CBLAS_CFLAGS', + ..., + 'ZLIB_LIBRARIES'] """ import pkgconfig aliases = {} - for lib in ['fflas-ffpack', 'givaro', 'gsl', 'linbox', 'Singular']: + for lib in ['fflas-ffpack', 'givaro', 'gsl', 'linbox', 'Singular', + 'libpng', 'gdlib', 'm4ri', 'zlib', 'cblas', 'lapack']: var = lib.upper().replace("-", "") + "_" - aliases[var + "CFLAGS"] = pkgconfig.cflags(lib).split() - pc = pkgconfig.parse(lib) - # INCDIR should be redundant because the -I options are also - # passed in CFLAGS + if lib == 'zlib': + aliases[var + "CFLAGS"] = "" + try: + pc = pkgconfig.parse('zlib') + libs = pkgconfig.libs(lib) + except pkgconfig.PackageNotFoundError: + from collections import defaultdict + pc = defaultdict(list, {'libraries': ['z']}) + libs = "-lz" + else: + aliases[var + "CFLAGS"] = pkgconfig.cflags(lib).split() + pc = pkgconfig.parse(lib) + libs = pkgconfig.libs(lib) + # It may seem that INCDIR is redundant because the -I options are also + # passed in CFLAGS. However, "extra_compile_args" are put at the end + # of the compiler command line. "include_dirs" go to the front; the + # include search order matters. aliases[var + "INCDIR"] = pc['include_dirs'] aliases[var + "LIBDIR"] = pc['library_dirs'] + aliases[var + "LIBEXTRA"] = list(filter(lambda s: not s.startswith(('-l','-L')), libs.split())) aliases[var + "LIBRARIES"] = pc['libraries'] + # uname-specific flags + UNAME = os.uname() + + def uname_specific(name, value, alternative): + if name in UNAME[0]: + return value + else: + return alternative + + aliases["LINUX_NOEXECSTACK"] = uname_specific("Linux", ["-Wl,-z,noexecstack"], + []) + # LinBox needs special care because it actually requires C++11 with # GNU extensions: -std=c++11 does not work, you need -std=gnu++11 # (this is true at least with GCC 7.2.0). @@ -416,5 +436,15 @@ def cython_aliases(): # This is not a problem in practice since LinBox depends on # fflas-ffpack and fflas-ffpack does add such a C++11 flag. aliases["LINBOX_CFLAGS"].append("-std=gnu++11") - aliases["ARB_LIBRARY"] = os.environ.get('SAGE_ARB_LIBRARY', 'arb') + aliases["ARB_LIBRARY"] = ARB_LIBRARY + + # TODO: Remove Cygwin hack by installing a suitable cblas.pc + if os.path.exists('/usr/lib/libblas.dll.a'): + aliases["CBLAS_LIBS"] = ['gslcblas'] + + try: + aliases["M4RI_CFLAGS"].remove("-pedantic") + except ValueError: + pass + return aliases diff --git a/src/sage/ext/cplusplus.pxd b/src/sage/ext/cplusplus.pxd index 66c562c2cac..e504f0c9fbb 100644 --- a/src/sage/ext/cplusplus.pxd +++ b/src/sage/ext/cplusplus.pxd @@ -10,7 +10,7 @@ cdef extern from "sage/ext/ccobject.h": # Print representation of any C++ object - str ccrepr[T](T x) + str ccrepr[T](const T& x) # Read a Python bytes/str into a C++ object int ccreadstr[T](T x, object b) except -1 diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index 94e2928525b..580f115692b 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -311,7 +311,6 @@ from sage.structure.element cimport parent def fast_callable(x, domain=None, vars=None, - _autocompute_vars_for_backward_compatibility_with_deprecated_fast_float_functionality=False, expect_one_var=False): r""" Given an expression x, compile it into a form that can be quickly @@ -423,39 +422,6 @@ def fast_callable(x, domain=None, vars=None, Traceback (most recent call last): ... TypeError: unable to simplify to float approximation - - Check :trac:`24805`--if a fast_callable expression involves division - on a Python object, it will always prefer Python 3 semantics (e.g. - ``x / y`` will try ``x.__truediv__`` instead of ``x.__div__``, as if - ``from __future__ import division`` is in effect). However, for - classes that implement ``__div__`` but not ``__truediv__`` it will still - fall back on ``__div__`` for backwards-compatibility, but reliance on - this functionality is deprecated:: - - sage: from sage.ext.fast_callable import ExpressionTreeBuilder - sage: etb = ExpressionTreeBuilder('x') - sage: x = etb.var('x') - sage: class One(object): - ....: def __div__(self, other): - ....: if not isinstance(other, Integer): - ....: return NotImplemented - ....: return 1 / other - sage: expr = One() / x - sage: f = fast_callable(expr, vars=[x]) - sage: f(2) # py2 - doctest:warning...: - DeprecationWarning: use of __truediv__ should be preferred over __div__ - See https://trac.sagemath.org/24805 for details. - 1/2 - sage: class ModernOne(One): - ....: def __truediv__(self, other): - ....: if not isinstance(other, Integer): - ....: return NotImplemented - ....: return 1 / other - sage: expr = ModernOne() / x - sage: f = fast_callable(expr, vars=[x]) - sage: f(2) - 1/2 """ cdef Expression et if isinstance(x, Expression): @@ -481,11 +447,7 @@ def fast_callable(x, domain=None, vars=None, if len(vars) == 0: vars = ['EXTRA_VAR0'] else: - if _autocompute_vars_for_backward_compatibility_with_deprecated_fast_float_functionality: - from sage.misc.superseded import deprecation - deprecation(5413, "Substitution using function-call syntax and unnamed arguments is deprecated and will be removed from a future release of Sage; you can use named arguments instead, like EXPR(x=..., y=...)") - else: - raise ValueError("List of variables must be specified for symbolic expressions") + raise ValueError("List of variables must be specified for symbolic expressions") from sage.rings.polynomial.polynomial_ring import is_PolynomialRing from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing if is_PolynomialRing(x.parent()) or is_MPolynomialRing(x.parent()): @@ -965,28 +927,6 @@ cdef class Expression: """ return _expression_binop_helper(s, o, op_truediv) - def __div__(s, o): - r""" - Compute a quotient of two Expressions. - - EXAMPLES:: - - sage: from sage.ext.fast_callable import ExpressionTreeBuilder - sage: etb = ExpressionTreeBuilder(vars=(x,)) - sage: x = etb(x) - sage: x/x - div(v_0, v_0) - sage: x/1 - div(v_0, 1) - sage: 1/x - div(1, v_0) - sage: x.__div__(1) # py2 - div(v_0, 1) - sage: x.__rdiv__(1) # py2 - div(1, v_0) - """ - return _expression_binop_helper(s, o, op_div) - def __floordiv__(s, o): r""" Compute the floordiv (the floor of the quotient) of two Expressions. diff --git a/src/sage/ext/fast_eval.pyx b/src/sage/ext/fast_eval.pyx index 0743f3052ca..d4e736b83ec 100644 --- a/src/sage/ext/fast_eval.pyx +++ b/src/sage/ext/fast_eval.pyx @@ -797,17 +797,6 @@ cdef class FastDoubleFunc: """ return binop(left, right, DIV) - def __div__(left, right): - """ - EXAMPLES:: - - sage: from sage.ext.fast_eval import fast_float_arg - sage: f = fast_float_arg(0) / 7 - sage: f(14) - 2.0 - """ - return binop(left, right, DIV) - def __pow__(FastDoubleFunc left, right, dummy): """ EXAMPLES:: @@ -1399,7 +1388,8 @@ def fast_float(f, *vars, old=None, expect_one_var=False): if old: return f._fast_float_(*vars) else: - return fast_callable(f, vars=vars, domain=float, _autocompute_vars_for_backward_compatibility_with_deprecated_fast_float_functionality=True, expect_one_var=expect_one_var) + return fast_callable(f, vars=vars, domain=float, + expect_one_var=expect_one_var) except AttributeError: pass diff --git a/src/sage/ext/solaris_fixes.h b/src/sage/ext/solaris_fixes.h deleted file mode 100644 index 547cbd634b5..00000000000 --- a/src/sage/ext/solaris_fixes.h +++ /dev/null @@ -1,3 +0,0 @@ -#if defined(__sun) -#define _Complex_I 1.0fi -#endif diff --git a/src/ext/doctest/invalid/syntax_error.tachyon b/src/sage/ext_data/doctest/invalid/syntax_error.tachyon similarity index 100% rename from src/ext/doctest/invalid/syntax_error.tachyon rename to src/sage/ext_data/doctest/invalid/syntax_error.tachyon diff --git a/src/ext/doctest/rich_output/example.avi b/src/sage/ext_data/doctest/rich_output/example.avi similarity index 100% rename from src/ext/doctest/rich_output/example.avi rename to src/sage/ext_data/doctest/rich_output/example.avi diff --git a/src/ext/doctest/rich_output/example.canvas3d b/src/sage/ext_data/doctest/rich_output/example.canvas3d similarity index 100% rename from src/ext/doctest/rich_output/example.canvas3d rename to src/sage/ext_data/doctest/rich_output/example.canvas3d diff --git a/src/ext/doctest/rich_output/example.dvi b/src/sage/ext_data/doctest/rich_output/example.dvi similarity index 100% rename from src/ext/doctest/rich_output/example.dvi rename to src/sage/ext_data/doctest/rich_output/example.dvi diff --git a/src/ext/doctest/rich_output/example.flv b/src/sage/ext_data/doctest/rich_output/example.flv similarity index 100% rename from src/ext/doctest/rich_output/example.flv rename to src/sage/ext_data/doctest/rich_output/example.flv diff --git a/src/ext/doctest/rich_output/example.gif b/src/sage/ext_data/doctest/rich_output/example.gif similarity index 100% rename from src/ext/doctest/rich_output/example.gif rename to src/sage/ext_data/doctest/rich_output/example.gif diff --git a/src/ext/doctest/rich_output/example.jpg b/src/sage/ext_data/doctest/rich_output/example.jpg similarity index 100% rename from src/ext/doctest/rich_output/example.jpg rename to src/sage/ext_data/doctest/rich_output/example.jpg diff --git a/src/ext/doctest/rich_output/example.mkv b/src/sage/ext_data/doctest/rich_output/example.mkv similarity index 100% rename from src/ext/doctest/rich_output/example.mkv rename to src/sage/ext_data/doctest/rich_output/example.mkv diff --git a/src/ext/doctest/rich_output/example.mov b/src/sage/ext_data/doctest/rich_output/example.mov similarity index 100% rename from src/ext/doctest/rich_output/example.mov rename to src/sage/ext_data/doctest/rich_output/example.mov diff --git a/src/ext/doctest/rich_output/example.mp4 b/src/sage/ext_data/doctest/rich_output/example.mp4 similarity index 100% rename from src/ext/doctest/rich_output/example.mp4 rename to src/sage/ext_data/doctest/rich_output/example.mp4 diff --git a/src/ext/doctest/rich_output/example.ogv b/src/sage/ext_data/doctest/rich_output/example.ogv similarity index 100% rename from src/ext/doctest/rich_output/example.ogv rename to src/sage/ext_data/doctest/rich_output/example.ogv diff --git a/src/ext/doctest/rich_output/example.pdf b/src/sage/ext_data/doctest/rich_output/example.pdf similarity index 100% rename from src/ext/doctest/rich_output/example.pdf rename to src/sage/ext_data/doctest/rich_output/example.pdf diff --git a/src/ext/doctest/rich_output/example.png b/src/sage/ext_data/doctest/rich_output/example.png similarity index 100% rename from src/ext/doctest/rich_output/example.png rename to src/sage/ext_data/doctest/rich_output/example.png diff --git a/src/ext/doctest/rich_output/example.svg b/src/sage/ext_data/doctest/rich_output/example.svg similarity index 100% rename from src/ext/doctest/rich_output/example.svg rename to src/sage/ext_data/doctest/rich_output/example.svg diff --git a/src/ext/doctest/rich_output/example.webm b/src/sage/ext_data/doctest/rich_output/example.webm similarity index 100% rename from src/ext/doctest/rich_output/example.webm rename to src/sage/ext_data/doctest/rich_output/example.webm diff --git a/src/ext/doctest/rich_output/example.wmv b/src/sage/ext_data/doctest/rich_output/example.wmv similarity index 100% rename from src/ext/doctest/rich_output/example.wmv rename to src/sage/ext_data/doctest/rich_output/example.wmv diff --git a/src/ext/doctest/rich_output/example_jmol.spt.zip b/src/sage/ext_data/doctest/rich_output/example_jmol.spt.zip similarity index 100% rename from src/ext/doctest/rich_output/example_jmol.spt.zip rename to src/sage/ext_data/doctest/rich_output/example_jmol.spt.zip diff --git a/src/ext/doctest/rich_output/example_wavefront/scene.mtl b/src/sage/ext_data/doctest/rich_output/example_wavefront/scene.mtl similarity index 100% rename from src/ext/doctest/rich_output/example_wavefront/scene.mtl rename to src/sage/ext_data/doctest/rich_output/example_wavefront/scene.mtl diff --git a/src/ext/doctest/rich_output/example_wavefront/scene.obj b/src/sage/ext_data/doctest/rich_output/example_wavefront/scene.obj similarity index 100% rename from src/ext/doctest/rich_output/example_wavefront/scene.obj rename to src/sage/ext_data/doctest/rich_output/example_wavefront/scene.obj diff --git a/src/ext/gap/console.g b/src/sage/ext_data/gap/console.g similarity index 100% rename from src/ext/gap/console.g rename to src/sage/ext_data/gap/console.g diff --git a/src/ext/gap/joyner/hurwitz_crv_rr_sp.gap b/src/sage/ext_data/gap/joyner/hurwitz_crv_rr_sp.gap similarity index 100% rename from src/ext/gap/joyner/hurwitz_crv_rr_sp.gap rename to src/sage/ext_data/gap/joyner/hurwitz_crv_rr_sp.gap diff --git a/src/ext/gap/joyner/modular_crv_rr_sp.gap b/src/sage/ext_data/gap/joyner/modular_crv_rr_sp.gap similarity index 100% rename from src/ext/gap/joyner/modular_crv_rr_sp.gap rename to src/sage/ext_data/gap/joyner/modular_crv_rr_sp.gap diff --git a/src/ext/gap/sage.g b/src/sage/ext_data/gap/sage.g similarity index 100% rename from src/ext/gap/sage.g rename to src/sage/ext_data/gap/sage.g diff --git a/src/ext/graphs/graph_plot_js.html b/src/sage/ext_data/graphs/graph_plot_js.html similarity index 78% rename from src/ext/graphs/graph_plot_js.html rename to src/sage/ext_data/graphs/graph_plot_js.html index ad2c7e165ac..75d08f91662 100644 --- a/src/ext/graphs/graph_plot_js.html +++ b/src/sage/ext_data/graphs/graph_plot_js.html @@ -23,6 +23,19 @@ marker { fill:#bbb; } + #menu-container { position: absolute; bottom: 30px; right: 40px; cursor: default; } + + #menu-message { position: absolute; bottom: 0px; right: 0px; white-space: nowrap; + display: none; background-color: #F5F5F5; padding: 10px; } + + #menu-content { position: absolute; bottom: 0px; right: 0px; + display: none; background-color: #F5F5F5; border-bottom: 1px solid black; + border-right: 1px solid black; border-left: 1px solid black; } + + #menu-content div { border-top: 1px solid black; padding: 10px; white-space: nowrap; } + + #menu-content div:hover { background-color: #FEFEFE;; } + // D3JS_SCRIPT_HEREEEEEEEEEEE @@ -266,6 +279,39 @@ } } + // menu functions + + function toggleMenu() { + + var m = document.getElementById( 'menu-content' ); + if ( m.style.display === 'block' ) m.style.display = 'none' + else m.style.display = 'block'; + + } + + function saveAsSVG() { + + var doctype = '' + + ''; + + var our_style=''; + + // serialize our SVG XML to a string. + var source = (new XMLSerializer()).serializeToString(d3.select('svg').node()); + + var styled_source = source.replace('"all">', '"all">' + our_style + ""); + // create a file blob of our SVG. + var blob = new Blob([doctype + styled_source], { type: 'image/svg+xml;charset=utf-8' }); + var a = document.body.appendChild( document.createElement( 'a' ) ); + a.href = window.URL.createObjectURL( blob ); + a.download = 'my_graph.svg'; + a.click() + } + @@ -274,5 +320,11 @@ // GRAPH_DATA_HEREEEEEEEEEEE + diff --git a/src/ext/kenzo/CP2.txt b/src/sage/ext_data/kenzo/CP2.txt similarity index 100% rename from src/ext/kenzo/CP2.txt rename to src/sage/ext_data/kenzo/CP2.txt diff --git a/src/ext/kenzo/CP3.txt b/src/sage/ext_data/kenzo/CP3.txt similarity index 100% rename from src/ext/kenzo/CP3.txt rename to src/sage/ext_data/kenzo/CP3.txt diff --git a/src/ext/kenzo/CP4.txt b/src/sage/ext_data/kenzo/CP4.txt similarity index 100% rename from src/ext/kenzo/CP4.txt rename to src/sage/ext_data/kenzo/CP4.txt diff --git a/src/ext/kenzo/README.txt b/src/sage/ext_data/kenzo/README.txt similarity index 100% rename from src/ext/kenzo/README.txt rename to src/sage/ext_data/kenzo/README.txt diff --git a/src/ext/kenzo/S4.txt b/src/sage/ext_data/kenzo/S4.txt similarity index 100% rename from src/ext/kenzo/S4.txt rename to src/sage/ext_data/kenzo/S4.txt diff --git a/src/ext/magma/latex/latex.m b/src/sage/ext_data/magma/latex/latex.m similarity index 100% rename from src/ext/magma/latex/latex.m rename to src/sage/ext_data/magma/latex/latex.m diff --git a/src/ext/magma/latex/latex.spec b/src/sage/ext_data/magma/latex/latex.spec similarity index 100% rename from src/ext/magma/latex/latex.spec rename to src/sage/ext_data/magma/latex/latex.spec diff --git a/src/ext/magma/sage/basic.m b/src/sage/ext_data/magma/sage/basic.m similarity index 100% rename from src/ext/magma/sage/basic.m rename to src/sage/ext_data/magma/sage/basic.m diff --git a/src/ext/magma/sage/sage.spec b/src/sage/ext_data/magma/sage/sage.spec similarity index 100% rename from src/ext/magma/sage/sage.spec rename to src/sage/ext_data/magma/sage/sage.spec diff --git a/src/ext/magma/spec b/src/sage/ext_data/magma/spec similarity index 100% rename from src/ext/magma/spec rename to src/sage/ext_data/magma/spec diff --git a/src/ext/mwrank/PRIMES b/src/sage/ext_data/mwrank/PRIMES similarity index 100% rename from src/ext/mwrank/PRIMES rename to src/sage/ext_data/mwrank/PRIMES diff --git a/src/ext/nbconvert/postprocess.py b/src/sage/ext_data/nbconvert/postprocess.py similarity index 100% rename from src/ext/nbconvert/postprocess.py rename to src/sage/ext_data/nbconvert/postprocess.py diff --git a/src/ext/nbconvert/rst_sage.tpl b/src/sage/ext_data/nbconvert/rst_sage.tpl similarity index 100% rename from src/ext/nbconvert/rst_sage.tpl rename to src/sage/ext_data/nbconvert/rst_sage.tpl diff --git a/src/sage/ext_data/nodoctest b/src/sage/ext_data/nodoctest new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/ext/notebook-ipython/logo-64x64.png b/src/sage/ext_data/notebook-ipython/logo-64x64.png similarity index 100% rename from src/ext/notebook-ipython/logo-64x64.png rename to src/sage/ext_data/notebook-ipython/logo-64x64.png diff --git a/src/ext/notebook-ipython/logo.svg b/src/sage/ext_data/notebook-ipython/logo.svg similarity index 100% rename from src/ext/notebook-ipython/logo.svg rename to src/sage/ext_data/notebook-ipython/logo.svg diff --git a/src/ext/pari/buzzard/DimensionSk.g b/src/sage/ext_data/pari/buzzard/DimensionSk.g similarity index 100% rename from src/ext/pari/buzzard/DimensionSk.g rename to src/sage/ext_data/pari/buzzard/DimensionSk.g diff --git a/src/ext/pari/buzzard/Tpprog.g b/src/sage/ext_data/pari/buzzard/Tpprog.g similarity index 100% rename from src/ext/pari/buzzard/Tpprog.g rename to src/sage/ext_data/pari/buzzard/Tpprog.g diff --git a/src/ext/pari/buzzard/genusn.g b/src/sage/ext_data/pari/buzzard/genusn.g similarity index 100% rename from src/ext/pari/buzzard/genusn.g rename to src/sage/ext_data/pari/buzzard/genusn.g diff --git a/src/ext/pari/dokchitser/computel.gp b/src/sage/ext_data/pari/dokchitser/computel.gp similarity index 100% rename from src/ext/pari/dokchitser/computel.gp rename to src/sage/ext_data/pari/dokchitser/computel.gp diff --git a/src/ext/pari/dokchitser/computel.gp.template b/src/sage/ext_data/pari/dokchitser/computel.gp.template similarity index 100% rename from src/ext/pari/dokchitser/computel.gp.template rename to src/sage/ext_data/pari/dokchitser/computel.gp.template diff --git a/src/ext/pari/dokchitser/ex-bsw b/src/sage/ext_data/pari/dokchitser/ex-bsw similarity index 100% rename from src/ext/pari/dokchitser/ex-bsw rename to src/sage/ext_data/pari/dokchitser/ex-bsw diff --git a/src/ext/pari/dokchitser/ex-chgen b/src/sage/ext_data/pari/dokchitser/ex-chgen similarity index 100% rename from src/ext/pari/dokchitser/ex-chgen rename to src/sage/ext_data/pari/dokchitser/ex-chgen diff --git a/src/ext/pari/dokchitser/ex-chqua b/src/sage/ext_data/pari/dokchitser/ex-chqua similarity index 100% rename from src/ext/pari/dokchitser/ex-chqua rename to src/sage/ext_data/pari/dokchitser/ex-chqua diff --git a/src/ext/pari/dokchitser/ex-delta b/src/sage/ext_data/pari/dokchitser/ex-delta similarity index 100% rename from src/ext/pari/dokchitser/ex-delta rename to src/sage/ext_data/pari/dokchitser/ex-delta diff --git a/src/ext/pari/dokchitser/ex-eisen b/src/sage/ext_data/pari/dokchitser/ex-eisen similarity index 100% rename from src/ext/pari/dokchitser/ex-eisen rename to src/sage/ext_data/pari/dokchitser/ex-eisen diff --git a/src/ext/pari/dokchitser/ex-gen2 b/src/sage/ext_data/pari/dokchitser/ex-gen2 similarity index 100% rename from src/ext/pari/dokchitser/ex-gen2 rename to src/sage/ext_data/pari/dokchitser/ex-gen2 diff --git a/src/ext/pari/dokchitser/ex-gen3 b/src/sage/ext_data/pari/dokchitser/ex-gen3 similarity index 100% rename from src/ext/pari/dokchitser/ex-gen3 rename to src/sage/ext_data/pari/dokchitser/ex-gen3 diff --git a/src/ext/pari/dokchitser/ex-gen4 b/src/sage/ext_data/pari/dokchitser/ex-gen4 similarity index 100% rename from src/ext/pari/dokchitser/ex-gen4 rename to src/sage/ext_data/pari/dokchitser/ex-gen4 diff --git a/src/ext/pari/dokchitser/ex-nf b/src/sage/ext_data/pari/dokchitser/ex-nf similarity index 100% rename from src/ext/pari/dokchitser/ex-nf rename to src/sage/ext_data/pari/dokchitser/ex-nf diff --git a/src/ext/pari/dokchitser/ex-shin b/src/sage/ext_data/pari/dokchitser/ex-shin similarity index 100% rename from src/ext/pari/dokchitser/ex-shin rename to src/sage/ext_data/pari/dokchitser/ex-shin diff --git a/src/ext/pari/dokchitser/ex-tau2 b/src/sage/ext_data/pari/dokchitser/ex-tau2 similarity index 100% rename from src/ext/pari/dokchitser/ex-tau2 rename to src/sage/ext_data/pari/dokchitser/ex-tau2 diff --git a/src/ext/pari/dokchitser/ex-zeta b/src/sage/ext_data/pari/dokchitser/ex-zeta similarity index 100% rename from src/ext/pari/dokchitser/ex-zeta rename to src/sage/ext_data/pari/dokchitser/ex-zeta diff --git a/src/ext/pari/dokchitser/ex-zeta2 b/src/sage/ext_data/pari/dokchitser/ex-zeta2 similarity index 100% rename from src/ext/pari/dokchitser/ex-zeta2 rename to src/sage/ext_data/pari/dokchitser/ex-zeta2 diff --git a/src/ext/pari/dokchitser/testall b/src/sage/ext_data/pari/dokchitser/testall similarity index 100% rename from src/ext/pari/dokchitser/testall rename to src/sage/ext_data/pari/dokchitser/testall diff --git a/src/ext/pari/simon/ell.gp b/src/sage/ext_data/pari/simon/ell.gp similarity index 100% rename from src/ext/pari/simon/ell.gp rename to src/sage/ext_data/pari/simon/ell.gp diff --git a/src/ext/pari/simon/ellQ.gp b/src/sage/ext_data/pari/simon/ellQ.gp similarity index 100% rename from src/ext/pari/simon/ellQ.gp rename to src/sage/ext_data/pari/simon/ellQ.gp diff --git a/src/ext/pari/simon/ellcommon.gp b/src/sage/ext_data/pari/simon/ellcommon.gp similarity index 100% rename from src/ext/pari/simon/ellcommon.gp rename to src/sage/ext_data/pari/simon/ellcommon.gp diff --git a/src/ext/pari/simon/qfsolve.gp b/src/sage/ext_data/pari/simon/qfsolve.gp similarity index 100% rename from src/ext/pari/simon/qfsolve.gp rename to src/sage/ext_data/pari/simon/qfsolve.gp diff --git a/src/ext/pari/simon/resultant3.gp b/src/sage/ext_data/pari/simon/resultant3.gp similarity index 100% rename from src/ext/pari/simon/resultant3.gp rename to src/sage/ext_data/pari/simon/resultant3.gp diff --git a/src/ext/singular/function_field/core.lib b/src/sage/ext_data/singular/function_field/core.lib similarity index 100% rename from src/ext/singular/function_field/core.lib rename to src/sage/ext_data/singular/function_field/core.lib diff --git a/src/sage/ext_data/threejs/animation.css b/src/sage/ext_data/threejs/animation.css new file mode 100644 index 00000000000..2f7bdc41eec --- /dev/null +++ b/src/sage/ext_data/threejs/animation.css @@ -0,0 +1,158 @@ +/* animation.css */ + +#animation-ui { + position: absolute; + top: 0; + left: 0; + width: 100%; +} + +#animation-ui .tick-marks { + height: 6px; + margin-bottom: 6px; + margin-left: 6px; /* Take into account the slider knob/thumb's width. */ + margin-right: 5px; /* Same, but save a pixel for the last tick mark. */ + background-image: repeating-linear-gradient( + to right, + darkgray 0 1px, /* Make each tick 1px wide. */ + /* -1px here because of that extra pixel on the right margin */ + transparent 1px calc((100% - 1px) / SAGE_FRAME_COUNT) + ); +} + +#animation-ui, +#animation-ui .buttons { + font-size: 0; /* hack to ignore white-space between inline-block elements */ +} + +#animation-ui button { + display: inline-block; + margin: 0; + padding: 2px; + width: 28px; + height: 28px; + background: none; + border: none; + border-radius: 28px; + cursor: pointer; +} + +#animation-ui button:focus { + background: #b8b9ff; + outline: none; +} + +/* Setting the outline to none isn't enough for Firefox. */ +#animation-ui button::-moz-focus-inner { + border: none; +} + +#animation-ui button:active { + background: #efefff; +} + +#animation-ui.playing .play, +#animation-ui.paused .pause, +#animation-ui.loop .once, +#animation-ui.once .loop { + display: none; +} + +/* +Slider styled using range.css tool by Daniel Stern + http://danielstern.ca/range.css/#/ + Generated April 24, 2020 +Customizations: + - darker focus color + - focused styling for Firefox. +*/ +input[type=range].slider { + -webkit-appearance: none; + width: 100%; + margin: 0px 0; +} +input[type=range].slider:focus { + outline: none; +} +input[type=range].slider::-webkit-slider-runnable-track { + width: 100%; + height: 12px; + cursor: pointer; + box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d; + background: #efefff; + border-radius: 6px; + border: 0px solid #000000; +} +input[type=range].slider::-webkit-slider-thumb { + box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d; + border: 0px solid #000000; + height: 12px; + width: 12px; + border-radius: 6px; + background: #3131ff; + cursor: pointer; + -webkit-appearance: none; + margin-top: 0px; +} +input[type=range].slider:focus::-webkit-slider-runnable-track { + background: #b8b9f6; +} +input[type=range].slider::-moz-range-track { + width: 100%; + height: 12px; + cursor: pointer; + box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d; + background: #efefff; + border-radius: 6px; + border: 0px solid #000000; +} +input[type=range].slider::-moz-range-thumb { + box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d; + border: 0px solid #000000; + height: 12px; + width: 12px; + border-radius: 6px; + background: #3131ff; + cursor: pointer; +} +input[type=range].slider:focus::-moz-range-track { + background: #b8b9f6; +} +input[type=range].slider::-moz-focus-outer { + border: none; +} +input[type=range].slider::-ms-track { + width: 100%; + height: 12px; + cursor: pointer; + background: transparent; + border-color: transparent; + color: transparent; +} +input[type=range].slider::-ms-fill-lower { + background: #efefff; + border: 0px solid #000000; + border-radius: 6px; + box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d; +} +input[type=range].slider::-ms-fill-upper { + background: #efefff; + border: 0px solid #000000; + border-radius: 6px; + box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d; +} +input[type=range].slider::-ms-thumb { + box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d; + border: 0px solid #000000; + width: 12px; + border-radius: 6px; + background: #3131ff; + cursor: pointer; + height: 12px; +} +input[type=range].slider:focus::-ms-fill-lower { + background: #b8b9f6; +} +input[type=range].slider:focus::-ms-fill-upper { + background: #b8b9f6; +} diff --git a/src/sage/ext_data/threejs/animation.html b/src/sage/ext_data/threejs/animation.html new file mode 100644 index 00000000000..3fc96ffd92f --- /dev/null +++ b/src/sage/ext_data/threejs/animation.html @@ -0,0 +1,85 @@ + + + + +
    + + +
    + +
    + + + + + + + + + + + +
    + +
    diff --git a/src/sage/ext_data/threejs/animation.js b/src/sage/ext_data/threejs/animation.js new file mode 100644 index 00000000000..35d0997c16f --- /dev/null +++ b/src/sage/ext_data/threejs/animation.js @@ -0,0 +1,273 @@ +// animation.js + +window.updateAnimation = (function() { + + var frameDuration = options.delay; + var frameCount, totalDuration; + var mixer, action, clock; + var ui, slider; + + init(); + options.animationControls && initUI(); + options.autoPlay && play(); + return update; + + function init() { + + var frames = partitionScene(); + frameCount = frames.length; + totalDuration = frameCount * frameDuration; + + var tracks = createTracks( frames ); + var clip = new THREE.AnimationClip( 'sage_animation', totalDuration, tracks ); + + mixer = new THREE.AnimationMixer( scene ); + mixer.addEventListener( 'finished', function() { pause(); } ); + + action = mixer.clipAction( clip ); + action.timeScale = 100; // Our times are in hundredths of a second. + action.clampWhenFinished = true; // Sets paused=true instead of disabling. + action.loop = options.loop ? THREE.LoopRepeat : THREE.LoopOnce; + + // We start/stop the clock to play/pause. The action's `paused` property + // is instead used to determine if the animation has finished. + action.play(); + + clock = new THREE.Clock( false ); // Don't start playing yet. + + } + + // Group objects in by keyframe. The result is a potentially sparse list of + // lists of objects. Assumes keyframe index specified in objects' user data. + function partitionScene() { + var frames = []; + for ( var i=0 ; i < scene.children.length ; i++) { + var obj = scene.children[i]; + var k = obj.userData && parseInt( obj.userData.keyframe ); + if ( k >= 0 ){ + if ( k in frames ) frames[k].push( obj ); + else frames[k] = [obj]; + } + } + return frames; + } + + // Create and return a list of keyframe tracks to animate objects' + // visibility depending on which keyframe they appear in. + function createTracks( frames ) { + var tracks = []; + for ( var keyframe=0 ; keyframe < frames.length ; keyframe++ ) { + var frame = frames[keyframe]; + if ( frame ) { + // Show during the frame. + var times = [keyframe * frameDuration]; + var values = [true]; + // For all but the first frame, start off hidden. + if ( keyframe > 0 ) { + times.unshift( 0 ); + values.unshift( false ); + } + // For all but the last frame, end up hidden. + if ( keyframe < frames.length - 1 ) { + times.push( ( keyframe + 1 ) * frameDuration ); + values.push( false ); + } + for ( var i=0; i < frame.length ; i++ ) { + var binding = frame[i].uuid + '.visible'; + var track = new THREE.BooleanKeyframeTrack( binding, times, values ); + tracks.push( track ); + } + } + } + return tracks; + } + + function initUI() { + + ui = document.getElementById( 'animation-ui' ); + + initSlider(); + + hookupButton( 'play-pause', togglePlayback ); + hookupButton( 'previous', gotoPreviousFrame ); + hookupButton( 'next', gotoNextFrame ); + hookupButton( 'slower', slowDown ); + hookupButton( 'faster', speedUp ); + hookupButton( 'toggle-loop', toggleLooping ); + + function hookupButton( _class, onclick ) { + var button = ui.getElementsByClassName( _class )[0]; + button.addEventListener( 'click', onclick ); + return button; + } + + } + + function initSlider() { + + slider = ui.getElementsByClassName( 'slider' )[0]; + slider.value = action.time; + slider.setAttribute( 'max', totalDuration ); + slider.addEventListener( 'change', change ); + slider.addEventListener( 'input', change ); + slider.addEventListener( 'mousedown', mousedown ); + slider.addEventListener( 'mouseup', mouseup ); + slider.addEventListener( 'keydown', keydown ); + + function change() { + // Keep animation in sync with position of the knob. + seek( parseFloat( slider.value ) ); + } + + // Temporarily pause playback while the knob is being dragged around. + var dragging = false; + var wasPlaying = false; + function mousedown( event ) { + if ( event.button === 0 && !dragging ) { + dragging = true; + wasPlaying = isPlaying(); + pause(); + } + } + function mouseup( event ) { + if ( event.button === 0 && dragging ) { + dragging = false; + if ( wasPlaying && !isFinished() ) play(); + } + } + + function keydown( event ) { + switch ( event.key ) { + // Toggle playback using spacebar/enter. + case ' ': + case 'Enter': + if ( !event.repeat ) { + togglePlayback(); + event.preventDefault(); + } + break; + // Adjust speed using up/down arrows. + case 'Down': + case 'ArrowDown': + slowDown(); + event.preventDefault(); + break; + case 'Up': + case 'ArrowUp': + speedUp(); + event.preventDefault(); + break; + // Navigate frame-by-frame using left/right arrows. + case 'Left': + case 'ArrowLeft': + gotoPreviousFrame(); + event.preventDefault(); + break; + case 'Right': + case 'ArrowRight': + gotoNextFrame(); + event.preventDefault(); + break; + } + } + + } + + function isPlaying() { + return clock.running; + } + + function isFinished() { + return action.paused; + } + + function play() { + if ( isFinished() ) action.reset(); // Return to the beginning. + clock.start(); + updateUI(); + requestAnimationFrame( render ); // Re-enter render loop. + } + + function pause() { + clock.stop(); + updateUI(); + } + + function togglePlayback() { + isPlaying() ? pause() : play(); + } + + function seek( time ) { + + // Ensure time is in range: [0, totalDuration]. + time = Math.max( 0, Math.min( totalDuration, time ) ); + action.time = time; + + // Make sure the animation is treated as finished if set to the very + // end of the animation. + action.paused = ( !isLooping() && time == totalDuration ); + + updateUI(); + + if ( !isPlaying() ) { + // Ensure the animation gets re-rendered at the new position. + requestAnimationFrame( render ); // Trigger a re-render. + } + + } + + function moveKeyframes( delta ) { + var keyframe = Math.floor( action.time / frameDuration ); + keyframe += delta; + // Ensure keyframe is in range: [0, frameCount]. + keyframe = Math.max( 0, Math.min( frameCount, keyframe ) ); + seek( keyframe * frameDuration ); + } + + function gotoPreviousFrame() { + pause(); + moveKeyframes( -1 ); + } + + function gotoNextFrame() { + pause(); + moveKeyframes( +1 ); + } + + function speedUp() { + action.timeScale *= 1.25; + } + + function slowDown() { + action.timeScale /= 1.25; + } + + function isLooping() { + return action.loop === THREE.LoopRepeat; + } + + function setLooping( looping ) { + action.loop = looping ? THREE.LoopRepeat : THREE.LoopOnce; + updateUI(); + } + + function toggleLooping() { + setLooping( !isLooping() ); + } + + function update() { + mixer.update( clock.getDelta() ); + updateUI(); + return clock.running; // Should exit render loop if false. + } + + function updateUI() { + if ( ui ) { + ui.classList.remove( 'playing', 'paused', 'once', 'loop' ); + ui.classList.add( isPlaying() ? 'playing' : 'paused' ); + ui.classList.add( isLooping() ? 'loop' : 'once' ); + } + if ( slider ) slider.value = action.time; + } + +})(); diff --git a/src/ext/threejs/threejs_template.html b/src/sage/ext_data/threejs/threejs_template.html similarity index 77% rename from src/ext/threejs/threejs_template.html rename to src/sage/ext_data/threejs/threejs_template.html index 5670792cb48..62dc0c44a93 100644 --- a/src/ext/threejs/threejs_template.html +++ b/src/sage/ext_data/threejs/threejs_template.html @@ -8,15 +8,21 @@ body { margin: 0px; overflow: hidden; } - #menu-container { position: absolute; bottom: 30px; right: 40px; } + #menu-container { position: absolute; bottom: 30px; right: 40px; cursor: default; } + + #menu-message { position: absolute; bottom: 0px; right: 0px; white-space: nowrap; + display: none; background-color: #F5F5F5; padding: 10px; } #menu-content { position: absolute; bottom: 0px; right: 0px; display: none; background-color: #F5F5F5; border-bottom: 1px solid black; border-right: 1px solid black; border-left: 1px solid black; } #menu-content div { border-top: 1px solid black; padding: 10px; white-space: nowrap; } - + + #menu-content div:hover { background-color: #FEFEFE;; } + +SAGE_STYLES @@ -32,10 +38,7 @@ document.body.appendChild( renderer.domElement ); var options = SAGE_OPTIONS; - - // When animations are supported by the viewer, the value 'false' - // will be replaced with an option set in Python by the user - var animate = false; // options.animate; + var animate = options.animate; var b = SAGE_BOUNDS; // bounds @@ -58,12 +61,12 @@ var yRange = b[1].y - b[0].y; var zRange = b[1].z - b[0].z; - var ar = options.aspect_ratio; + var ar = options.aspectRatio; var a = [ ar[0], ar[1], ar[2] ]; // aspect multipliers var autoAspect = 2.5; if ( zRange > autoAspect * rRange && a[2] === 1 ) a[2] = autoAspect * rRange / zRange; - // Distance from (xMid,yMid,zMid) to any corner of the bounding box, after applying aspect_ratio. + // Distance from (xMid,yMid,zMid) to any corner of the bounding box, after applying aspectRatio var midToCorner = Math.sqrt( a[0]*a[0]*xRange*xRange + a[1]*a[1]*yRange*yRange + a[2]*a[2]*zRange*zRange ) / 2; var xMid = ( b[0].x + b[1].x ) / 2; @@ -76,11 +79,11 @@ var boxMesh = new THREE.Line( box ); if ( options.frame ) scene.add( new THREE.BoxHelper( boxMesh, 'black' ) ); - if ( options.axes_labels ) { + if ( options.axesLabels ) { var d = options.decimals; // decimals var offsetRatio = 0.1; - var al = options.axes_labels; + var al = options.axesLabels; var offset = offsetRatio * a[1]*( b[1].y - b[0].y ); var xm = xMid.toFixed(d); @@ -105,45 +108,77 @@ } - function addLabel( text, x, y, z ) { - - var fontsize = 14; + function addLabel( text, x, y, z, color='black', fontsize=14 ) { var canvas = document.createElement( 'canvas' ); + var context = canvas.getContext( '2d' ); var pixelRatio = Math.round( window.devicePixelRatio ); - canvas.width = 128 * pixelRatio; - canvas.height = 32 * pixelRatio; // powers of two - canvas.style.width = '128px'; - canvas.style.height = '32px'; - var context = canvas.getContext( '2d' ); - context.scale( pixelRatio, pixelRatio ); - context.fillStyle = 'black'; context.font = fontsize + 'px monospace'; + var width = context.measureText( text ).width; + var height = fontsize; + + // The dimensions of the canvas's underlying image data need to be powers + // of two in order for the resulting texture to support mipmapping. + canvas.width = THREE.MathUtils.ceilPowerOfTwo( width * pixelRatio ); + canvas.height = THREE.MathUtils.ceilPowerOfTwo( height * pixelRatio ); + + // Re-compute the unscaled dimensions after the power of two conversion. + width = canvas.width / pixelRatio; + height = canvas.height / pixelRatio; + + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + + context.scale( pixelRatio, pixelRatio ); + context.fillStyle = color; + context.font = fontsize + 'px monospace'; // Must be set again after measureText. context.textAlign = 'center'; context.textBaseline = 'middle'; - context.fillText( text, canvas.width/2/pixelRatio, canvas.height/2/pixelRatio ); + context.fillText( text, width/2, height/2 ); var texture = new THREE.Texture( canvas ); texture.needsUpdate = true; - var sprite = new THREE.Sprite( new THREE.SpriteMaterial( { map: texture } ) ); + var materialOptions = { map: texture, sizeAttenuation: false }; + var sprite = new THREE.Sprite( new THREE.SpriteMaterial( materialOptions ) ); sprite.position.set( x, y, z ); - // Set the initial scale based on plot size to accomodate orthographic projection. - // For other projections, the scale will get reset each frame based on camera distance. - var scale = midToCorner/2; - sprite.scale.set( scale, scale*.25 ); // ratio of canvas width to height + // Scaling factor, chosen somewhat arbitrarily so that the size of the text + // is consistent with previously generated plots. + var scale = 1/625; + if ( options.projection === 'orthographic' ) { + scale = midToCorner/256; // Needs to scale along with the plot itself. + } + sprite.scale.set( scale * width, scale * height, 1 ); scene.add( sprite ); + return sprite; + } if ( options.axes ) scene.add( new THREE.AxesHelper( Math.min( a[0]*b[1].x, a[1]*b[1].y, a[2]*b[1].z ) ) ); var camera = createCamera(); camera.up.set( 0, 0, 1 ); - camera.position.set( a[0]*(xMid+xRange), a[1]*(yMid+yRange), a[2]*(zMid+zRange) ); + camera.position.set( a[0]*xMid, a[1]*yMid, a[2]*zMid ); + + var offset = new THREE.Vector3( a[0]*xRange, a[1]*yRange, a[2]*zRange ); + + if ( options.viewpoint ) { + + var aa = options.viewpoint; + var axis = new THREE.Vector3( aa[0][0], aa[0][1], aa[0][2] ).normalize(); + var angle = aa[1] * Math.PI / 180; + var q = new THREE.Quaternion().setFromAxisAngle( axis, angle ).inverse(); + + offset.set( 0, 0, offset.length() ); + offset.applyQuaternion( q ); + + } + + camera.position.add( offset ); function createCamera() { @@ -201,16 +236,20 @@ controls.addEventListener( 'change', function() { if ( !animate ) render(); } ); window.addEventListener( 'resize', function() { - + renderer.setSize( window.innerWidth, window.innerHeight ); updateCameraAspect( camera, window.innerWidth / window.innerHeight ); if ( !animate ) render(); - + } ); var texts = SAGE_TEXTS; - for ( var i=0 ; i < texts.length ; i++ ) - addLabel( texts[i].text, a[0]*texts[i].x, a[1]*texts[i].y, a[2]*texts[i].z ); + for ( var i=0 ; i < texts.length ; i++ ) addText( texts[i] ); + + function addText( json ) { + var sprite = addLabel( json.text, a[0]*json.x, a[1]*json.y, a[2]*json.z, json.color ); + sprite.userData = json; + } var points = SAGE_POINTS; for ( var i=0 ; i < points.length ; i++ ) addPoint( points[i] ); @@ -246,6 +285,7 @@ var mesh = new THREE.Points( geometry, material ); mesh.position.set( c.x, c.y, c.z ); + mesh.userData = json; scene.add( mesh ); } @@ -272,6 +312,7 @@ var mesh = new THREE.Line( geometry, material ); mesh.position.set( c.x, c.y, c.z ); + mesh.userData = json; scene.add( mesh ); } @@ -300,12 +341,13 @@ var side = json.singleSide ? THREE.FrontSide : THREE.DoubleSide; var transparent = json.opacity < 1 ? true : false; + var flatShading = json.useFlatShading ? json.useFlatShading : false; var material = new THREE.MeshPhongMaterial( { side: side, color: useFaceColors ? 'white' : json.color, vertexColors: useFaceColors ? THREE.FaceColors : THREE.NoColors, transparent: transparent, opacity: json.opacity, - shininess: 20, flatShading: json.useFlatShading } ); + shininess: 20, flatShading: flatShading } ); var c = new THREE.Vector3(); geometry.computeBoundingBox(); @@ -315,6 +357,7 @@ var mesh = new THREE.Mesh( geometry, material ); mesh.position.set( c.x, c.y, c.z ); if ( transparent && json.renderOrder ) mesh.renderOrder = json.renderOrder; + mesh.userData = json; scene.add( mesh ); if ( json.showMeshGrid ) { @@ -343,33 +386,22 @@ var mesh = new THREE.LineSegments( geometry, material ); mesh.position.set( c.x, c.y, c.z ); + mesh.userData = json; scene.add( mesh ); } } - var scratch = new THREE.Vector3(); - function render() { + if ( window.updateAnimation ) animate = updateAnimation(); if ( animate ) requestAnimationFrame( render ); + renderer.render( scene, camera ); - // Resize text based on distance from camera. - // Not neccessary for orthographic due to the nature of the projection (preserves sizes). - if ( !camera.isOrthographicCamera ) { - for ( var i=0 ; i < scene.children.length ; i++ ) { - if ( scene.children[i].type === 'Sprite' ) { - var sprite = scene.children[i]; - var adjust = scratch.addVectors( sprite.position, scene.position ) - .sub( camera.position ).length() / 5; - sprite.scale.set( adjust, .25*adjust ); // ratio of canvas width to height - } - } - } } - + render(); controls.update(); if ( !animate ) render(); @@ -410,20 +442,38 @@ function getViewpoint() { - var info = '
    ' + JSON.stringify( camera, null, '\t' ) + '
    '; - window.open().document.write( info ); + function roundTo( x, n ) { return +x.toFixed(n); } + + var v = camera.quaternion.inverse(); + var r = Math.sqrt( v.x*v.x + v.y*v.y + v.z*v.z ); + var axis = [ roundTo( v.x / r, 4 ), roundTo( v.y / r, 4 ), roundTo( v.z / r, 4 ) ]; + var angle = roundTo( 2 * Math.atan2( r, v.w ) * 180 / Math.PI, 2 ); + + var textArea = document.createElement( 'textarea' ); + textArea.textContent = JSON.stringify( axis ) + ',' + angle; + textArea.style.csstext = 'position: absolute; top: -100%'; + document.body.append( textArea ); + textArea.select(); + document.execCommand( 'copy' ); + + var m = document.getElementById( 'menu-message' ); + m.innerHTML = 'Viewpoint copied to clipboard'; + m.style.display = 'block'; + setTimeout( function() { m.style.display = 'none'; }, 2000 ); } +SAGE_EXTRA_HTML diff --git a/src/ext/valgrind/pyalloc.supp b/src/sage/ext_data/valgrind/pyalloc.supp similarity index 100% rename from src/ext/valgrind/pyalloc.supp rename to src/sage/ext_data/valgrind/pyalloc.supp diff --git a/src/ext/valgrind/sage-additional.supp b/src/sage/ext_data/valgrind/sage-additional.supp similarity index 100% rename from src/ext/valgrind/sage-additional.supp rename to src/sage/ext_data/valgrind/sage-additional.supp diff --git a/src/ext/valgrind/sage.supp b/src/sage/ext_data/valgrind/sage.supp similarity index 100% rename from src/ext/valgrind/sage.supp rename to src/sage/ext_data/valgrind/sage.supp diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index ca61368d138..933984be5d9 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -55,9 +55,9 @@ from distutils.errors import CCompilerError from distutils.spawn import find_executable -from sage.misc import six from sage.env import SAGE_SHARE + class TrivialClasscallMetaClass(type): """ A trivial version of :class:`ClasscallMetaclass` without Cython dependencies. @@ -73,7 +73,7 @@ def __call__(cls, *args, **kwds): _trivial_unique_representation_cache = dict() -class TrivialUniqueRepresentation(six.with_metaclass(TrivialClasscallMetaClass)): +class TrivialUniqueRepresentation(metaclass=TrivialClasscallMetaClass): """ A trivial version of :class:`UniqueRepresentation` without Cython dependencies. """ diff --git a/src/sage/features/kenzo.py b/src/sage/features/kenzo.py new file mode 100644 index 00000000000..c3c68ede71b --- /dev/null +++ b/src/sage/features/kenzo.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +r""" +Check for Kenzo +""" + +from sage.libs.ecl import ecl_eval +from . import Feature, FeatureTestResult + +class Kenzo(Feature): + r""" + A :class:`sage.features.Feature` describing the presence of ``Kenzo``. + + EXAMPLES:: + + sage: from sage.features.kenzo import Kenzo + sage: Kenzo().is_present() # optional - kenzo + FeatureTestResult('Kenzo', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.kenzo import Kenzo + sage: isinstance(Kenzo(), Kenzo) + True + """ + Feature.__init__(self, name="Kenzo", spkg="kenzo", + url="https://github.com/miguelmarco/kenzo/") + + def _is_present(self): + r""" + Check whether Kenzo is installed and works. + + EXAMPLES:: + + sage: from sage.features.kenzo import Kenzo + sage: Kenzo()._is_present() # optional - kenzo + FeatureTestResult('Kenzo', True) + """ + # Redirection of ECL and Maxima stdout to /dev/null + # This is also done in the Maxima library, but we + # also do it here for redundancy. + ecl_eval(r"""(defparameter *dev-null* (make-two-way-stream + (make-concatenated-stream) (make-broadcast-stream)))""") + ecl_eval("(setf original-standard-output *standard-output*)") + ecl_eval("(setf *standard-output* *dev-null*)") + + try: + ecl_eval("(require :kenzo)") + except RuntimeError: + return FeatureTestResult(self, False, reason="Unable to make ECL require kenzo") + return FeatureTestResult(self, True) + diff --git a/src/sage/finance/stock.py b/src/sage/finance/stock.py index 9fcc391fb5d..d1a5d713355 100644 --- a/src/sage/finance/stock.py +++ b/src/sage/finance/stock.py @@ -43,8 +43,7 @@ from sage.structure.all import Sequence from datetime import date -# import compatible with py2 and py3 -from six.moves.urllib.request import urlopen +from urllib.request import urlopen class OHLC: @@ -138,7 +137,7 @@ def __ne__(self, other): True """ return not (self == other) - + class Stock: """ diff --git a/src/sage/functions/all.py b/src/sage/functions/all.py index b84db9e7a57..0c449236e67 100644 --- a/src/sage/functions/all.py +++ b/src/sage/functions/all.py @@ -20,13 +20,13 @@ reciprocal_trig_functions = {'sec': cos, 'csc': sin, 'cot': tan, 'sech': cosh, 'csch': sinh, 'coth': tanh} -from .other import ( ceil, floor, abs_symbolic, sqrt, +from .other import ( ceil, floor, abs_symbolic, sqrt, real_nth_root, arg, real_part, real, frac, factorial, binomial, imag_part, imag, imaginary, conjugate, cases, complex_root_of) -from .log import (exp, exp_polar, log, ln, polylog, dilog, lambert_w, harmonic_number) +from .log import (exp, exp_polar, log, ln, polylog, dilog, lambert_w, harmonic_number) from .transcendental import (zeta, zetaderiv, zeta_symmetric, hurwitz_zeta, dickman_rho, stieltjes) diff --git a/src/sage/functions/exp_integral.py b/src/sage/functions/exp_integral.py index f3f1a6513bc..7f4ced54aa1 100644 --- a/src/sage/functions/exp_integral.py +++ b/src/sage/functions/exp_integral.py @@ -427,7 +427,7 @@ class Function_log_integral(BuiltinFunction): """ def __init__(self): - """ + r""" See the docstring for ``Function_log_integral``. EXAMPLES:: @@ -438,9 +438,16 @@ def __init__(self): li(x) sage: log_integral(x)._fricas_init_() 'li(x)' + + TESTS: + + Verify that :trac:`28917` is fixed:: + + sage: latex(log_integral(x)) + \operatorname{log\_integral}\left(x\right) """ BuiltinFunction.__init__(self, "log_integral", nargs=1, - latex_name=r'log_integral', + latex_name=r'\operatorname{log\_integral}', conversions=dict(maxima='expintegral_li', sympy='li', fricas='li')) @@ -615,8 +622,8 @@ class Function_log_integral_offset(BuiltinFunction): """ def __init__(self): - """ - See the docstring for ``Function_log_integral-offset``. + r""" + See the docstring for ``Function_log_integral_offset``. EXAMPLES:: @@ -625,9 +632,16 @@ def __init__(self): sage: log_integral_offset(x, hold=True)._sympy_() Li(x) + TESTS: + + Verify that the problem described in :trac:`28917` no longer appears here:: + + sage: latex(log_integral_offset) + \operatorname{log\_integral\_offset} + """ BuiltinFunction.__init__(self, "log_integral_offset", nargs=1, - latex_name=r'log_integral_offset', + latex_name=r'\operatorname{log\_integral\_offset}', conversions=dict(sympy='Li')) def _eval_(self,z): diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index 5cae346dd8d..060aaf8fc2f 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -161,7 +161,6 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ diff --git a/src/sage/functions/jacobi.py b/src/sage/functions/jacobi.py index 21fbec8bb93..b61892dda09 100644 --- a/src/sage/functions/jacobi.py +++ b/src/sage/functions/jacobi.py @@ -125,7 +125,7 @@ REFERENCES: -- :wikipedia:`Jacobi's_elliptic_functions` +- :wikipedia:`Jacobi%27s_elliptic_functions` - [KS2002]_ diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index 1f9ddcb0be2..d7a4c921eda 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -8,8 +8,6 @@ - Tomas Kalvoda (2015-04-01): Add :meth:`exp_polar()` (:trac:`18085`) """ -from six.moves import range - from sage.symbolic.function import GinacFunction, BuiltinFunction from sage.symbolic.constants import e as const_e from sage.symbolic.constants import pi as const_pi @@ -155,7 +153,7 @@ def __init__(self): e^x """ GinacFunction.__init__(self, "exp", latex_name=r"\exp", - conversions=dict(maxima='exp', fricas='exp')) + conversions=dict(maxima='exp', fricas='exp')) exp = Function_exp() @@ -265,8 +263,8 @@ def __init__(self): log """ GinacFunction.__init__(self, 'log', ginac_name='logb', nargs=2, - latex_name=r'\log', - conversions=dict(maxima='log')) + latex_name=r'\log', + conversions=dict(maxima='log')) logb = Function_log2() @@ -411,7 +409,7 @@ def log(*args, **kwds): -I*log(3)/pi sage: log(int(8),2) 3 - sage: log(8,int(2)) # known bug, see #21518 + sage: log(8,int(2)) 3 sage: log(8,2) 3 @@ -419,7 +417,7 @@ def log(*args, **kwds): -3 sage: log(1/8,1/2) 3 - sage: log(8,1/2) # known bug, see #21517 + sage: log(8,1/2) -3 sage: log(1000, 10, base=5) @@ -445,6 +443,7 @@ def log(*args, **kwds): except (AttributeError, TypeError): return logb(args[0], args[1]) + class Function_polylog(GinacFunction): def __init__(self): r""" @@ -538,14 +537,21 @@ def __init__(self): [1.644934066848226 +/- ...] sage: parent(_) Complex ball field with 53 bits of precision + + sage: polylog(1,-1) # known bug + -log(2) """ - GinacFunction.__init__(self, "polylog", nargs=2) + GinacFunction.__init__(self, "polylog", nargs=2, + conversions=dict(mathematica='PolyLog', + magma='Polylog', + matlab='polylog', + sympy='polylog')) def _maxima_init_evaled_(self, *args): """ EXAMPLES: - These are indirect doctests for this function.:: + These are indirect doctests for this function:: sage: polylog(2, x)._maxima_() li[2](_SAGE_VAR_x) @@ -562,14 +568,15 @@ def _maxima_init_evaled_(self, *args): args_maxima.append(str(a)) n, x = args_maxima - if int(n) in [1,2,3]: - return 'li[%s](%s)'%(n, x) + if int(n) in [1, 2, 3]: + return 'li[%s](%s)' % (n, x) else: - return 'polylog(%s, %s)'%(n, x) + return 'polylog(%s, %s)' % (n, x) polylog = Function_polylog() + class Function_dilog(GinacFunction): def __init__(self): r""" @@ -657,8 +664,27 @@ def __init__(self): """ GinacFunction.__init__(self, 'dilog', conversions=dict(maxima='li[2]', + magma='Dilog', fricas='(x+->dilog(1-x))')) + def _sympy_(self, z): + r""" + Special case for sympy, where there is no dilog function. + + EXAMPLES:: + + sage: w = dilog(x)._sympy_(); w + polylog(2, x) + sage: w.diff() + polylog(1, x)/x + sage: w._sage_() + dilog(x) + """ + import sympy + from sympy import polylog as sympy_polylog + return sympy_polylog(2, sympy.sympify(z, evaluate=False)) + + dilog = Function_dilog() @@ -674,9 +700,9 @@ class Function_lambert_w(BuiltinFunction): INPUT: - - ``n`` - an integer. `n=0` corresponds to the principal branch. + - ``n`` -- an integer. `n=0` corresponds to the principal branch. - - ``z`` - a complex number + - ``z`` -- a complex number If called with a single argument, that argument is ``z`` and the branch ``n`` is assumed to be 0 (the principal branch). @@ -1020,7 +1046,7 @@ def __init__(self): INPUT: - - ``z`` - a complex number `z = a + ib`. + - ``z`` -- a complex number `z = a + ib`. OUTPUT: @@ -1389,6 +1415,7 @@ def _swap_harmonic(a,b): return harmonic_number(b,a) register_symbol(_swap_harmonic,{'maxima':'gen_harmonic_number'}) register_symbol(_swap_harmonic,{'maple':'harmonic'}) + class Function_harmonic_number(BuiltinFunction): r""" Harmonic number function, defined by: diff --git a/src/sage/functions/min_max.py b/src/sage/functions/min_max.py index ba19dc8739b..b4c7a3f9f1b 100644 --- a/src/sage/functions/min_max.py +++ b/src/sage/functions/min_max.py @@ -39,7 +39,7 @@ from sage.symbolic.expression import Expression from sage.symbolic.ring import SR -from six.moves.builtins import max as builtin_max, min as builtin_min +from builtins import max as builtin_max, min as builtin_min class MinMax_base(BuiltinFunction): def eval_helper(self, this_f, builtin_f, initial_val, args): diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index 5cedc083de6..d1717b1f92e 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -298,7 +298,6 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** -from six.moves import range import warnings @@ -531,7 +530,7 @@ def _eval_(self, n, x): # Numerical evaluation failed => keep symbolic return None - + class Func_chebyshev_T(ChebyshevFunction): """ Chebyshev polynomials of the first kind. @@ -2007,7 +2006,7 @@ def __init__(self): class Func_laguerre(OrthogonalFunction): """ REFERENCE: - + - [AS1964]_ 22.5.16, page 778 and page 789. """ def __init__(self): diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index ec1e87a8174..21a03792d21 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -13,8 +13,6 @@ beta(x, x) """ from __future__ import print_function -from six.moves import range -from six import integer_types from sage.misc.lazy_import import lazy_import lazy_import('sage.functions.gamma', @@ -35,6 +33,7 @@ from sage.structure.all import parent as s_parent from sage.functions.trig import arctan2 + from sage.arith.all import binomial as arith_binomial one_half = SR.one() / SR(2) @@ -186,7 +185,7 @@ def _eval_floor_ceil(self, x, method, bits=0, **kwds): else: return m() - if isinstance(x, integer_types): + if isinstance(x, int): return Integer(x) if isinstance(x, (float, complex)): m = getattr(math, method) @@ -447,7 +446,7 @@ def _eval_(self, x): try: return x.ceil() except AttributeError: - if isinstance(x, integer_types): + if isinstance(x, int): return Integer(x) elif isinstance(x, (float, complex)): return Integer(math.ceil(x)) @@ -611,7 +610,7 @@ def _eval_(self, x): try: return x.floor() except AttributeError: - if isinstance(x, integer_types): + if isinstance(x, int): return Integer(x) elif isinstance(x, (float, complex)): return Integer(math.floor(x)) @@ -619,6 +618,7 @@ def _eval_(self, x): floor = Function_floor() + class Function_Order(GinacFunction): def __init__(self): r""" @@ -679,7 +679,6 @@ def _sympy_(self, arg): import sympy return sympy.O(*sympy.sympify(arg, evaluate=False)) - Order = Function_Order() @@ -741,7 +740,7 @@ def _eval_(self, x): try: return x - x.floor() except AttributeError: - if isinstance(x, integer_types): + if isinstance(x, int): return Integer(0) elif isinstance(x, (float, complex)): return x - Integer(math.floor(x)) @@ -753,6 +752,7 @@ def _eval_(self, x): frac = Function_frac() + def _do_sqrt(x, prec=None, extend=True, all=False): r""" Used internally to compute the square root of x. @@ -911,6 +911,162 @@ def sqrt(x, *args, **kwds): {'__call__': staticmethod(sqrt), '__setstate__': lambda x, y: None}) + +class Function_real_nth_root(BuiltinFunction): + r""" + Real `n`-th root function `x^\frac{1}{n}`. + + The function assumes positive integer `n` and real number `x`. + + EXAMPLES:: + + sage: v = real_nth_root(2, 3) + sage: v + real_nth_root(2, 3) + sage: v^3 + 2 + sage: v = real_nth_root(-2, 3) + sage: v + real_nth_root(-2, 3) + sage: v^3 + -2 + sage: real_nth_root(8, 3) + 2 + sage: real_nth_root(-8, 3) + -2 + + For numeric input, it gives a numerical approximation. :: + + sage: real_nth_root(2., 3) + 1.25992104989487 + sage: real_nth_root(-2., 3) + -1.25992104989487 + + Some symbolic calculus:: + + sage: f = real_nth_root(x, 5)^3 + sage: f + real_nth_root(x^3, 5) + sage: f.diff() + 3/5*x^2*real_nth_root(x^(-12), 5) + sage: f.integrate(x) + integrate((abs(x)^3)^(1/5)*sgn(x^3), x) + sage: _.diff() + (abs(x)^3)^(1/5)*sgn(x^3) + + """ + def __init__(self): + r""" + Initialize. + + TESTS:: + + sage: cube_root = real_nth_root(x, 3) + sage: loads(dumps(cube_root)) + real_nth_root(x, 3) + + :: + + sage: f = real_nth_root(x, 3) + sage: f._sympy_() + Piecewise((Abs(x)**(1/3)*sign(x), Eq(im(x), 0)), (x**(1/3), True)) + + """ + BuiltinFunction.__init__(self, "real_nth_root", nargs=2, + conversions=dict(sympy='real_root', + mathematica='Surd', + maple='surd')) + + def _print_latex_(self, base, exp): + r""" + TESTS:: + + sage: latex(real_nth_root(x, 3)) + x^{\frac{1}{3}} + sage: latex(real_nth_root(x^2 + x, 3)) + {\left(x^{2} + x\right)}^{\frac{1}{3}} + """ + return latex(base**(1/exp)) + + def _evalf_(self, base, exp, parent=None): + """ + TESTS:: + + sage: real_nth_root(RIF(2), 3) + 1.259921049894873? + sage: real_nth_root(RBF(2), 3) + [1.259921049894873 +/- 3.92e-16] + sage: real_nth_root(-2, 4) + Traceback (most recent call last): + ... + ValueError: no real nth root of negative real number with even n + """ + negative = base < 0 + + if negative: + if exp % 2 == 0: + raise ValueError('no real nth root of negative real number with even n') + base = -base + + r = base**(1/exp) + + if negative: + return -r + else: + return r + + def _eval_(self, base, exp): + """ + TESTS:: + + sage: real_nth_root(x, 1) + x + sage: real_nth_root(x, 3) + real_nth_root(x, 3) + """ + if not isinstance(base, Expression) and not isinstance(exp, Expression): + if isinstance(base, Integer): + try: + return base.nth_root(exp) + except ValueError: + pass + self._evalf_(base, exp, parent=s_parent(base)) + + if isinstance(exp, Integer) and exp.is_one(): + return base + + def _power_(self, base, exp, power_param=None): + """ + TESTS:: + + sage: f = real_nth_root(x, 3) + sage: f^5 + real_nth_root(x^5, 3) + """ + return self(base**power_param, exp) + + def _derivative_(self, base, exp, diff_param=None): + """ + TESTS:: + + sage: f = real_nth_root(x, 3) + sage: f.diff() + 1/3*real_nth_root(x^(-2), 3) + sage: f = real_nth_root(-x, 3) + sage: f.diff() + -1/3*real_nth_root(x^(-2), 3) + sage: f = real_nth_root(x, 4) + sage: f.diff() + 1/4*real_nth_root(x^(-3), 4) + sage: f = real_nth_root(-x, 4) + sage: f.diff() + -1/4*real_nth_root(-1/x^3, 4) + """ + return 1/exp * self(base, exp)**(1-exp) + +real_nth_root = Function_real_nth_root() + + class Function_arg(BuiltinFunction): def __init__(self): r""" @@ -1154,6 +1310,7 @@ def __call__(self, x, **kwargs): real = real_part = Function_real_part() + class Function_imag_part(GinacFunction): def __init__(self): r""" @@ -1300,6 +1457,7 @@ def __init__(self): conjugate = Function_conjugate() + class Function_factorial(GinacFunction): def __init__(self): r""" @@ -1465,6 +1623,13 @@ def _eval_(self, x): sage: factorial(RBF(2)**64) [+/- 2.30e+347382171326740403407] + + Check that :trac:`26749` is fixed:: + + sage: factorial(float(3.2)) # abs tol 1e-14 + 7.7566895357931776 + sage: type(factorial(float(3.2))) + """ if isinstance(x, Integer): try: @@ -1475,10 +1640,15 @@ def _eval_(self, x): from sage.functions.gamma import gamma return gamma(x + 1) elif self._is_numerical(x): - return (x + 1).gamma() + try: + return (x + 1).gamma() + except AttributeError: + from sage.functions.gamma import gamma + return gamma(x + 1) factorial = Function_factorial() + class Function_binomial(GinacFunction): def __init__(self): r""" @@ -1984,6 +2154,14 @@ def _print_latex_(self, l, **kwargs): cases(((x == 0, pi), (1, 0))) sage: latex(ex) \begin{cases}{\pi} & {x = 0}\\{0} & {1}\end{cases} + + TESTS: + + Verify that :trac:`25624` is fixed:: + + sage: L = latex(cases([(x == 0, 0), (1, 1)])) + sage: L + \begin{cases}{0} & {x = 0}\\{1} & {1}\end{cases} """ if not isinstance(l, (list, tuple)): raise ValueError("cases() argument must be a list") @@ -1995,7 +2173,7 @@ def _print_latex_(self, l, **kwargs): else: right = pair str += r"{%s} & {%s}\\" % (latex(left), latex(right)) - print(str[:-2] + r"\end{cases}") + return str[:-2] + r"\end{cases}" def _sympy_(self, l): """ diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index e8743b8fe2f..790b3f0878c 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -77,8 +77,6 @@ from sage.symbolic.ring import SR from sage.rings.infinity import minus_infinity, infinity -from six import get_function_code - class PiecewiseFunction(BuiltinFunction): def __init__(self): @@ -149,7 +147,7 @@ def __call__(self, function_pieces, **kwds): if isinstance(function, FunctionType): if var is None: var = SR.var('x') - if get_function_code(function).co_argcount == 0: + if function.__code__.co_argcount == 0: function = function() else: function = function(var) diff --git a/src/sage/functions/prime_pi.pyx b/src/sage/functions/prime_pi.pyx index a29dce47bff..60ddf604af1 100644 --- a/src/sage/functions/prime_pi.pyx +++ b/src/sage/functions/prime_pi.pyx @@ -18,15 +18,15 @@ EXAMPLES:: True """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2009,2011 R. Andrew Ohana # Copyright (C) 2009 William Stein # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from cypari2.paridecl cimport * from cysignals.signals cimport * @@ -274,7 +274,7 @@ cdef class PrimePi(BuiltinFunction): cdef uint32_t _cached_count(self, uint32_t p): r""" For p < 65536, returns the value stored in ``self.__smallPi[p]``. For - p <= ``self.__maxSieve``, uses a binary seach on ``self.__primes`` to + p <= ``self.__maxSieve``, uses a binary search on ``self.__primes`` to compute pi(p). """ # inspired by Yann Laigle-Chapuy's suggestion diff --git a/src/sage/functions/spike_function.py b/src/sage/functions/spike_function.py index 56635ca98fa..00bd6ea83de 100644 --- a/src/sage/functions/spike_function.py +++ b/src/sage/functions/spike_function.py @@ -7,15 +7,15 @@ - Karl-Dieter Crisman (2009-09): adding documentation and doctests """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein # Copyright (C) 2009 Karl-Dieter Crisman # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from __future__ import print_function import math @@ -24,19 +24,20 @@ from sage.modules.free_module_element import vector from sage.rings.all import RDF + class SpikeFunction: """ Base class for spike functions. INPUT: - - ``v`` - list of pairs (x, height) + - ``v`` - list of pairs (x, height) - - ``eps`` - parameter that determines approximation to a true spike + - ``eps`` - parameter that determines approximation to a true spike OUTPUT: - a function with spikes at each point ``x`` in ``v`` with the given height. + a function with spikes at each point ``x`` in ``v`` with the given height. EXAMPLES:: @@ -62,7 +63,7 @@ class SpikeFunction: """ def __init__(self, v, eps=0.0000001): """ - Initializes base class SpikeFunction. + Initialize base class SpikeFunction. EXAMPLES:: @@ -73,8 +74,8 @@ def __init__(self, v, eps=0.0000001): sage: S.eps 0.00100000000000000 """ - if len(v) == 0: - v = [(0,0)] + if not v: + v = [(0, 0)] v = sorted([(float(x[0]), float(x[1])) for x in v]) notify = False @@ -101,13 +102,14 @@ def __repr__(self): sage: spike_function([(-3,4),(-1,1),(2,3)],0.001) A spike function with spikes at [-3.0, -1.0, 2.0] """ - return "A spike function with spikes at %s"%self.support + return "A spike function with spikes at %s" % self.support def _eval(self, x): """ - Evaluates spike function. Note that when one calls - the function within the tolerance, the return value - is the full height at that point. + Evaluate spike function. + + Note that when one calls the function within the tolerance, + the return value is the full height at that point. EXAMPLES:: @@ -158,10 +160,10 @@ def plot_fft_abs(self, samples=2**12, xmin=None, xmax=None, **kwds): A spike function with spikes at [-3.0, -1.0, 2.0] sage: P = S.plot_fft_abs(8) sage: p = P[0]; p.ydata # abs tol 1e-8 - [5.0, 5.0, 3.367958691924177, 3.367958691924177, 4.123105625617661, 4.123105625617661, 4.759921664218055, 4.759921664218055] + [5.0, 5.0, 3.367958691924177, 3.367958691924177, 4.123105625617661, + 4.123105625617661, 4.759921664218055, 4.759921664218055] """ - w = self.vector(samples = samples, xmin=xmin, xmax=xmax) - xmin, xmax = self._ranges(xmin, xmax) + w = self.vector(samples=samples, xmin=xmin, xmax=xmax) z = w.fft() k = vector(RDF, [abs(z[i]) for i in range(len(z)//2)]) return k.plot(xmin=0, xmax=1, **kwds) @@ -177,36 +179,38 @@ def plot_fft_arg(self, samples=2**12, xmin=None, xmax=None, **kwds): A spike function with spikes at [-3.0, -1.0, 2.0] sage: P = S.plot_fft_arg(8) sage: p = P[0]; p.ydata # abs tol 1e-8 - [0.0, 0.0, -0.211524990023434, -0.211524990023434, 0.244978663126864, 0.244978663126864, -0.149106180027477, -0.149106180027477] + [0.0, 0.0, -0.211524990023434, -0.211524990023434, + 0.244978663126864, 0.244978663126864, -0.149106180027477, + -0.149106180027477] """ - w = self.vector(samples = samples, xmin=xmin, xmax=xmax) - xmin, xmax = self._ranges(xmin, xmax) + w = self.vector(samples=samples, xmin=xmin, xmax=xmax) z = w.fft() k = vector(RDF, [(z[i]).arg() for i in range(len(z)//2)]) return k.plot(xmin=0, xmax=1, **kwds) def vector(self, samples=2**16, xmin=None, xmax=None): """ - Creates a sampling vector of the spike function in question. + Create a sampling vector of the spike function in question. EXAMPLES:: sage: S = spike_function([(-3,4),(-1,1),(2,3)],0.001); S A spike function with spikes at [-3.0, -1.0, 2.0] sage: S.vector(16) - (4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) + (4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0) """ - v = vector(RDF, samples) # creates vector of zeros of length 2^16 + v = vector(RDF, samples) # creates vector of zeros of length 2^16 xmin, xmax = self._ranges(xmin, xmax) - delta = (xmax - xmin)/samples - w = int(math.ceil(self.eps/delta)) + delta = (xmax - xmin) / samples + w = int(math.ceil(self.eps / delta)) for i in range(len(self.support)): x = self.support[i] if x > xmax: break h = self.height[i] - j = int((x - xmin)/delta) - for k in range(j, min(samples, j+w)): + j = int((x - xmin) / delta) + for k in range(j, min(samples, j + w)): v[k] = h return v @@ -222,11 +226,11 @@ def _ranges(self, xmin, xmax): """ width = (self.support[-1] + self.support[0])/float(2) if xmin is None: - xmin = self.support[0] - width/float(5) + xmin = self.support[0] - width/float(5) if xmax is None: - xmax = self.support[-1] + width/float(5) + xmax = self.support[-1] + width/float(5) if xmax <= xmin: - xmax = xmin + 1 + xmax = xmin + 1 return xmin, xmax def plot(self, xmin=None, xmax=None, **kwds): @@ -246,16 +250,16 @@ def plot(self, xmin=None, xmax=None, **kwds): eps = self.eps while x < xmax: y, i = self._eval(x) - v.append( (x, y) ) + v.append((x, y)) if i != -1: x0 = self.support[i] + eps - v.extend([(x0,y), (x0,0)]) + v.extend([(x0, y), (x0, 0)]) if i+1 < len(self.support): - x = self.support[i+1] - eps - v.append( (x, 0) ) + x = self.support[i + 1] - eps + v.append((x, 0)) else: x = xmax - v.append( (xmax, 0) ) + v.append((xmax, 0)) else: new_x = None for j in range(len(self.support)): @@ -264,12 +268,12 @@ def plot(self, xmin=None, xmax=None, **kwds): break if new_x is None: new_x = xmax - v.append( (new_x, 0) ) + v.append((new_x, 0)) x = new_x L = line(v, **kwds) - L.xmin(xmin-1); L.xmax(xmax) + L.xmin(xmin-1) + L.xmax(xmax) return L - spike_function = SpikeFunction diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index a97ae90ae70..6d2c630cd40 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -669,7 +669,7 @@ def is_efficient(self, payoff_vector): sage: long_game.is_efficient({1: 20, 2: 20, 3: 5, 4: 20}) True """ - pl = tuple(sorted(list(self.player_list))) + pl = tuple(sorted(self.player_list)) return sum(payoff_vector.values()) == self.ch_f[pl] def nullplayer(self, payoff_vector): diff --git a/src/sage/games/all.py b/src/sage/games/all.py index d8066fd6190..e3e31bcf03e 100644 --- a/src/sage/games/all.py +++ b/src/sage/games/all.py @@ -1,20 +1,2 @@ -""" -Test for deprecations of imports into global namespace:: - - sage: backtrack_all - doctest:warning...: - DeprecationWarning: - Importing backtrack_all from here is deprecated. If you need to use it, please import it directly from sage.games.sudoku_backtrack - See https://trac.sagemath.org/27066 for details. - ... -""" -from __future__ import absolute_import - -from sage.misc.lazy_import import lazy_import - -lazy_import("sage.games.sudoku_backtrack", 'backtrack_all', deprecation=27066) - from .sudoku import Sudoku, sudoku from .hexad import Minimog - -del absolute_import diff --git a/src/sage/games/sudoku.py b/src/sage/games/sudoku.py index f767e52e4e5..fd3ea3e018a 100644 --- a/src/sage/games/sudoku.py +++ b/src/sage/games/sudoku.py @@ -21,8 +21,6 @@ # http://www.gnu.org/licenses/ ###################################################################### from __future__ import print_function, absolute_import -from six.moves import range -from six import string_types from sage.structure.sage_object import SageObject @@ -183,7 +181,7 @@ def __init__(self, puzzle, verify_input = True): if verify_input and not(puzzle.is_square()): raise ValueError('Sudoku puzzle must be a square matrix') self.puzzle = tuple([int(x) for x in puzzle.list()]) - elif isinstance(puzzle, string_types): + elif isinstance(puzzle, str): puzzle_size = int(round(sqrt(len(puzzle)))) puzzle_numeric = [] for char in puzzle: diff --git a/src/sage/geometry/all.py b/src/sage/geometry/all.py index e9907d2aad6..ade132ba84b 100644 --- a/src/sage/geometry/all.py +++ b/src/sage/geometry/all.py @@ -4,6 +4,7 @@ from .cone import Cone, random_cone +lazy_import('sage.geometry', 'cone_catalog', 'cones') from .fan import Fan, FaceFan, NormalFan, Fan2d diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index afb30e59490..d28fa0091ec 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -192,7 +192,6 @@ # Use python-3.x versions of print() and range(). from __future__ import print_function -from six.moves import range import collections import copy @@ -356,6 +355,14 @@ def Cone(rays, lattice=None, check=True, normalize=True): sage: origin.lattice() 2-d lattice N + However, the trivial cone in ``n`` dimensions has a predefined + constructor for you to use:: + + sage: origin = cones.trivial(2) + sage: origin.rays() + Empty collection + in 2-d lattice N + Of course, you can also provide ``lattice`` in other cases:: sage: L = ToricLattice(3, "L") @@ -1093,7 +1100,7 @@ def codim(self): The codimension of the nonnegative orthant is zero, since the span of its generators equals the entire ambient space:: - sage: K = Cone([(1,0,0), (0,1,0), (0,0,1)]) + sage: K = cones.nonnegative_orthant(3) sage: K.codim() 0 @@ -1118,19 +1125,19 @@ def codim(self): And if the cone is trivial in any space, then its codimension is equal to the dimension of the ambient space:: - sage: K = Cone([], lattice=ToricLattice(0)) + sage: K = cones.trivial(0) sage: K.lattice_dim() 0 sage: K.codim() 0 - sage: K = Cone([(0,)]) + sage: K = cones.trivial(1) sage: K.lattice_dim() 1 sage: K.codim() 1 - sage: K = Cone([(0,0)]) + sage: K = cones.trivial(2) sage: K.lattice_dim() 2 sage: K.codim() @@ -1192,7 +1199,7 @@ def span(self, base_ring=None): The span of the nonnegative orthant is the entire ambient lattice:: - sage: K = Cone([(1,0,0),(0,1,0),(0,0,1)]) + sage: K = cones.nonnegative_orthant(3) sage: K.span() == K.lattice() True @@ -1210,8 +1217,7 @@ def span(self, base_ring=None): We can take the span of the trivial cone:: - sage: K = Cone([], ToricLattice(0)) - sage: K.span() + sage: cones.trivial(0).span() Sublattice <> The span of a solid cone is the entire ambient space:: @@ -1472,7 +1478,7 @@ def _PPL_cone(self): sage: Cone([(0,0)])._PPL_cone() A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point - sage: Cone([], lattice=ToricLattice(2))._PPL_cone() + sage: cones.trivial(2)._PPL_cone() A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point """ @@ -2765,6 +2771,11 @@ def incidence_matrix(self): sage: halfspace.incidence_matrix().is_immutable() True + + Check that the base ring is ``ZZ``, see :trac:`29840`:: + + sage: halfspace.incidence_matrix().base_ring() + Integer Ring """ normals = self.facet_normals() incidence_matrix = matrix(ZZ, self.nrays(), @@ -3022,13 +3033,15 @@ def is_isomorphic(self, other): We check that :trac:`18613` is fixed:: - sage: K = Cone([], ToricLattice(0)) + sage: K = cones.trivial(0) sage: K.is_isomorphic(K) True - sage: K = Cone([(0,)]) + sage: K = cones.trivial(1) + sage: K.is_isomorphic(K) + True + sage: K = cones.trivial(2) sage: K.is_isomorphic(K) True - sage: K = Cone([(0,0)]) A random (strictly convex) cone is isomorphic to itself:: @@ -3115,7 +3128,7 @@ def is_trivial(self): EXAMPLES:: - sage: c0 = Cone([], lattice=ToricLattice(3)) + sage: c0 = cones.trivial(3) sage: c0.is_trivial() True sage: c0.nrays() @@ -3327,7 +3340,7 @@ def strict_quotient(self): The quotient of the trivial cone is trivial:: - sage: K = Cone([], ToricLattice(0)) + sage: K = cones.trivial(0) sage: K.strict_quotient() 0-d cone in 0-d lattice N sage: K = Cone([(0,0,0,0)]) @@ -3437,10 +3450,10 @@ def solid_restriction(self): The solid restriction of the trivial cone lives in a trivial space:: - sage: K = Cone([], ToricLattice(0)) + sage: K = cones.trivial(0) sage: K.solid_restriction() 0-d cone in 0-d lattice N - sage: K = Cone([(0,0,0,0)]) + sage: K = cones.trivial(4) sage: K.solid_restriction() 0-d cone in 0-d lattice N @@ -3543,7 +3556,7 @@ def _split_ambient_lattice(self): Trivial cone:: - sage: trivial_cone = Cone([], lattice=ToricLattice(3)) + sage: trivial_cone = cones.trivial(3) sage: trivial_cone._split_ambient_lattice() sage: trivial_cone._sublattice Sublattice <> @@ -4081,7 +4094,7 @@ def semigroup_generators(self): sage: len(Cone(identity_matrix(10).rows()).semigroup_generators()) 10 - sage: trivial_cone = Cone([], lattice=ToricLattice(3)) + sage: trivial_cone = cones.trivial(3) sage: trivial_cone.semigroup_generators() Empty collection in 3-d lattice N @@ -4374,7 +4387,7 @@ def is_solid(self): The nonnegative orthant is always solid:: - sage: quadrant = Cone([(1,0), (0,1)]) + sage: quadrant = cones.nonnegative_orthant(2) sage: quadrant.is_solid() True sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) @@ -4422,7 +4435,7 @@ def is_proper(self): The nonnegative orthant is always proper:: - sage: quadrant = Cone([(1,0), (0,1)]) + sage: quadrant = cones.nonnegative_orthant(2) sage: quadrant.is_proper() True sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) @@ -4467,7 +4480,7 @@ def is_full_space(self): Neither is the nonnegative orthant:: - sage: K = Cone([(1,0),(0,1)]) + sage: K = cones.nonnegative_orthant(2) sage: K.is_full_space() False @@ -4509,7 +4522,7 @@ def lineality(self): The lineality of the nonnegative orthant is zero, since it clearly contains no lines:: - sage: K = Cone([(1,0,0), (0,1,0), (0,0,1)]) + sage: K = cones.nonnegative_orthant(3) sage: K.lineality() 0 @@ -4534,7 +4547,7 @@ def lineality(self): Per the definition, the lineality of the trivial cone in a trivial space is zero:: - sage: K = Cone([], lattice=ToricLattice(0)) + sage: K = cones.trivial(0) sage: K.lineality() 0 @@ -4590,7 +4603,7 @@ def discrete_complementarity_set(self): Pairs of standard basis elements form a discrete complementarity set for the nonnegative orthant:: - sage: K = Cone([(1,0),(0,1)]) + sage: K = cones.nonnegative_orthant(2) sage: K.discrete_complementarity_set() ((N(1, 0), M(0, 1)), (N(0, 1), M(1, 0))) @@ -4619,9 +4632,7 @@ def discrete_complementarity_set(self): Likewise for trivial cones, whose duals are the entire space:: - sage: L = ToricLattice(0) - sage: K = Cone([], ToricLattice(0)) - sage: K.discrete_complementarity_set() + sage: cones.trivial(0).discrete_complementarity_set() () TESTS: @@ -4683,7 +4694,7 @@ def lyapunov_like_basis(self): Every transformation is Lyapunov-like on the trivial cone:: - sage: K = Cone([(0,0)]) + sage: K = cones.trivial(2) sage: M = MatrixSpace(K.lattice().base_field(), K.lattice_dim()) sage: list(M.basis()) == K.lyapunov_like_basis() True @@ -4701,26 +4712,25 @@ def lyapunov_like_basis(self): However, in a trivial space, there are no non-trivial linear maps, so there can be no Lyapunov-like basis:: - sage: L = ToricLattice(0) - sage: K = Cone([], lattice=L) + sage: K = cones.trivial(0) sage: K.lyapunov_like_basis() [] The Lyapunov-like transformations on the nonnegative orthant are diagonal matrices:: - sage: K = Cone([(1,)]) + sage: K = cones.nonnegative_orthant(1) sage: K.lyapunov_like_basis() [[1]] - sage: K = Cone([(1,0),(0,1)]) + sage: K = cones.nonnegative_orthant(2) sage: K.lyapunov_like_basis() [ [1 0] [0 0] [0 0], [0 1] ] - sage: K = Cone([(1,0,0),(0,1,0),(0,0,1)]) + sage: K = cones.nonnegative_orthant(3) sage: K.lyapunov_like_basis() [ [1 0 0] [0 0 0] [0 0 0] @@ -4850,13 +4860,13 @@ def lyapunov_rank(self): The Lyapunov rank of the nonnegative orthant is the same as the dimension of the ambient space [RNPA2011]_:: - sage: positives = Cone([(1,)]) + sage: positives = cones.nonnegative_orthant(1) sage: positives.lyapunov_rank() 1 - sage: quadrant = Cone([(1,0), (0,1)]) + sage: quadrant = cones.nonnegative_orthant(2) sage: quadrant.lyapunov_rank() 2 - sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) + sage: octant = cones.nonnegative_orthant(3) sage: octant.lyapunov_rank() 3 @@ -5066,7 +5076,7 @@ def random_element(self, ring=ZZ): The trivial element ``()`` is always returned in a trivial space:: sage: set_random_seed() - sage: K = Cone([], ToricLattice(0)) + sage: K = cones.trivial(0) sage: K.random_element() N() sage: K.random_element(ring=QQ) @@ -5075,7 +5085,7 @@ def random_element(self, ring=ZZ): A random element of the trivial cone in a nontrivial space is zero:: sage: set_random_seed() - sage: K = Cone([(0,0,0)]) + sage: K = cones.trivial(3) sage: K.random_element() N(0, 0, 0) sage: K.random_element(ring=QQ) @@ -5085,7 +5095,7 @@ def random_element(self, ring=ZZ): components nonnegative:: sage: set_random_seed() - sage: K = Cone([(1,0,0),(0,1,0),(0,0,1)]) + sage: K = cones.nonnegative_orthant(3) sage: all( x >= 0 for x in K.random_element() ) True sage: all( x >= 0 for x in K.random_element(ring=QQ) ) @@ -5259,17 +5269,17 @@ def positive_operators_gens(self, K2=None): The trivial cone in a trivial space has no positive operators:: - sage: K = Cone([], ToricLattice(0)) + sage: K = cones.trivial(0) sage: K.positive_operators_gens() [] Every operator is positive on the trivial cone:: - sage: K = Cone([(0,)]) + sage: K = cones.trivial(1) sage: K.positive_operators_gens() [[1], [-1]] - sage: K = Cone([(0,0)]) + sage: K = cones.trivial(2) sage: K.is_trivial() True sage: K.positive_operators_gens() @@ -5386,9 +5396,7 @@ def positive_operators_gens(self, K2=None): expected dimensions [Or2018b]_:: sage: n = ZZ.random_element(5) - sage: K = Cone([[0] * n], ToricLattice(n)) - sage: K.is_trivial() - True + sage: K = cones.trivial(n) sage: L = ToricLattice(n^2) sage: pi_gens = K.positive_operators_gens() sage: pi_cone = Cone((g.list() for g in pi_gens), @@ -5434,9 +5442,7 @@ def positive_operators_gens(self, K2=None): the expected linealities [Or2018b]_:: sage: n = ZZ.random_element(5) - sage: K = Cone([[0] * n], ToricLattice(n)) - sage: K.is_trivial() - True + sage: K = cones.trivial(n) sage: L = ToricLattice(n^2) sage: pi_gens = K.positive_operators_gens() sage: pi_cone = Cone((g.list() for g in pi_gens), @@ -5664,7 +5670,7 @@ def cross_positive_operators_gens(self): negations of Z-matrices; that is, matrices whose off-diagonal elements are nonnegative:: - sage: K = Cone([(1,0),(0,1)]) + sage: K = cones.nonnegative_orthant(2) sage: K.cross_positive_operators_gens() [ [0 1] [0 0] [1 0] [-1 0] [0 0] [ 0 0] @@ -5680,7 +5686,7 @@ def cross_positive_operators_gens(self): The trivial cone in a trivial space has no cross-positive operators:: - sage: K = Cone([], ToricLattice(0)) + sage: K = cones.trivial(0) sage: K.cross_positive_operators_gens() [] @@ -5779,9 +5785,7 @@ def cross_positive_operators_gens(self): the expected dimensions [Or2018b]_:: sage: n = ZZ.random_element(5) - sage: K = Cone([[0] * n], ToricLattice(n)) - sage: K.is_trivial() - True + sage: K = cones.trivial(n) sage: L = ToricLattice(n^2) sage: cp_gens = K.cross_positive_operators_gens() sage: cp_cone = Cone((g.list() for g in cp_gens), diff --git a/src/sage/geometry/cone_catalog.py b/src/sage/geometry/cone_catalog.py new file mode 100644 index 00000000000..94e4c136bb6 --- /dev/null +++ b/src/sage/geometry/cone_catalog.py @@ -0,0 +1,694 @@ +r""" +Catalog of common polyhedral convex cones + +This module provides shortcut functions, grouped under the +globally-available ``cones`` prefix, to create some common cones: + +- The nonnegative orthant, +- The rearrangement cone of order ``p``, +- The Schur cone, +- The trivial cone. + +At the moment, only convex rational polyhedral cones are +supported---specifically, those cones that can be built using the +:func:`Cone` constructor. As a result, each shortcut method can be +passed either an ambient dimension ``ambient_dim``, or a toric +``lattice`` (from which the dimension can be inferred) to determine +the ambient space. + +Here are some typical usage examples:: + + sage: cones.nonnegative_orthant(2).rays() + N(1, 0), + N(0, 1) + in 2-d lattice N + +:: + + sage: cones.rearrangement(2,2).rays() + N( 1, 0), + N( 1, -1), + N(-1, 1) + in 2-d lattice N + +:: + + sage: cones.schur(3).rays() + N(1, -1, 0), + N(0, 1, -1) + in 3-d lattice N + +:: + + sage: cones.trivial(3).rays() + Empty collection + in 3-d lattice N + +To specify some other lattice, pass it as an argument to the function:: + + sage: K = cones.nonnegative_orthant(3) + sage: cones.schur(lattice=K.dual().lattice()) + 2-d cone in 3-d lattice M + +For more information about these cones, see the documentation for the +individual functions and the references therein. +""" + +# +# WARNING: +# +# Don't import anything into global scope here. If you do, it will +# show up in the cones. list, and that list should contain only +# the top-level non-underscore functions defined in this module. +# + +def _preprocess_args(ambient_dim, lattice): + r""" + Preprocess arguments for cone-constructing functions. + + Each cone-constructing function in this module performs some + preprocessing on its ``ambient_dim`` and ``lattice`` arguments to + ensure that they both wind up defined and compatible with one + another. In particular, the ``ambient_dim`` can be inferred from + a ``lattice`` and a default lattice can be chosen based on an + ``ambient_dim``. Both can be specified so long as the rank of + the ``lattice`` matches the ``ambient_dim``; it is an error + if the two numbers are different. + + Since each function performs exactly the same steps, + + - Check that either ``ambient_dim`` or ``lattice`` was given, + - Infer the ``ambient_dim`` from the ``lattice`` if ``lattice`` + was given and ``ambient_dim`` was not, + - Use the default ``lattice`` if ``ambient_dim`` was given and + ``lattice`` was not, + - Ensure that the rank of the ``lattice`` equals ``ambient_dim``, + + we collect them here. + + INPUT: + + - ``ambient_dim`` -- a nonnegative integer; the dimension of the + ambient space in which the cone will live + + - ``lattice`` -- a toric lattice; the lattice in which the cone + will live + + OUTPUT: + + A pair ``(ambient_dim, lattice)`` containing the processed + arguments. Both are guaranteed to be defined (that is, not + ``None``) and the rank of the ``lattice`` will equal + ``ambient_dim``. + + EXAMPLES: + + If the ``lattice`` argument is ``None``, then the default lattice + with rank equal to ``ambient_dim`` will be used:: + + sage: from sage.geometry.cone_catalog import _preprocess_args + sage: _preprocess_args(3, None) + (3, 3-d lattice N) + + If the ``ambient_dim`` argument is ``None``, then the rank of the + ``lattice`` will be used as the ambient dimension:: + + sage: from sage.geometry.cone_catalog import _preprocess_args + sage: _preprocess_args(None, ToricLattice(3)) + (3, 3-d lattice N) + + So long as the rank of ``lattice`` equals ``ambient_dim``, it is + not an error for both to be defined:: + + sage: from sage.geometry.cone_catalog import _preprocess_args + sage: _preprocess_args(3, ToricLattice(3)) + (3, 3-d lattice N) + + .. NOTE:: + + The two error conditions are tested in the cone-constructing + functions themselves, which introduces some duplication but + serves to ensure that ``_preprocess_args()`` is actually called + in each such function. + """ + from sage.geometry.toric_lattice import ToricLattice + + if ambient_dim is None and lattice is None: + raise ValueError("either the ambient dimension or the lattice " + "must be specified") + + if ambient_dim is None: + ambient_dim = lattice.rank() + + if lattice is None: + lattice = ToricLattice(ambient_dim) + + if lattice.rank() != ambient_dim: + raise ValueError("lattice rank=%d and ambient_dim=%d " + "are incompatible" % (lattice.rank(), ambient_dim)) + + return (ambient_dim, lattice) + + +def nonnegative_orthant(ambient_dim=None, lattice=None): + r""" + The nonnegative orthant in ``ambient_dim`` dimensions, or living + in ``lattice``. + + The nonnegative orthant consists of all componentwise-nonnegative + vectors. It is the convex-conic hull of the standard basis. + + INPUT: + + - ``ambient_dim`` -- a nonnegative integer (default: ``None``); the + dimension of the ambient space + + - ``lattice`` -- a toric lattice (default: ``None``); the lattice in + which the cone will live + + If ``ambient_dim`` is omitted, then it will be inferred from the + rank of ``lattice``. If the ``lattice`` is omitted, then the + default lattice of rank ``ambient_dim`` will be used. + + A ``ValueError`` is raised if neither ``ambient_dim`` nor + ``lattice`` are specified. It is also a ``ValueError`` to specify + both ``ambient_dim`` and ``lattice`` unless the rank of + ``lattice`` is equal to ``ambient_dim``. + + OUTPUT: + + A :class:`.ConvexRationalPolyhedralCone` living in ``lattice`` + and having ``ambient_dim`` standard basis vectors as its + generators. Each generating ray has the integer ring as its + base ring. + + A ``ValueError`` can be raised if the inputs are incompatible or + insufficient. See the INPUT documentation for details. + + REFERENCES: + + - Chapter 2 in [BV2009]_ (Examples 2.4, 2.14, and 2.23 in particular) + + EXAMPLES:: + + sage: cones.nonnegative_orthant(3).rays() + N(1, 0, 0), + N(0, 1, 0), + N(0, 0, 1) + in 3-d lattice N + + TESTS: + + We can construct the trivial cone as the nonnegative orthant in a + trivial vector space:: + + sage: cones.nonnegative_orthant(0) + 0-d cone in 0-d lattice N + + The nonnegative orthant is a proper cone:: + + sage: set_random_seed() + sage: ambient_dim = ZZ.random_element(10) + sage: K = cones.nonnegative_orthant(ambient_dim) + sage: K.is_proper() + True + + If a ``lattice`` was given, it is actually used:: + + sage: L = ToricLattice(3, 'M') + sage: cones.nonnegative_orthant(lattice=L) + 3-d cone in 3-d lattice M + + Unless the rank of the lattice disagrees with ``ambient_dim``:: + + sage: L = ToricLattice(1, 'M') + sage: cones.nonnegative_orthant(3, lattice=L) + Traceback (most recent call last): + ... + ValueError: lattice rank=1 and ambient_dim=3 are incompatible + + We also get an error if no arguments are given:: + + sage: cones.nonnegative_orthant() + Traceback (most recent call last): + ... + ValueError: either the ambient dimension or the lattice must + be specified + """ + from sage.geometry.cone import Cone + from sage.matrix.constructor import matrix + from sage.rings.all import ZZ + + (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice) + + I = matrix.identity(ZZ, ambient_dim) + return Cone(I.rows(), lattice) + + +def rearrangement(p, ambient_dim=None, lattice=None): + r""" + The rearrangement cone of order ``p`` in ``ambient_dim`` + dimensions, or living in ``lattice``. + + The rearrangement cone of order ``p`` in ``ambient_dim`` + dimensions consists of all vectors of length ``ambient_dim`` + whose smallest ``p`` components sum to a nonnegative number. + + For example, the rearrangement cone of order one has its single + smallest component nonnegative. This implies that all components + are nonnegative, and that therefore the rearrangement cone of + order one is the nonnegative orthant in its ambient space. + + When ``p`` and ``ambient_dim`` are equal, all components of the + cone's elements must sum to a nonnegative number. In other + words, the rearrangement cone of order ``ambient_dim`` is a + half-space. + + INPUT: + + - ``p`` -- a nonnegative integer; the number of components to + "rearrange", between ``1`` and ``ambient_dim`` inclusive + + - ``ambient_dim`` -- a nonnegative integer (default: ``None``); the + dimension of the ambient space + + - ``lattice`` -- a toric lattice (default: ``None``); the lattice in + which the cone will live + + If ``ambient_dim`` is omitted, then it will be inferred from the + rank of ``lattice``. If the ``lattice`` is omitted, then the + default lattice of rank ``ambient_dim`` will be used. + + A ``ValueError`` is raised if neither ``ambient_dim`` nor + ``lattice`` are specified. It is also a ``ValueError`` to specify + both ``ambient_dim`` and ``lattice`` unless the rank of + ``lattice`` is equal to ``ambient_dim``. + + It is also a ``ValueError`` to specify a non-integer ``p``. + + OUTPUT: + + A :class:`.ConvexRationalPolyhedralCone` representing the + rearrangement cone of order ``p`` living in ``lattice``, with + ambient dimension ``ambient_dim``. Each generating ray has the + integer ring as its base ring. + + A ``ValueError`` can be raised if the inputs are incompatible or + insufficient. See the INPUT documentation for details. + + ALGORITHM: + + Suppose that the ambient space is of dimension `n`. The extreme + directions of the rearrangement cone for `1 \le p \le n-1` are + given by [Jeong2017]_ Theorem 5.2.3. When `2 \le p \le n-2` (that + is, if we ignore `p = 1` and `p = n-1`), they consist of + + - the standard basis `\left\{e_{1},e_{2},\ldots,e_{n}\right\}` for + the ambient space, and + + - the `n` vectors `\left(1,1,\ldots,1\right)^{T} - pe_{i}` for + `i = 1,2,\ldots,n`. + + Special cases are then given for `p = 1` and `p = n-1` in the + theorem. However in SageMath we don't need conically-independent + extreme directions. We only need a generating set, because the + :func:`Cone` function will eliminate any redundant generators. And + one can easily verify that the special-case extreme directions for + `p = 1` and `p = n-1` are contained in the conic hull of the `2n` + generators just described. The half space resulting from `p = n` + is also covered by this set of generators, so for all valid `p` we + simply take the conic hull of those `2n` vectors. + + REFERENCES: + + - [GJ2016]_, Section 4 + + - [HS2010]_, Example 2.21 + + - [Jeong2017]_, Section 5.2 + + EXAMPLES: + + The rearrangement cones of order one are nonnegative orthants:: + + sage: orthant = cones.nonnegative_orthant(6) + sage: cones.rearrangement(1,6).is_equivalent(orthant) + True + + When ``p`` and ``ambient_dim`` are equal, the rearrangement cone + is a half-space, so we expect its lineality to be one less than + ``ambient_dim`` because it will contain a hyperplane but is not + the entire space:: + + sage: cones.rearrangement(5,5).lineality() + 4 + + Jeong's Proposition 5.2.1 [Jeong2017]_ states that all rearrangement + cones are proper when ``p`` is less than ``ambient_dim``:: + + sage: all( cones.rearrangement(p, ambient_dim).is_proper() + ....: for ambient_dim in range(10) + ....: for p in range(1, ambient_dim) ) + True + + Jeong's Corollary 5.2.4 [Jeong2017]_ states that if `p = n-1` in + an `n`-dimensional ambient space, then the Lyapunov rank of the + rearrangement cone is `n`, and that for all other `p > 1` its + Lyapunov rank is one:: + + sage: all( cones.rearrangement(p, ambient_dim).lyapunov_rank() + ....: == + ....: ambient_dim + ....: for ambient_dim in range(2, 10) + ....: for p in [ ambient_dim-1 ] ) + True + sage: all( cones.rearrangement(p, ambient_dim).lyapunov_rank() == 1 + ....: for ambient_dim in range(3, 10) + ....: for p in range(2, ambient_dim-1) ) + True + + TESTS: + + Jeong's Proposition 5.2.1 [Jeong2017]_ states that rearrangement + cones are permutation-invariant:: + + sage: ambient_dim = ZZ.random_element(2,10).abs() + sage: p = ZZ.random_element(1, ambient_dim) + sage: K = cones.rearrangement(p, ambient_dim) + sage: P = SymmetricGroup(ambient_dim).random_element().matrix() + sage: all( K.contains(P*r) for r in K ) + True + + The smallest ``p`` components of every element of the rearrangement + cone should sum to a nonnegative number. In other words, the + generators really are what we think they are:: + + sage: set_random_seed() + sage: def _has_rearrangement_property(v,p): + ....: return sum( sorted(v)[0:p] ) >= 0 + sage: all( + ....: _has_rearrangement_property( + ....: cones.rearrangement(p, ambient_dim).random_element(), + ....: p + ....: ) + ....: for ambient_dim in range(2, 10) + ....: for p in range(1, ambient_dim+1) + ....: ) + True + + The rearrangement cone of order ``p`` is, almost by definition, + contained in the rearrangement cone of order ``p + 1``:: + + sage: set_random_seed() + sage: ambient_dim = ZZ.random_element(2,10) + sage: p = ZZ.random_element(1, ambient_dim) + sage: K1 = cones.rearrangement(p, ambient_dim) + sage: K2 = cones.rearrangement(p+1, ambient_dim) + sage: all( x in K2 for x in K1 ) + True + + Jeong's Proposition 5.2.1 [Jeong2017]_ states that the rearrangement + cone of order ``p`` is linearly isomorphic to the rearrangement + cone of order ``ambient_dim - p`` when ``p`` is less than + ``ambient_dim``:: + + sage: set_random_seed() + sage: ambient_dim = ZZ.random_element(2,10) + sage: p = ZZ.random_element(1, ambient_dim) + sage: K1 = cones.rearrangement(p, ambient_dim) + sage: K2 = cones.rearrangement(ambient_dim-p, ambient_dim) + sage: Mp = ((1/p)*matrix.ones(QQ, ambient_dim) + ....: - matrix.identity(QQ, ambient_dim)) + sage: Cone( (Mp*K2.rays()).columns() ).is_equivalent(K1) + True + + The order ``p`` should be an integer between ``1`` and + ``ambient_dim``, inclusive:: + + sage: cones.rearrangement(0,3) + Traceback (most recent call last): + ... + ValueError: order p=0 should be an integer between 1 and + ambient_dim=3, inclusive + sage: cones.rearrangement(5,3) + Traceback (most recent call last): + ... + ValueError: order p=5 should be an integer between 1 and + ambient_dim=3, inclusive + sage: cones.rearrangement(3/2, 3) + Traceback (most recent call last): + ... + ValueError: order p=3/2 should be an integer between 1 and + ambient_dim=3, inclusive + + If a ``lattice`` was given, it is actually used:: + + sage: L = ToricLattice(3, 'M') + sage: cones.rearrangement(2, 3, lattice=L) + 3-d cone in 3-d lattice M + + Unless the rank of the lattice disagrees with ``ambient_dim``:: + + sage: L = ToricLattice(1, 'M') + sage: cones.rearrangement(2, 3, lattice=L) + Traceback (most recent call last): + ... + ValueError: lattice rank=1 and ambient_dim=3 are incompatible + + We also get an error if neither the ambient dimension nor lattice + are specified:: + + sage: cones.rearrangement(3) + Traceback (most recent call last): + ... + ValueError: either the ambient dimension or the lattice must + be specified + """ + from sage.geometry.cone import Cone + from sage.matrix.constructor import matrix + from sage.rings.all import ZZ + + (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice) + + if p < 1 or p > ambient_dim or not p in ZZ: + raise ValueError("order p=%s should be an integer between 1 " + "and ambient_dim=%d, inclusive" % (p, ambient_dim)) + + I = matrix.identity(ZZ, ambient_dim) + M = matrix.ones(ZZ, ambient_dim) - p*I + G = matrix.identity(ZZ, ambient_dim).rows() + M.rows() + return Cone(G, lattice=lattice) + + +def schur(ambient_dim=None, lattice=None): + r""" + The Schur cone in ``ambient_dim`` dimensions, or living + in ``lattice``. + + The Schur cone in `n` dimensions induces the majorization ordering + on the ambient space. If `\left\{e_{1}, e_{2}, \ldots, + e_{n}\right\}` is the standard basis for the space, then its + generators are `\left\{e_{i} - e_{i+1}\ |\ 1 \le i \le + n-1\right\}`. Its dual is the downward monotonic cone. + + INPUT: + + - ``ambient_dim`` -- a nonnegative integer (default: ``None``); the + dimension of the ambient space + + - ``lattice`` -- a toric lattice (default: ``None``); the lattice in + which the cone will live + + If ``ambient_dim`` is omitted, then it will be inferred from the + rank of ``lattice``. If the ``lattice`` is omitted, then the + default lattice of rank ``ambient_dim`` will be used. + + A ``ValueError`` is raised if neither ``ambient_dim`` nor + ``lattice`` are specified. It is also a ``ValueError`` to specify + both ``ambient_dim`` and ``lattice`` unless the rank of + ``lattice`` is equal to ``ambient_dim``. + + OUTPUT: + + A :class:`.ConvexRationalPolyhedralCone` representing the Schur + cone living in ``lattice``, with ambient dimension ``ambient_dim``. + Each generating ray has the integer ring as its base ring. + + A ``ValueError`` can be raised if the inputs are incompatible or + insufficient. See the INPUT documentation for details. + + REFERENCES: + + - [GS2010]_, Section 3.1 + + - [IS2005]_, Example 7.3 + + - [SS2016]_, Example 7.4 + + EXAMPLES: + + Verify the claim [SS2016]_ that the maximal angle between any two + generators of the Schur cone and the nonnegative orthant in + dimension five is `\left(3/4\right)\pi`:: + + sage: P = cones.schur(5) + sage: Q = cones.nonnegative_orthant(5) + sage: G = ( g.change_ring(QQbar).normalized() for g in P ) + sage: H = ( h.change_ring(QQbar).normalized() for h in Q ) + sage: actual = max(arccos(u.inner_product(v)) for u in G for v in H) + sage: expected = 3*pi/4 + sage: abs(actual - expected).n() < 1e-12 + True + + The dual of the Schur cone is the "downward monotonic cone" + [GS2010]_, whose elements' entries are in non-increasing order:: + + sage: set_random_seed() + sage: ambient_dim = ZZ.random_element(10) + sage: K = cones.schur(ambient_dim).dual() + sage: x = K.random_element() + sage: all( x[i] >= x[i+1] for i in range(ambient_dim-1) ) + True + + TESTS: + + We get the trivial cone when ``ambient_dim`` is zero:: + + sage: cones.schur(0).is_trivial() + True + + The Schur cone induces the majorization ordering, as in Iusem + and Seeger's [IS2005]_ Example 7.3:: + + sage: set_random_seed() + sage: def majorized_by(x,y): + ....: return (all(sum(x[0:i]) <= sum(y[0:i]) + ....: for i in range(x.degree()-1)) + ....: and sum(x) == sum(y)) + sage: ambient_dim = ZZ.random_element(10) + sage: V = VectorSpace(QQ, ambient_dim) + sage: S = cones.schur(ambient_dim) + sage: majorized_by(V.zero(), S.random_element()) + True + sage: x = V.random_element() + sage: y = V.random_element() + sage: majorized_by(x,y) == ( (y-x) in S ) + True + + If a ``lattice`` was given, it is actually used:: + + sage: L = ToricLattice(3, 'M') + sage: cones.schur(3, lattice=L) + 2-d cone in 3-d lattice M + + Unless the rank of the lattice disagrees with ``ambient_dim``:: + + sage: L = ToricLattice(1, 'M') + sage: cones.schur(3, lattice=L) + Traceback (most recent call last): + ... + ValueError: lattice rank=1 and ambient_dim=3 are incompatible + + We also get an error if no arguments are given:: + + sage: cones.schur() + Traceback (most recent call last): + ... + ValueError: either the ambient dimension or the lattice must + be specified + """ + from sage.geometry.cone import Cone + from sage.matrix.constructor import matrix + from sage.rings.all import ZZ + + (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice) + + def _f(i,j): + if i == j: + return 1 + elif j - i == 1: + return -1 + else: + return 0 + + # The "max" below catches the trivial case where ambient_dim == 0. + S = matrix(ZZ, max(0, ambient_dim-1), ambient_dim, _f) + + return Cone(S.rows(), lattice) + + +def trivial(ambient_dim=None, lattice=None): + r""" + The trivial cone with no nonzero generators in ``ambient_dim`` + dimensions, or living in ``lattice``. + + INPUT: + + - ``ambient_dim`` -- a nonnegative integer (default: ``None``); the + dimension of the ambient space + + - ``lattice`` -- a toric lattice (default: ``None``); the lattice in + which the cone will live + + If ``ambient_dim`` is omitted, then it will be inferred from the + rank of ``lattice``. If the ``lattice`` is omitted, then the + default lattice of rank ``ambient_dim`` will be used. + + A ``ValueError`` is raised if neither ``ambient_dim`` nor + ``lattice`` are specified. It is also a ``ValueError`` to specify + both ``ambient_dim`` and ``lattice`` unless the rank of + ``lattice`` is equal to ``ambient_dim``. + + OUTPUT: + + A :class:`.ConvexRationalPolyhedralCone` representing the + trivial cone with no nonzero generators living in ``lattice``, + with ambient dimension ``ambient_dim``. + + A ``ValueError`` can be raised if the inputs are incompatible or + insufficient. See the INPUT documentation for details. + + EXAMPLES: + + Construct the trivial cone, containing only the origin, in three + dimensions:: + + sage: cones.trivial(3) + 0-d cone in 3-d lattice N + + If a ``lattice`` is given, the trivial cone will live in that + lattice:: + + sage: L = ToricLattice(3, 'M') + sage: cones.trivial(3, lattice=L) + 0-d cone in 3-d lattice M + + TESTS: + + We can construct the trivial cone in a trivial ambient space:: + + sage: cones.trivial(0) + 0-d cone in 0-d lattice N + + An error is raised if the rank of the lattice disagrees with + ``ambient_dim``:: + + sage: L = ToricLattice(1, 'M') + sage: cones.trivial(3, lattice=L) + Traceback (most recent call last): + ... + ValueError: lattice rank=1 and ambient_dim=3 are incompatible + + We also get an error if no arguments are given:: + + sage: cones.trivial() + Traceback (most recent call last): + ... + ValueError: either the ambient dimension or the lattice must + be specified + """ + from sage.geometry.cone import Cone + + (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice) + + return Cone([], lattice) diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py b/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py index 37b660fa834..66edc59bc82 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py @@ -27,7 +27,7 @@ from sage.rings.infinity import infinity from sage.functions.other import real, imag, sqrt from sage.misc.lazy_import import lazy_import -lazy_import('sage.misc.misc', 'attrcall') +lazy_import('sage.misc.call', 'attrcall') class HyperbolicModelCoercion(Morphism): """ diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_point.py b/src/sage/geometry/hyperbolic_space/hyperbolic_point.py index dc16085fe99..b697c5e05f6 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_point.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_point.py @@ -589,7 +589,7 @@ def show(self, boundary=True, **options): sage: HyperbolicPlane().UHP().get_point(infinity).show() Traceback (most recent call last): ... - NotImplementedError: can't draw the point infinity + NotImplementedError: can...t draw the point infinity """ p = self.coordinates() if p == infinity: diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 740a7a56b4e..b46759c0d04 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -64,6 +64,19 @@ sage: a = H([(1,2,3), 4], [(5,6,7), 8]); a Arrangement +Number fields are also possible:: + + sage: x = var('x') + sage: NF. = NumberField(x**4 - 5*x**2 + 5,embedding=1.90) + sage: H. = HyperplaneArrangements(NF) + sage: A = H([[(-a**3 + 3*a, -a**2 + 4), 1], [(a**3 - 4*a, -1), 1], + ....: [(0, 2*a**2 - 6), 1], [(-a**3 + 4*a, -1), 1], + ....: [(a**3 - 3*a, -a**2 + 4), 1]]) + sage: A + Arrangement of 5 hyperplanes of dimension 2 and rank 2 + sage: A.base_ring() + Number Field in a with defining polynomial x^4 - 5*x^2 + 5 with a = 1.902113032590308? + Notation (iii): a list or tuple of hyperplanes:: sage: H. = HyperplaneArrangements(GF(5)) @@ -116,6 +129,9 @@ sage: b == hyperplane_arrangements.coordinate(3) True +Properties of Arrangements +-------------------------- + A hyperplane arrangement is *essential* if the normals to its hyperplanes span the ambient space. Otherwise, it is *inessential*. The essentialization is formed by intersecting the hyperplanes by this @@ -352,7 +368,7 @@ class HyperplaneArrangementElement(Element): :class:`HyperplaneArrangementElement` instances directly, always use the parent. """ - def __init__(self, parent, hyperplanes, check=True): + def __init__(self, parent, hyperplanes, check=True, backend=None): """ Construct a hyperplane arrangement. @@ -365,15 +381,30 @@ def __init__(self, parent, hyperplanes, check=True): - ``check`` -- boolean (optional; default ``True``); whether to check input + - ``backend`` -- string (optional; default: ``None``); the backend to + use for the related polyhedral objects + EXAMPLES:: sage: H. = HyperplaneArrangements(QQ) sage: elt = H(x, y); elt Arrangement sage: TestSuite(elt).run() + + It is possible to specify a backend for polyhedral computations:: + + sage: R. = QuadraticField(5) + sage: H = HyperplaneArrangements(R, names='xyz') + sage: x,y,z = H.gens() + sage: A = H(sqrt5*x+2*y+3*z, backend='normaliz') + sage: A.backend() + 'normaliz' + sage: A.regions()[0].backend() # optional - pynormaliz + 'normaliz' """ super(HyperplaneArrangementElement, self).__init__(parent) self._hyperplanes = hyperplanes + self._backend = backend if check: if not isinstance(hyperplanes, tuple): raise ValueError("the hyperplanes must be given as a tuple") @@ -546,6 +577,33 @@ def rank(self): normals = [h.normal() for h in self] return matrix(R, normals).rank() + def backend(self): + """ + Return the backend used for polyhedral objects + + OUTPUT: + + A string giving the backend or ``None`` if none is specified. + + EXAMPLES: + + By default, no backend is specified:: + + sage: H = HyperplaneArrangements(QQ) + sage: A = H() + sage: A.backend() + + Otherwise, one may specify a polyhedral backend:: + + sage: A = H(backend='ppl') + sage: A.backend() + 'ppl' + sage: A = H(backend='normaliz') + sage: A.backend() + 'normaliz' + """ + return self._backend + def _richcmp_(self, other, op): """ Compare two hyperplane arrangements. @@ -602,7 +660,7 @@ def union(self, other): P = self.parent() other = P(other) hyperplanes = self._hyperplanes + other._hyperplanes - return P(*hyperplanes) + return P(*hyperplanes, backend=self._backend) add_hyperplane = union @@ -680,7 +738,7 @@ def cone(self, variable='t'): P = self.parent() names = (variable,) + P._names H = HyperplaneArrangements(self.parent().base_ring(), names=names) - return H(*hyperplanes) + return H(*hyperplanes, backend=self._backend) @cached_method def intersection_poset(self): @@ -848,6 +906,15 @@ def deletion(self, hyperplanes): Traceback (most recent call last): ... ValueError: hyperplane is not in the arrangement + + Checks that deletion preserves the backend:: + + sage: H = HyperplaneArrangements(QQ,names='xyz') + sage: x,y,z = H.gens() + sage: h1,h2 = [1*x+2*y+3*z,3*x+2*y+1*z] + sage: A = H(h1,h2,backend='normaliz') + sage: A.deletion(h2).backend() + 'normaliz' """ parent = self.parent() hyperplanes = parent(hyperplanes) @@ -857,7 +924,7 @@ def deletion(self, hyperplanes): planes.remove(hyperplane) except ValueError: raise ValueError('hyperplane is not in the arrangement') - return parent(*planes) + return parent(*planes, backend=self._backend) def restriction(self, hyperplane): r""" @@ -893,6 +960,17 @@ def restriction(self, hyperplane): .. SEEALSO:: :meth:`deletion` + + TESTS: + + Checks that restriction preserves the backend:: + + sage: H = HyperplaneArrangements(QQ,names='xyz') + sage: x,y,z = H.gens() + sage: h1,h2 = [1*x+2*y+3*z,3*x+2*y+1*z] + sage: A = H(h1, h2, backend='normaliz') + sage: A.restriction(h2).backend() + 'normaliz' """ parent = self.parent() hyperplane = parent(hyperplane)[0] @@ -913,7 +991,7 @@ def restriction(self, hyperplane): names = list(parent._names) names.pop(pivot) H = HyperplaneArrangements(parent.base_ring(), names=tuple(names)) - return H(*hyperplanes, signed=False) + return H(*hyperplanes, signed=False, backend=self._backend) def change_ring(self, base_ring): """ @@ -945,9 +1023,20 @@ def change_ring(self, base_ring): sage: A = H([(1,1), 0], [(2,3), -1]) sage: A.change_ring(FiniteField(2)) Arrangement + + TESTS: + + Checks that changing the ring preserves the backend:: + + sage: H = HyperplaneArrangements(QQ,names='xyz') + sage: x,y,z = H.gens() + sage: h1,h2 = [1*x+2*y+3*z,3*x+2*y+1*z] + sage: A = H(h1, h2, backend='normaliz') + sage: A.change_ring(RDF).backend() + 'normaliz' """ parent = self.parent().change_ring(base_ring) - return parent(self) + return parent(self, backend=self._backend) @cached_method def n_regions(self): @@ -1151,7 +1240,7 @@ def is_central(self, certificate=False): if self.n_hyperplanes() == 0: if certificate: from sage.geometry.polyhedron.parent import Polyhedra - pp = Polyhedra(R, self.dimension()) + pp = Polyhedra(R, self.dimension(), backend=self._backend) return (True, pp.universe()) else: return True @@ -1165,7 +1254,7 @@ def is_central(self, certificate=False): # The solution set is empty, therefore the center is empty if certificate: from sage.geometry.polyhedron.parent import Polyhedra - pp = Polyhedra(R, self.dimension()) + pp = Polyhedra(R, self.dimension(), backend=self._backend) return (False, pp.empty()) else: return False @@ -1173,7 +1262,9 @@ def is_central(self, certificate=False): if certificate: Ker = m.right_kernel() from sage.geometry.polyhedron.constructor import Polyhedron - return (True, Polyhedron(base_ring=R, vertices=[x], lines=Ker.basis())) + return (True, Polyhedron(base_ring=R, vertices=[x], + lines=Ker.basis(), + backend=self._backend)) else: return True @@ -1218,13 +1309,13 @@ def center(self): def is_simplicial(self): r""" Test whether the arrangement is simplicial. - + A region is simplicial if the normal vectors of its bounding hyperplanes are linearly independent. A hyperplane arrangement is said to be simplicial if every region is simplicial. OUTPUT: - + A boolean whether the hyperplane arrangement is simplicial. EXAMPLES:: @@ -1250,7 +1341,6 @@ def is_simplicial(self): rank = self.rank() return all(R.n_facets() == rank for R in self.regions()) - @cached_method def essentialization(self): r""" @@ -1345,7 +1435,7 @@ def echelon_col_iter(row_iter): names = tuple(name for i, name in enumerate(parent._names) if i not in echelon_pivots) # Construct the result restricted_parent = HyperplaneArrangements(R, names=names) - return restricted_parent(*restricted, signed=False) + return restricted_parent(*restricted, signed=False, backend=self._backend) def sign_vector(self, p): r""" @@ -1558,11 +1648,20 @@ def _make_region(self, hyperplanes): sage: h = H(x) sage: h._make_region([x, 1-x, y, 1-y]) A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices + + TESTS: + + Checks that it creates the regions with the appropriate backend:: + + sage: h = H(x,backend='normaliz') # optional - pynormaliz + sage: h._make_region([x, 1-x, y, 1-y]).backend() # optional - pynormaliz + 'normaliz' """ ieqs = [h.dense_coefficient_list() for h in hyperplanes] from sage.geometry.polyhedron.constructor import Polyhedron return Polyhedron(ieqs=ieqs, ambient_dim=self.dimension(), - base_ring=self.parent().base_ring()) + base_ring=self.parent().base_ring(), + backend=self._backend) @cached_method def regions(self): @@ -1601,26 +1700,128 @@ def regions(self): sage: chessboard = H(chessboard) sage: len(chessboard.bounded_regions()) # long time, 359 ms on a Core i7 64 + + Example 6 of [KP2020]_:: + + sage: from itertools import product + sage: def zero_one(d): + ....: for x in product([0,1], repeat=d): + ....: if any(y for y in x): + ....: yield [0] + list(x) + ....: + sage: K. = HyperplaneArrangements(QQ) + sage: A = K(*zero_one(2)) + sage: len(A.regions()) + 6 + sage: K. = HyperplaneArrangements(QQ) + sage: A = K(*zero_one(3)) + sage: len(A.regions()) + 32 + sage: K. = HyperplaneArrangements(QQ) + sage: A = K(*zero_one(4)) + sage: len(A.regions()) + 370 + sage: K. = HyperplaneArrangements(QQ) + sage: A = K(*zero_one(5)) + sage: len(A.regions()) # not tested (~25s) + 11292 + + It is possible to specify the backend:: + + sage: K. = CyclotomicField(9) + sage: L. = NumberField((q+q**(-1)).minpoly(),embedding = AA(q+q**-1)) + sage: norms = [[1,1/3*(-2*r9**2-r9+1),0], + ....: [1,-r9**2-r9,0], + ....: [1,-r9**2+1,0], + ....: [1,-r9**2,0], + ....: [1,r9**2-4,-r9**2+3]] + sage: H. = HyperplaneArrangements(L) + sage: A = H(backend='normaliz') + sage: for v in norms: + ....: a,b,c = v + ....: A = A.add_hyperplane(a*x + b*y + c*z) + sage: R = A.regions() # optional - pynormaliz + sage: R[0].backend() # optional - pynormaliz + 'normaliz' + + TESTS:: + + sage: K. = HyperplaneArrangements(QQ) + sage: A = K() + sage: A.regions() + (A 5-dimensional polyhedron in QQ^5 defined as the convex hull of 1 vertex and 5 lines,) """ if self.base_ring().characteristic() != 0: raise ValueError('base field must have characteristic zero') from sage.geometry.polyhedron.constructor import Polyhedron R = self.base_ring() dim = self.dimension() - universe = Polyhedron(eqns=[[0] + [0] * dim], base_ring=R) + be = self._backend + universe = Polyhedron(eqns=[[0] + [0] * dim], + base_ring=R, + backend=be) regions = [universe] + if self.is_linear() and self.n_hyperplanes(): + # We only take the positive half w.r. to the first hyperplane. + # We fix this by appending all negative regions in the end. + regions = None + for hyperplane in self: ieq = vector(R, hyperplane.dense_coefficient_list()) - pos_half = Polyhedron(ieqs=[ ieq], base_ring=R) - neg_half = Polyhedron(ieqs=[-ieq], base_ring=R) + pos_half = Polyhedron(ieqs=[ ieq], base_ring=R, backend=be) + neg_half = Polyhedron(ieqs=[-ieq], base_ring=R, backend=be) + if not regions: + # See comment above. + regions = [pos_half] + continue subdivided = [] for region in regions: - for half_space in pos_half, neg_half: - part = region.intersection(half_space) - if part.dim() == dim: - subdivided.append(part) + # For each region we determine, if the hyperplane splits it. + splits = False + + # Determine if all vertices lie on one side of the hyperplane. + # If so, we determine on which side. + valuations = tuple(ieq[0] + ieq[1:]*v[:] for v in region.vertices()) + direction = 0 + if any(x > 0 for x in valuations): + direction = 1 + if any(x < 0 for x in valuations): + if direction: + splits = True + else: + direction = -1 + + if not splits: + # All vertices lie in one closed halfspace of the hyperplane. + region_lines = region.lines() + if direction == 0: + # In this case all vertices lie on the hyperplane and we must + # check if rays are contained in one closed halfspace given by the hyperplane. + valuations = tuple(ieq[1:]*ray[:] for ray in region.rays()) + if region_lines: + valuations += tuple(ieq[1:]*line[:] for line in region_lines) + valuations += tuple(-ieq[1:]*line[:] for line in region_lines) + if any(x > 0 for x in valuations) and any(x < 0 for x in valuations): + splits = True + else: + # In this case, at least one of the vertices is not on the hyperplane. + # So we check if any ray or line pokes the hyperplane. + if ( any(ieq[1:]*r[:]*direction < 0 for r in region.rays()) or + any(ieq[1:]*l[:] != 0 for l in region_lines)): + splits = True + + if splits: + subdivided.append(region.intersection(pos_half)) + subdivided.append(region.intersection(neg_half)) + else: + subdivided.append(region) regions = subdivided - return tuple(regions) + + if self.is_linear() and self.n_hyperplanes(): + # We have treated so far only the positive half space w.r. to the first hyperplane. + return tuple(regions) + tuple(-x for x in regions) + else: + return tuple(regions) @cached_method def poset_of_regions(self, B=None, numbered_labels=True): @@ -1667,7 +1868,6 @@ def poset_of_regions(self, B=None, numbered_labels=True): sage: base_region = R[3] sage: A.poset_of_regions(B=base_region) Finite poset containing 14 elements - """ # We use RX to keep track of indexes and R to keep track of which regions # we've already hit. This poset is graded, so we can go one set at a time @@ -1693,20 +1893,19 @@ def poset_of_regions(self, B=None, numbered_labels=True): for r in R: # Since it's graded, it suffices to look at the regions of the previous rank for b in curTest: - if self.distance_between_regions(b,r) == 1: + if self.distance_between_regions(b, r) == 1: nextTest.add(r) if numbered_labels: - edges.append([RX.index(b),RX.index(r)]) + edges.append([RX.index(b), RX.index(r)]) else: - edges.append([b,r]) + edges.append([b, r]) for x in nextTest: R.discard(x) if numbered_labels: - return Poset([range(len(RX)),edges]) + return Poset([range(len(RX)), edges]) else: - return Poset([RX,edges]) - + return Poset([RX, edges]) @cached_method def closed_faces(self, labelled=True): @@ -1906,7 +2105,8 @@ def closed_faces(self, labelled=True): from sage.geometry.polyhedron.constructor import Polyhedron dim = self.dimension() hypes = self.hyperplanes() - universe = Polyhedron(eqns=[[0] + [0] * dim], base_ring=R) + be = self._backend + universe = Polyhedron(eqns=[[0] + [0] * dim], base_ring=R, backend=be) faces = [((), universe)] for k, hyperplane in enumerate(hypes): # Loop invariant: @@ -1914,11 +2114,11 @@ def closed_faces(self, labelled=True): # hyperplane arrangement given by the first ``k`` hyperplanes # in the list ``hypes`` (that is, by ``hypes[:k]``). ieq = vector(R, hyperplane.dense_coefficient_list()) - zero_half = Polyhedron(eqns=[ieq], base_ring=R) + zero_half = Polyhedron(eqns=[ieq], base_ring=R, backend=be) # ``zero_half`` is the hyperplane ``hyperplane`` itself # (viewed as a polyhedron). - pos_half = Polyhedron(ieqs=[ ieq], base_ring=R) - neg_half = Polyhedron(ieqs=[-ieq], base_ring=R) + pos_half = Polyhedron(ieqs=[ ieq], base_ring=R, backend=be) + neg_half = Polyhedron(ieqs=[-ieq], base_ring=R, backend=be) subdivided = [] for signs, face in faces: # So ``face`` is a face of the hyperplane arrangement @@ -2077,7 +2277,7 @@ def face_product(self, F, G, normalize=True): ieqs.append(-ieq) else: ieqs.append(ieq) - face = Polyhedron(eqns=eqns, ieqs=ieqs, base_ring=R) + face = Polyhedron(eqns=eqns, ieqs=ieqs, base_ring=R, backend=self._backend) if not normalize: return face # Look for ``I`` in ``self.closed_faces()``: @@ -2272,7 +2472,8 @@ def _bounded_region_indices(self): """ from sage.geometry.polyhedron.constructor import Polyhedron normal = Polyhedron(vertices=[[0]*self.dimension()], - lines=[hyperplane.normal() for hyperplane in self]) + lines=[hyperplane.normal() for hyperplane in self], + backend=self._backend) if normal.dim() == 0: transverse = lambda poly: poly else: @@ -2592,7 +2793,7 @@ def distance_enumerator(self, base_region): sage: c.distance_enumerator(c.region_containing_point([1,1,1])) x^3 + 3*x^2 + 3*x + 1 """ - d = [self.distance_between_regions(r,base_region) for r in self.regions()] + d = [self.distance_between_regions(r, base_region) for r in self.regions()] d = [d.count(i) for i in range(max(d)+1)] from sage.rings.polynomial.polynomial_ring import polygen x = polygen(QQ, 'x') @@ -2640,7 +2841,7 @@ def varchenko_matrix(self, names='h'): for j in range(i+1, k): t = prod(h[p] for p in range(k) if self.is_separating_hyperplane(region[i], region[j], self[p])) - v[i,j] = v[j,i] = t + v[i, j] = v[j, i] = t v.set_immutable() return v @@ -2783,7 +2984,7 @@ def minimal_generated_number(self): d = list(d) dep = V.linear_dependence([norms[j] for j in d]) w = W.zero().list() - for j,k in enumerate(d): + for j, k in enumerate(d): w[k] = dep[0][j] sol.append(w) mat = matrix(sol) @@ -2879,7 +3080,7 @@ def derivation_module_free_chain(self): from sage.geometry.hyperplane_arrangement.check_freeness import construct_free_chain return construct_free_chain(self) - @cached_method(key=lambda self,a: None) + @cached_method(key=lambda self, a: None) def is_free(self, algorithm="singular"): r""" Return if ``self`` is free. @@ -3014,14 +3215,14 @@ def derivation_module_basis(self, algorithm="singular"): """ alg = algorithm # prevent possible changes to a global variable if alg == "singular": - #import sage.libs.singular.function_factory - #syz = sage.libs.singular.function_factory.ff.syz + # import sage.libs.singular.function_factory + # syz = sage.libs.singular.function_factory.ff.syz f = self.defining_polynomial() I = f + f.jacobian_ideal() IS = I._singular_() ISS = IS.syz() MSTD = ISS.mstd() - basis = MSTD[2]._sage_().transpose().submatrix(0,1) + basis = MSTD[2]._sage_().transpose().submatrix(0, 1) try: det = basis.det() # Check using Saito's criterion @@ -3047,6 +3248,7 @@ def derivation_module_basis(self, algorithm="singular"): else: raise ValueError("invalid algorithm") + class HyperplaneArrangements(Parent, UniqueRepresentation): """ Hyperplane arrangements. @@ -3094,8 +3296,9 @@ def __init__(self, base_ring, names=tuple()): sage: K = HyperplaneArrangements(QQ) sage: TestSuite(K).run() """ - from sage.categories.all import Fields, Sets - if not base_ring in Fields: + from sage.categories.all import Sets + from sage.rings.ring import _Fields + if base_ring not in _Fields: raise ValueError('base ring must be a field') super(HyperplaneArrangements, self).__init__(category=Sets()) self._base_ring = base_ring @@ -3249,6 +3452,7 @@ def _element_constructor_(self, *args, **kwds): signed = kwds.pop('signed', not_char2) warn_duplicates = kwds.pop('warn_duplicates', False) check = kwds.pop('check', True) + backend = kwds.pop('backend', None) if len(kwds) > 0: raise ValueError('unknown keyword argument') # process positional arguments @@ -3278,7 +3482,7 @@ def _element_constructor_(self, *args, **kwds): raise ValueError('linear expression must be non-constant to define a hyperplane') if not_char2 and -h in hyperplanes: raise ValueError('arrangement cannot simultaneously have h and -h as hyperplane') - return self.element_class(self, tuple(sorted(hyperplanes))) + return self.element_class(self, tuple(sorted(hyperplanes)), backend=backend) @cached_method def ngens(self): @@ -3364,4 +3568,3 @@ def _coerce_map_from_(self, P): if isinstance(P, HyperplaneArrangements): return self.base_ring().has_coerce_map_from(P.base_ring()) return super(HyperplaneArrangements, self)._coerce_map_from_(P) - diff --git a/src/sage/geometry/integral_points.pyx b/src/sage/geometry/integral_points.pyx index d11ddb67523..765b0a5bb55 100644 --- a/src/sage/geometry/integral_points.pyx +++ b/src/sage/geometry/integral_points.pyx @@ -507,19 +507,19 @@ cpdef rectangular_box_points(list box_min, list box_max, sage: cube = polytopes.cube() sage: cube.Hrepresentation(0) - An inequality (0, 0, -1) x + 1 >= 0 + An inequality (-1, 0, 0) x + 1 >= 0 sage: cube.Hrepresentation(1) An inequality (0, -1, 0) x + 1 >= 0 sage: cube.Hrepresentation(2) - An inequality (-1, 0, 0) x + 1 >= 0 + An inequality (0, 0, -1) x + 1 >= 0 sage: rectangular_box_points([0]*3, [1]*3, cube, return_saturated=True) (((0, 0, 0), frozenset()), - ((0, 0, 1), frozenset({0})), + ((0, 0, 1), frozenset({2})), ((0, 1, 0), frozenset({1})), - ((0, 1, 1), frozenset({0, 1})), - ((1, 0, 0), frozenset({2})), + ((0, 1, 1), frozenset({1, 2})), + ((1, 0, 0), frozenset({0})), ((1, 0, 1), frozenset({0, 2})), - ((1, 1, 0), frozenset({1, 2})), + ((1, 1, 0), frozenset({0, 1})), ((1, 1, 1), frozenset({0, 1, 2}))) TESTS: diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index f89b192675c..1b7310e4286 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -102,10 +102,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range -from six import StringIO -from six.moves import copyreg -import six from sage.arith.all import gcd from sage.combinat.posets.posets import FinitePoset @@ -135,14 +131,12 @@ from copy import copy import collections +import copyreg import os import subprocess import warnings from functools import reduce -from io import IOBase - -if not six.PY2: - file = IOBase +from io import IOBase, StringIO class SetOfAllLatticePolytopesClass(Set_generic): @@ -300,7 +294,7 @@ def LatticePolytope(data, compute_vertices=True, n=0, lattice=None): skip_palp_matrix(f, n) data = read_palp_point_collection(data) f.close() - if isinstance(data, (file, IOBase, StringIO)): + if isinstance(data, (IOBase, StringIO)): data = read_palp_point_collection(data) if not is_PointCollection(data) and not isinstance(data, (list, tuple)): try: @@ -2395,6 +2389,11 @@ def incidence_matrix(self): sage: o.incidence_matrix().is_immutable() True + + Check that the base ring is ``ZZ``, see :trac:`29840`:: + + sage: o.incidence_matrix().base_ring() + Integer Ring """ incidence_matrix = matrix(ZZ, self.nvertices(), self.nfacets(), 0) @@ -4987,6 +4986,7 @@ def _palp(command, polytopes, reduce_dimension=False): sage: p = LatticePolytope([(1,0,0), (0,1,0), (-1,0,0), (0,-1,0)]) sage: lattice_polytope._palp("poly.x -f", [p]) Traceback (most recent call last): + ... ValueError: Cannot run PALP for a 2-dimensional polytope in a 3-dimensional space! sage: result_name = lattice_polytope._palp("poly.x -f", [p], reduce_dimension=True) diff --git a/src/sage/geometry/linear_expression.py b/src/sage/geometry/linear_expression.py index cc49fd19b70..142f7a0321e 100644 --- a/src/sage/geometry/linear_expression.py +++ b/src/sage/geometry/linear_expression.py @@ -36,7 +36,6 @@ sage: m-m 0*x + 0*y + 0*z + 0 """ -from six.moves import zip from sage.structure.parent import Parent from sage.structure.richcmp import richcmp diff --git a/src/sage/geometry/point_collection.pyx b/src/sage/geometry/point_collection.pyx index f04fe59d042..102929a15bb 100644 --- a/src/sage/geometry/point_collection.pyx +++ b/src/sage/geometry/point_collection.pyx @@ -912,7 +912,7 @@ cdef class PointCollection(SageObject): EXAMPLES:: sage: o = lattice_polytope.cross_polytope(3) - sage: from six import StringIO + sage: from io import StringIO sage: f = StringIO() sage: o.vertices().write_for_palp(f) sage: print(f.getvalue()) @@ -975,7 +975,7 @@ def read_palp_point_collection(f, lattice=None, permutation=False): 2 3 transposed 1 2 3 4 5 6 - sage: from six import StringIO + sage: from io import StringIO sage: f = StringIO(data) sage: from sage.geometry.point_collection \ ....: import read_palp_point_collection diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index 02b20ff2ec7..2bf3e1b437c 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -15,7 +15,6 @@ # **************************************************************************** from __future__ import print_function, absolute_import -from six import PY2 from subprocess import Popen, PIPE from sage.rings.all import ZZ @@ -109,8 +108,18 @@ def _init_from_Hrepresentation(self, ieqs, eqns, verbose=False): ....: backend='cdd', base_ring=QQ) # indirect doctest A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + + TESTS: + + The polyhedron with zero inequalities can be initialized from Hrepresentation; + see :trac:`29899`:: + + sage: Polyhedron(ieqs=[], ambient_dim=5, backend='cdd') + A 5-dimensional polyhedron in QQ^5 defined as the convex hull of 1 vertex and 5 lines """ from .cdd_file_format import cdd_Hrepresentation + # We have to add a trivial inequality, in case the polyhedron is the universe. + ieqs = tuple(ieqs) + ((1,) + tuple(0 for _ in range(self.ambient_dim())),) s = cdd_Hrepresentation(self._cdd_type, ieqs, eqns) s = self._run_cdd(s, '--redcheck', verbose=verbose) s = self._run_cdd(s, '--repall', verbose=verbose) @@ -147,13 +156,9 @@ def _run_cdd(self, cdd_input_string, cmdline_arg, verbose=False): print('---- CDD input -----') print(cdd_input_string) - if PY2: - enc_kwargs = {} - else: - enc_kwargs = {'encoding': 'latin-1'} - cdd_proc = Popen([self._cdd_executable, cmdline_arg], - stdin=PIPE, stdout=PIPE, stderr=PIPE, **enc_kwargs) + stdin=PIPE, stdout=PIPE, stderr=PIPE, + encoding='latin-1') ans, err = cdd_proc.communicate(input=cdd_input_string) if verbose: @@ -213,6 +218,27 @@ def _init_from_cdd_output(self, cddout): sage: p = Polyhedron(vertices = [[0,0],[1,0],[0,1],[1,1]], backend='cdd', base_ring=QQ) # indirect doctest sage: p.vertices() (A vertex at (0, 0), A vertex at (1, 0), A vertex at (0, 1), A vertex at (1, 1)) + + Check that :trac:`29176` is fixed:: + + sage: e = [[11582947.657000002, 5374.38, 4177.06, 1.0], [11562795.9322, 5373.62, 4168.38, 1.0]] + sage: p = Polyhedron(ieqs=e); p + A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 1 vertex, 2 rays, 1 line + sage: p.incidence_matrix() + [1 1] + [1 0] + [0 1] + [1 1] + + sage: P = [[-2687.19, -2088.53], [-2686.81, -2084.19]] + sage: V = VoronoiDiagram(P) + sage: R = V.regions() + sage: V.points()[0], R[V.points()[0]] + (P(-2687.19000000000, -2088.53000000000), + A 2-dimensional polyhedron in RDF^2 defined as the convex hull of 1 vertex, 1 ray, 1 line) + sage: V.points()[1], R[V.points()[1]] + (P(-2686.81000000000, -2084.19000000000), + A 2-dimensional polyhedron in RDF^2 defined as the convex hull of 1 vertex, 1 ray, 1 line) """ cddout = cddout.splitlines() @@ -293,8 +319,11 @@ def parse_V_representation(intro, data): self.parent()._make_Vertex(self, [self.base_ring().zero()] * self.ambient_dim()) self._Vrepresentation = tuple(self._Vrepresentation) - def parse_adjacency(intro, data, N, cdd_indices_to_sage_indices): - ret = matrix(ZZ, N, N, 0) + def parse_adjacency(intro, data, M, N, cdd_indices_to_sage_indices, cdd_indices_to_sage_indices2=None): + # This function is also used to parse the incidence matrix. + if cdd_indices_to_sage_indices2 is None: + cdd_indices_to_sage_indices2 = cdd_indices_to_sage_indices + ret = matrix(ZZ, M, N, 0) data.pop(0) data.reverse() for adjacencies in data: @@ -311,7 +340,7 @@ def parse_adjacency(intro, data, N, cdd_indices_to_sage_indices): v = cdd_indices_to_sage_indices[cdd_vertex] if v is None: continue - for w in parse_indices(count, adjacencies[3:], cdd_indices_to_sage_indices): + for w in parse_indices(count, adjacencies[3:], cdd_indices_to_sage_indices2): if w is None: continue ret[v, w] = 1 @@ -321,7 +350,7 @@ def parse_vertex_adjacency(intro, data): if '_V_adjacency_matrix' in self.__dict__: raise NotImplementedError("can not replace internal representation as this breaks caching") N = len(self._Vrepresentation) - self._V_adjacency_matrix = parse_adjacency(intro, data, N, self._cdd_V_to_sage_V) + self._V_adjacency_matrix = parse_adjacency(intro, data, N, N, self._cdd_V_to_sage_V) for i, v in enumerate(self._Vrepresentation): # cdd reports that lines are never adjacent to anything. # we disagree, they are adjacent to everything. @@ -337,14 +366,24 @@ def parse_facet_adjacency(intro, data): if '_H_adjacency_matrix' in self.__dict__: raise NotImplementedError("can not replace internal representation as this breaks caching") N = len(self._Hrepresentation) - self._H_adjacency_matrix = parse_adjacency(intro, data, N, self._cdd_H_to_sage_H) + self._H_adjacency_matrix = parse_adjacency(intro, data, N, N, self._cdd_H_to_sage_H) self._H_adjacency_matrix.set_immutable() self.facet_adjacency_matrix.set_cache(self._H_adjacency_matrix) + def parse_incidence_matrix(intro, data): + if 'incidence_matrix' in self.__dict__: + raise NotImplementedError("can not replace internal representation as this breaks caching") + N = len(self._Hrepresentation) + M = len(self._Vrepresentation) + inc_mat = parse_adjacency(intro, data, M, N, self._cdd_V_to_sage_V, self._cdd_H_to_sage_H) + inc_mat.set_immutable() + self.incidence_matrix.set_cache(inc_mat) + Polyhedron_cdd._parse_block(cddout, 'H-representation', parse_H_representation) Polyhedron_cdd._parse_block(cddout, 'V-representation', parse_V_representation) Polyhedron_cdd._parse_block(cddout, 'Facet adjacency', parse_facet_adjacency) Polyhedron_cdd._parse_block(cddout, 'Vertex adjacency', parse_vertex_adjacency) + Polyhedron_cdd._parse_block(cddout, 'Vertex incidence', parse_incidence_matrix) class Polyhedron_QQ_cdd(Polyhedron_cdd, Polyhedron_QQ): @@ -479,3 +518,120 @@ def __init__(self, parent, Vrep, Hrep, **kwds): sage: TestSuite(p).run() """ Polyhedron_cdd.__init__(self, parent, Vrep, Hrep, **kwds) + + def _init_from_Vrepresentation_and_Hrepresentation(self, Vrep, Hrep, verbose=False): + """ + Construct polyhedron from Vrepresentation and Hrepresentation data. + + See :class:`Polyhedron_base` for a description of ``Vrep`` and ``Hrep``. + + .. NOTE:: + + The representation is assumed to be correct. + + As long as cdd can obtain a consistent object with Vrepresentation + or Hrepresentation no warning is raised. Consistency is checked by + comparing the output length of Vrepresentation and Hrepresentation + with the input. + + In comparison, the "normal" initialization from Vrepresentation over RDF + expects the output length to be consistent with the computed length + when re-feeding cdd the outputed Hrepresentation. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.parent import Polyhedra_RDF_cdd + sage: from sage.geometry.polyhedron.backend_cdd import Polyhedron_RDF_cdd + sage: parent = Polyhedra_RDF_cdd(RDF, 1, 'cdd') + sage: Vrep = [[[0.0], [1.0]], [], []] + sage: Hrep = [[[0.0, 1.0], [1.0, -1.0]], []] + sage: p = Polyhedron_RDF_cdd(parent, Vrep, Hrep, + ....: Vrep_minimal=True, Hrep_minimal=True) # indirect doctest + sage: p + A 1-dimensional polyhedron in RDF^1 defined as the convex hull of 2 vertices + + TESTS: + + Test that :trac:`29568` is fixed:: + + sage: P = polytopes.buckyball(exact=False) + sage: Q = P + P.center() + sage: P.is_combinatorially_isomorphic(Q) + True + sage: R = 2*P + sage: P.is_combinatorially_isomorphic(R) + True + + The polyhedron with zero inequalities works correctly; see :trac:`29899`:: + + sage: Vrep = [[], [], [[1.0]]] + sage: Hrep = [[], []] + sage: p = Polyhedron_RDF_cdd(parent, Vrep, Hrep, + ....: Vrep_minimal=True, Hrep_minimal=True) # indirect doctest + sage: p + A 1-dimensional polyhedron in RDF^1 defined as the convex hull of 1 vertex and 1 line + """ + def parse_Vrep(intro, data): + count = int(data[0][0]) + if count != len(vertices) + len(rays) + len(lines): + # Upstream claims that nothing can be done about these + # cases/that they are features not bugs. Imho, cddlib is + # not really suitable for automatic parsing of its output, + # the implementation backed by doubles has not really been + # optimized for numerical stability, and makes some + # somewhat random numerical choices. (But I am not an + # expert in that field by any means.) See also + # https://github.com/cddlib/cddlib/pull/7. + from warnings import warn + warn("This polyhedron data is numerically complicated; cdd could not convert between the inexact V and H representation without loss of data. The resulting object might show inconsistencies.") + + def parse_Hrep(intro, data): + count = int(data[0][0]) + infinite_count = len([d for d in data[1:] if d[0] == '1' and all(c == '0' for c in d[1:])]) + if count - infinite_count != len(ieqs) + len(eqns): + # Upstream claims that nothing can be done about these + # cases/that they are features not bugs. Imho, cddlib is + # not really suitable for automatic parsing of its output, + # the implementation backed by doubles has not really been + # optimized for numerical stability, and makes some + # somewhat random numerical choices. (But I am not an + # expert in that field by any means.) + from warnings import warn + warn("This polyhedron data is numerically complicated; cdd could not convert between the inexact V and H representation without loss of data. The resulting object might show inconsistencies.") + + def try_init(rep): + if rep == "Vrep": + from .cdd_file_format import cdd_Vrepresentation + s = cdd_Vrepresentation(self._cdd_type, vertices, rays, lines) + else: + # We have to add a trivial inequality, in case the polyhedron is the universe. + new_ieqs = ieqs + ((1,) + tuple(0 for _ in range(self.ambient_dim())),) + + from .cdd_file_format import cdd_Hrepresentation + s = cdd_Hrepresentation(self._cdd_type, new_ieqs, eqns) + + s = self._run_cdd(s, '--redcheck', verbose=verbose) + s = self._run_cdd(s, '--repall', verbose=verbose) + Polyhedron_cdd._parse_block(s.splitlines(), 'V-representation', parse_Vrep) + Polyhedron_cdd._parse_block(s.splitlines(), 'H-representation', parse_Hrep) + self._init_from_cdd_output(s) + + from warnings import catch_warnings, simplefilter + + vertices, rays, lines = (tuple(x) for x in Vrep) + ieqs, eqns = (tuple(x) for x in Hrep) + + # We prefer the shorter representation. + # Note that for the empty polyhedron we prefer Hrepresentation. + prim = "Hrep" if len(ieqs) <= len(vertices) + len(rays) else "Vrep" + sec = "Vrep" if len(ieqs) <= len(vertices) + len(rays) else "Hrep" + + with catch_warnings(): + # Raise an error and try the other representation in case of + # numerical inconsistency. + simplefilter("error") + try: + try_init(prim) + except UserWarning: + simplefilter("once") # Only print the first warning. + try_init(sec) diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index 472c8a837c6..95928812574 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -26,7 +26,7 @@ from __future__ import absolute_import, print_function from sage.structure.element import Element -from sage.misc.all import prod +from sage.misc.all import cached_method, prod from sage.features import PythonModule from sage.rings.all import ZZ, QQ @@ -332,12 +332,20 @@ def _convert_to_pynormaliz(x): [[7, 2], [1, 1]] sage: Pn._convert_to_pynormaliz([[1, 2], (3, 4)]) [[1, 2], [3, 4]] + + Check that :trac:`29836` is fixed:: + + sage: P = polytopes.simplex(backend='normaliz') # optional - pynormaliz + sage: K. = QuadraticField(2) # optional - pynormaliz + sage: P.dilation(sqrt2) # optional - pynormaliz + A 3-dimensional polyhedron in (Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.41...)^4 defined as the convex hull of 4 vertices """ def _QQ_pair(x): x = QQ(x) return [ int(x.numerator()), int(x.denominator())] from sage.rings.rational import Rational - if isinstance(x, list) or isinstance(x, tuple): + from types import GeneratorType + if isinstance(x, (list, tuple, GeneratorType)): return [ Polyhedron_normaliz._convert_to_pynormaliz(y) for y in x ] try: return int(ZZ(x)) @@ -393,8 +401,7 @@ def _cone_from_normaliz_data(self, data, verbose=False): [[-1L, 2L, 0L], [0L, 0L, 1L], [2L, -1L, 0L]] """ if verbose: - import six - if isinstance(verbose, six.string_types): + if isinstance(verbose, str): print("# Wrote equivalent Normaliz input file to {}".format(verbose)) self._normaliz_format(data, file_output=verbose) else: @@ -588,6 +595,18 @@ def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False): sage: p = Polyhedron(ieqs=[(1, a, 0)], backend='normaliz') # optional - pynormaliz sage: p & p == p # optional - pynormaliz True + + Check that :trac:`30248` is fixed, that maps as input works:: + + sage: q = Polyhedron(backend='normaliz', base_ring=AA, # optional - pynormaliz + ....: rays=[(0, 0, 1), (0, 1, -1), (1, 0, -1)]) + sage: make_new_Hrep = lambda h: tuple(x if i == 0 else -1*x for i, x in enumerate(h._vector)) + sage: new_inequalities = map(make_new_Hrep, q.inequality_generator()) # optional - pynormaliz + sage: new_equations = map(make_new_Hrep, q.equation_generator()) # optional - pynormaliz + sage: parent = q.parent() # optional - pynormaliz + sage: new_q = parent.element_class(parent,None,[new_inequalities,new_equations]) # optional - pynormaliz + sage: new_q # optional - pynormaliz + A 3-dimensional polyhedron in AA^3 defined as the convex hull of 1 vertex and 3 rays """ def nmz_ieqs_eqns_NF(ieqs, eqns): @@ -822,7 +841,13 @@ def _compute_nmz_data_lists_and_field(self, data_lists, convert_QQ, convert_NF): ... ValueError: invalid base ring: Number Field in a ... is not real embedded + Checks that :trac:`30248` is fixed:: + sage: q = Polyhedron(backend='normaliz', base_ring=AA, # indirect doctest # optional - pynormaliz + ....: rays=[(0, 0, 1), (0, 1, -1), (1, 0, -1)]); q + A 3-dimensional polyhedron in AA^3 defined as the convex hull of 1 vertex and 3 rays + sage: -q # optional - pynormaliz + A 3-dimensional polyhedron in AA^3 defined as the convex hull of 1 vertex and 3 rays """ from sage.categories.number_fields import NumberFields from sage.rings.all import RDF @@ -831,6 +856,9 @@ def _compute_nmz_data_lists_and_field(self, data_lists, convert_QQ, convert_NF): normaliz_field = QQ nmz_data_lists = convert_QQ(*data_lists) else: + # Allows to re-iterate if K is QQ below when data_lists contain + # iterators: + data_lists = [tuple(_) for _ in data_lists] nmz_data_lists = convert_NF(*data_lists) if self.base_ring() in NumberFields: if not RDF.has_coerce_map_from(self.base_ring()): @@ -1105,6 +1133,7 @@ def format_number(x): return '{}'.format(QQ(x)) except (ValueError, TypeError): return '({})'.format(x.polynomial('a')) + def format_field(key, value): if isinstance(value, list) or isinstance(value, tuple): s = '{} {}\n'.format(key, len(value)) @@ -1115,6 +1144,7 @@ def format_field(key, value): return s else: return '{} {}\n'.format(key, value) + def format_number_field_data(nf_triple): min_poly, gen, emb = nf_triple return 'min_poly ({}) embedding {}'.format(min_poly, emb) @@ -1285,25 +1315,60 @@ def integral_hull(self): return self.parent().element_class._from_normaliz_cone(parent=self.parent(), normaliz_cone=cone) + def _h_star_vector_normaliz(self): + r""" + Return the `h^*`-vector of the lattice polytope. + + INPUT: + + - ``self`` -- A lattice polytope with backend ``'normaliz'``. + + OUTPUT: + + The `h^*`-vector as a list. + + EXAMPLES: + + The `h^*`-vector of a unimodular simplex is 1:: + + sage: s3 = polytopes.simplex(3,backend='normaliz') # optional - pynormaliz + sage: s3._h_star_vector_normaliz() # optional - pynormaliz + [1] + + The `h^*`-vector of the `0/1`-cube is [1,4,1]:: + + sage: cube = polytopes.cube(intervals='zero_one', backend='normaliz') # optional - pynormaliz + sage: cube.h_star_vector() # optional - pynormaliz + [1, 4, 1] + """ + return self.ehrhart_series().numerator().coefficients() + def _volume_normaliz(self, measure='euclidean'): r""" Computes the volume of a polytope using normaliz. 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: @@ -1314,7 +1379,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 @@ -1326,6 +1391,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:: @@ -1333,6 +1406,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 @@ -1343,6 +1437,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""" @@ -1411,6 +1519,7 @@ def _triangulate_normaliz(self): return triangulation + ######################################################################### class Polyhedron_QQ_normaliz(Polyhedron_normaliz, Polyhedron_QQ): r""" @@ -1429,6 +1538,7 @@ class Polyhedron_QQ_normaliz(Polyhedron_normaliz, Polyhedron_QQ): sage: TestSuite(p).run() # optional - pynormaliz """ + @cached_method(do_pickle=True) def ehrhart_series(self, variable='t'): r""" Return the Ehrhart series of a compact rational polyhedron. @@ -1482,6 +1592,14 @@ def ehrhart_series(self, variable='t'): .. SEEALSO:: :meth:`~sage.geometry.polyhedron.backend_normaliz.hilbert_series` + + TESTS: + + Check that the Ehrhart series is pickled:: + + sage: new_poly = loads(dumps(rat_poly)) # optional - pynormaliz + sage: new_poly.ehrhart_series.is_in_cache() # optional - pynormaliz + True """ if self.is_empty(): return 0 @@ -1585,6 +1703,7 @@ def _ehrhart_quasipolynomial_normaliz(self, variable='t'): _ehrhart_polynomial_normaliz = _ehrhart_quasipolynomial_normaliz + @cached_method(do_pickle=True, key=lambda self, g, v: (tuple(g), v)) def hilbert_series(self, grading, variable='t'): r""" Return the Hilbert series of the polyhedron with respect to ``grading``. @@ -1646,6 +1765,14 @@ def hilbert_series(self, grading, variable='t'): .. SEEALSO:: :meth:`~sage.geometry.polyhedron.backend_normaliz.ehrhart_series` + + TESTS: + + Check that the Hilbert series is pickled:: + + sage: new_magic = loads(dumps(magic_square)) # optional - pynormaliz + sage: new_magic.hilbert_series.is_in_cache(grading) # optional - pynormaliz + True """ if self.is_empty(): return 0 diff --git a/src/sage/geometry/polyhedron/backend_ppl.py b/src/sage/geometry/polyhedron/backend_ppl.py index b4dcb8dc70e..a33b401b60f 100644 --- a/src/sage/geometry/polyhedron/backend_ppl.py +++ b/src/sage/geometry/polyhedron/backend_ppl.py @@ -3,7 +3,7 @@ """ from __future__ import absolute_import -from sage.rings.all import ZZ, QQ +from sage.rings.all import ZZ from sage.rings.integer import Integer from sage.arith.functions import LCM_list from sage.misc.functional import denominator diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 614a49ee1d8..56203186e3c 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2,7 +2,7 @@ Base class for polyhedra """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Marshall Hampton # Copyright (C) 2011 Volker Braun # Copyright (C) 2015 Jean-Philippe Labbe @@ -13,28 +13,27 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # https://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** from __future__ import division, print_function, absolute_import import itertools -import six from sage.structure.element import Element, coerce_binop, is_Vector, is_Matrix from sage.structure.richcmp import rich_to_bool, op_NE from sage.cpython.string import bytes_to_str from sage.misc.all import cached_method, prod from sage.misc.randstate import current_randstate +from sage.misc.superseded import deprecated_function_alias from sage.rings.all import QQ, ZZ, AA from sage.rings.real_double import RDF from sage.modules.free_module_element import vector from sage.modules.vector_space_morphism import linear_transformation from sage.matrix.constructor import matrix -from sage.functions.other import sqrt, floor, ceil, binomial +from sage.functions.other import sqrt, floor, ceil 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 @@ -103,6 +102,10 @@ class Polyhedron_base(Element): - ``Hrep_minimal`` (optional) -- see below + - ``pref_rep`` -- string (default: ``None``); + one of``Vrep`` or ``Hrep`` to pick this in case the backend + cannot initialize from complete double description + If both ``Vrep`` and ``Hrep`` are provided, then ``Vrep_minimal`` and ``Hrep_minimal`` must be set to ``True``. @@ -110,9 +113,34 @@ class Polyhedron_base(Element): sage: p = Polyhedron() sage: TestSuite(p).run() + + :: + + sage: p = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)], base_ring=ZZ) + sage: TestSuite(p).run() + + :: + + sage: p=polytopes.flow_polytope(digraphs.DeBruijn(3,2)) + sage: TestSuite(p).run() + + :: + + sage: TestSuite(Polyhedron([[]])).run() + sage: TestSuite(Polyhedron([[0]])).run() + + :: + + sage: P = polytopes.permutahedron(3) * Polyhedron(rays=[[0,0,1],[0,1,1],[1,2,3]]) + sage: TestSuite(P).run() + + :: + + sage: P = polytopes.permutahedron(3)*Polyhedron(rays=[[0,0,1],[0,1,1]], lines=[[1,0,0]]) + sage: TestSuite(P).run() """ - def __init__(self, parent, Vrep, Hrep, Vrep_minimal=None, Hrep_minimal=None, **kwds): + def __init__(self, parent, Vrep, Hrep, Vrep_minimal=None, Hrep_minimal=None, pref_rep=None, **kwds): """ Initializes the polyhedron. @@ -133,6 +161,43 @@ def __init__(self, parent, Vrep, Hrep, Vrep_minimal=None, Hrep_minimal=None, **k Traceback (most recent call last): ... ValueError: if both Vrep and Hrep are provided, they must be minimal... + + Illustration of ``pref_rep``. + Note that ``ppl`` doesn't support precomputed data:: + + sage: from sage.geometry.polyhedron.backend_ppl import Polyhedron_QQ_ppl + sage: from sage.geometry.polyhedron.parent import Polyhedra_QQ_ppl + sage: parent = Polyhedra_QQ_ppl(QQ, 1, 'ppl') + sage: p = Polyhedron_QQ_ppl(parent, Vrep, 'nonsense', + ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrep') + sage: p = Polyhedron_QQ_ppl(parent, 'nonsense', Hrep, + ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Hrep') + sage: p = Polyhedron_QQ_ppl(parent, 'nonsense', Hrep, + ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrepresentation') + Traceback (most recent call last): + ... + ValueError: ``pref_rep`` must be one of ``(None, 'Vrep', 'Hrep')`` + + If the backend supports precomputed data, ``pref_rep`` is ignored:: + + sage: p = Polyhedron_field(parent, Vrep, 'nonsense', # py3 + ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrep') + Traceback (most recent call last): + ... + TypeError: _init_Hrepresentation() takes 3 positional arguments but 9 were given + sage: p = Polyhedron_field(parent, Vrep, 'nonsense', # py2 + ....: Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrep') + Traceback (most recent call last): + ... + TypeError: _init_Hrepresentation() takes exactly 3 arguments (9 given) + + The empty polyhedron is detected when the Vrepresentation is given with generator; + see :trac:`29899`:: + + sage: from sage.geometry.polyhedron.backend_cdd import Polyhedron_QQ_cdd + sage: from sage.geometry.polyhedron.parent import Polyhedra_QQ_cdd + sage: parent = Polyhedra_QQ_cdd(QQ, 0, 'cdd') + sage: p = Polyhedron_QQ_cdd(parent, [iter([]), iter([]), iter([])], None) """ Element.__init__(self, parent=parent) if Vrep is not None and Hrep is not None: @@ -143,13 +208,43 @@ def __init__(self, parent, Vrep, Hrep, Vrep_minimal=None, Hrep_minimal=None, **k self._init_from_Vrepresentation_and_Hrepresentation(Vrep, Hrep) return else: - # Initialize from Hrepresentation if this seems simpler. - Vrep = [tuple(Vrep[0]), tuple(Vrep[1]), Vrep[2]] - Hrep = [tuple(Hrep[0]), Hrep[1]] - if len(Hrep[0]) < len(Vrep[0]) + len(Vrep[1]): + if pref_rep is None: + # Initialize from Hrepresentation if this seems simpler. + Vrep = [tuple(Vrep[0]), tuple(Vrep[1]), Vrep[2]] + Hrep = [tuple(Hrep[0]), Hrep[1]] + if len(Hrep[0]) < len(Vrep[0]) + len(Vrep[1]): + pref_rep = 'Hrep' + else: + pref_rep = 'Vrep' + if pref_rep == 'Vrep': + Hrep = None + elif pref_rep == 'Hrep': Vrep = None + else: + raise ValueError("``pref_rep`` must be one of ``(None, 'Vrep', 'Hrep')``") if Vrep is not None: vertices, rays, lines = Vrep + + # We build tuples out of generators now to detect the empty polyhedron. + + # The damage is limited: + # The backend will have to obtain all elements from the generator anyway. + # The generators are mainly for saving time with initializing from + # Vrepresentation and Hrepresentation. + # If we dispose of one of them (see above), it is wasteful to have generated it. + + # E.g. the dilate will be set up with new Vrepresentation and Hrepresentation + # regardless of the backend along with the argument ``pref_rep``. + # As we only use generators, there is no penalty to this approach + # (and the method ``dilation`` does not have to distinguish by backend). + + if not isinstance(vertices, (tuple, list)): + vertices = tuple(vertices) + if not isinstance(rays, (tuple, list)): + rays = tuple(rays) + if not isinstance(lines, (tuple, list)): + lines = tuple(lines) + if vertices or rays or lines: self._init_from_Vrepresentation(vertices, rays, lines, **kwds) else: @@ -417,6 +512,29 @@ def _delete(self): """ self.parent().recycle(self) + def _test_basic_properties(self, tester=None, **options): + """ + Run some basic tests to see, that some general assertion on polyhedra hold. + + TESTS:: + + sage: polytopes.cross_polytope(3)._test_basic_properties() + """ + if tester is None: + tester = self._tester(**options) + + tester.assertEqual(self.n_vertices() + self.n_rays() + self.n_lines(), self.n_Vrepresentation()) + tester.assertEqual(self.n_inequalities() + self.n_equations(), self.n_Hrepresentation()) + tester.assertEqual(self.dim() + self.n_equations(), self.ambient_dim()) + + tester.assertTrue(all(len(v[::]) == self.ambient_dim() for v in self.Vrep_generator())) + tester.assertTrue(all(len(h[::]) == self.ambient_dim() + 1 for h in self.Hrep_generator())) + + if self.n_vertices() + self.n_rays() < 40: + tester.assertEqual(self, Polyhedron(vertices=self.vertices(), rays=self.rays(), lines=self.lines())) + if self.n_inequalities() < 40: + tester.assertEqual(self, Polyhedron(ieqs=self.inequalities(), eqns=self.equations())) + def base_extend(self, base_ring, backend=None): """ Return a new polyhedron over a larger base ring. @@ -516,9 +634,9 @@ def change_ring(self, base_ring, backend=None): sage: P = Polyhedron([[2/3,0],[6666666666666667/10^16,0]], base_ring=AA); P A 1-dimensional polyhedron in AA^2 defined as the convex hull of 2 vertices - sage: P.change_ring(RDF) + sage: Q = P.change_ring(RDF); Q A 0-dimensional polyhedron in RDF^2 defined as the convex hull of 1 vertex - sage: P == P.change_ring(RDF) + sage: P.n_vertices() == Q.n_vertices() False """ @@ -682,24 +800,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 @@ -890,7 +998,7 @@ def plot(self, ... NotImplementedError: plotting of 5-dimensional polyhedra not implemented - If the polyhedron is not full-dimensional, the :meth:`affine_hull` is used if necessary:: + If the polyhedron is not full-dimensional, the :meth:`affine_hull_projection` is used if necessary:: sage: type(Polyhedron([(0,), (1,)]).plot()) @@ -911,7 +1019,7 @@ def merge_options(*opts): continue elif opt is False: return False - elif isinstance(opt, (six.string_types, list, tuple)): + elif isinstance(opt, (str, list, tuple)): merged['color'] = opt else: merged.update(opt) @@ -936,7 +1044,7 @@ def project(polyhedron): try: plot_method = projection.plot except AttributeError: - projection = project(self.affine_hull()) + projection = project(self.affine_hull_projection()) try: plot_method = projection.plot except AttributeError: @@ -996,22 +1104,32 @@ def _repr_(self): if self.n_vertices() > 0: desc += ' defined as the convex hull of ' desc += repr(self.n_vertices()) - if self.n_vertices() == 1: desc += ' vertex' - else: desc += ' vertices' + if self.n_vertices() == 1: + desc += ' vertex' + else: + desc += ' vertices' if self.n_rays() > 0: - if self.n_lines() > 0: desc += ", " - else: desc += " and " + if self.n_lines() > 0: + desc += ", " + else: + desc += " and " desc += repr(self.n_rays()) - if self.n_rays() == 1: desc += ' ray' - else: desc += ' rays' + if self.n_rays() == 1: + desc += ' ray' + else: + desc += ' rays' if self.n_lines() > 0: - if self.n_rays() > 0: desc += ", " - else: desc += " and " + if self.n_rays() > 0: + desc += ", " + else: + desc += " and " desc += repr(self.n_lines()) - if self.n_lines() == 1: desc += ' line' - else: desc += ' lines' + if self.n_lines() == 1: + desc += ' line' + else: + desc += ' lines' return desc @@ -1079,11 +1197,12 @@ def cdd_Hrepresentation(self): H-representation begin 4 3 rational - 1 1 0 - 1 0 1 1 -1 0 1 0 -1 + 1 1 0 + 1 0 1 end + sage: triangle = Polyhedron(vertices = [[1,0],[0,1],[1,1]],base_ring=AA) sage: triangle.base_ring() @@ -1430,7 +1549,7 @@ def Hrepresentation(self, index=None): sage: p = polytopes.hypercube(3) sage: p.Hrepresentation(0) - An inequality (0, 0, -1) x + 1 >= 0 + An inequality (-1, 0, 0) x + 1 >= 0 sage: p.Hrepresentation(0) == p.Hrepresentation() [0] True """ @@ -1522,7 +1641,7 @@ def Hrepresentation_str(self, separator='\n', latex=False, style='>=', align=Non sage: c = polytopes.cube() sage: c.Hrepresentation_str(separator=', ', style='positive') - '1 >= x2, 1 >= x1, 1 >= x0, x0 + 1 >= 0, x2 + 1 >= 0, x1 + 1 >= 0' + '1 >= x0, 1 >= x1, 1 >= x2, x0 + 1 >= 0, x2 + 1 >= 0, x1 + 1 >= 0' """ pretty_hs = [h.repr_pretty(split=True, latex=latex, style=style, **kwds) for h in self.Hrepresentation()] shift = any(pretty_h[2].startswith('-') for pretty_h in pretty_hs) @@ -1530,7 +1649,7 @@ def Hrepresentation_str(self, separator='\n', latex=False, style='>=', align=Non if align is None: align = separator == "\n" if align: - lengths = [(len(s[0]), len(s[1]), len(s[2])) for s in pretty_hs] + lengths = [(len(s[0]), len(s[1]), len(s[2])) for s in pretty_hs] from operator import itemgetter length_left = max(lengths, key=itemgetter(0))[0] length_middle = max(lengths, key=itemgetter(1))[1] @@ -1574,7 +1693,7 @@ def Hrep_generator(self): sage: p = polytopes.hypercube(3) sage: next(p.Hrep_generator()) - An inequality (0, 0, -1) x + 1 >= 0 + An inequality (-1, 0, 0) x + 1 >= 0 """ for H in self.Hrepresentation(): yield H @@ -1977,6 +2096,87 @@ def vertices_matrix(self, base_ring=None): m.set_immutable() return m + def an_affine_basis(self): + """ + Return vertices that are a basis for the affine + span of the polytope. + + This basis is obtained by considering a maximal chain of faces + in the face lattice and picking for each cover relation + one vertex that is in the difference. Thus this method + is independent of the concrete realization of the polytope. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: P.an_affine_basis() + [A vertex at (-1, -1, -1), + A vertex at (1, -1, -1), + A vertex at (1, -1, 1), + A vertex at (1, 1, -1)] + + sage: P = polytopes.permutahedron(5) + sage: P.an_affine_basis() + [A vertex at (4, 1, 5, 2, 3), + A vertex at (5, 1, 4, 2, 3), + A vertex at (4, 2, 5, 1, 3), + A vertex at (4, 1, 5, 3, 2), + A vertex at (1, 2, 3, 4, 5)] + + The method is not implemented for unbounded polyhedra:: + + sage: p = Polyhedron(vertices=[(0,0)],rays=[(1,0),(0,1)]) + sage: p.an_affine_basis() + Traceback (most recent call last): + ... + NotImplementedError: this function is not implemented for unbounded polyhedra + """ + if not self.is_compact(): + raise NotImplementedError("this function is not implemented for unbounded polyhedra") + + chain = self.a_maximal_chain()[1:] # we exclude the empty face + chain_indices = [face.ambient_V_indices() for face in chain] + basis_indices = [] + + # We use in the following that elements in ``chain_indices`` are sorted lists + # of V-indices. + # Thus for each two faces we can easily find the first vertex that differs. + for dim, face in enumerate(chain_indices): + if dim == 0: + # Append the vertex. + basis_indices.append(face[0]) + continue + + prev_face = chain_indices[dim-1] + for i in range(len(prev_face)): + if prev_face[i] != face[i]: + # We found a vertex that ``face`` has, but its facet does not. + basis_indices.append(face[i]) + break + else: # no break + # ``prev_face`` contains all the same vertices as ``face`` until now. + # But ``face`` is guaranteed to contain one more vertex (at least). + basis_indices.append(face[len(prev_face)]) + + return [self.Vrepresentation()[i] for i in basis_indices] + + def _test_an_affine_basis(self, tester=None, **options): + """ + Run tests on the method :meth:`.an_affine_basis` + + TESTS:: + + sage: polytopes.cross_polytope(3)._test_an_affine_basis() + """ + if tester is None: + tester = self._tester(**options) + if self.is_compact(): + b = self.an_affine_basis() + m = matrix([1] + list(v) for v in b) + tester.assertEqual(m.rank(), self.dim() + 1) + for v in b: + tester.assertIn(v, self.vertices()) + def ray_generator(self): """ Return a generator for the rays of the polyhedron. @@ -2104,10 +2304,13 @@ def bounded_edges(self): """ obj = self.Vrepresentation() for i in range(len(obj)): - if not obj[i].is_vertex(): continue + if not obj[i].is_vertex(): + continue for j in range(i+1, len(obj)): - if not obj[j].is_vertex(): continue - if self.vertex_adjacency_matrix()[i, j] == 0: continue + if not obj[j].is_vertex(): + continue + if self.vertex_adjacency_matrix()[i, j] == 0: + continue yield (obj[i], obj[j]) def Vrepresentation_space(self): @@ -2351,6 +2554,18 @@ def vertex_adjacency_matrix(self): (0, 0, 0, 0, 1) A ray in the direction (1, 1) (0, 0, 1, 1, 0) A vertex at (3, 0) + The vertex adjacency matrix has base ring integers. This way one can express various + counting questions:: + + sage: P = polytopes.cube() + sage: Q = P.stack(P.faces(2)[0]) + sage: M = Q.vertex_adjacency_matrix() + sage: sum(M) + (4, 4, 3, 3, 4, 4, 4, 3, 3) + sage: G = Q.vertex_graph() + sage: G.degree() + [4, 4, 3, 3, 4, 4, 4, 3, 3] + TESTS: Check that :trac:`28828` is fixed:: @@ -2413,7 +2628,7 @@ def boundary_complex(self): ineq_indices = [inc_mat_cols[i].nonzero_positions() for i in range(self.n_Hrepresentation()) if self.Hrepresentation()[i].is_inequality()] - return SimplicialComplex(ineq_indices,maximality_check=False) + return SimplicialComplex(ineq_indices, maximality_check=False) else: raise NotImplementedError("this function is only implemented for simplicial polytopes") @@ -2432,6 +2647,15 @@ def facet_adjacency_matrix(self): [1 1 1 0 1] [1 1 1 1 0] + The facet adjacency matrix has base ring integers. This way one can express various + counting questions:: + + sage: P = polytopes.cube() + sage: Q = P.stack(P.faces(2)[0]) + sage: M = Q.facet_adjacency_matrix() + sage: sum(M) + (4, 4, 4, 4, 3, 3, 3, 3, 4) + TESTS: Check that :trac:`28828` is fixed:: @@ -2453,6 +2677,10 @@ def incidence_matrix(self): vertices/rays/lines in the order :meth:`Vrepresentation`. + .. SEEALSO:: + + :meth:`slack_matrix`. + EXAMPLES:: sage: p = polytopes.cuboctahedron() @@ -2495,7 +2723,7 @@ def incidence_matrix(self): [1 1 1 0 1] [1 1 0 1 1] [1 0 1 1 1] - sage: simplex = simplex.affine_hull(); simplex + sage: simplex = simplex.affine_hull_projection(); simplex A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices sage: simplex.incidence_matrix() [1 1 1 0] @@ -2534,40 +2762,150 @@ def incidence_matrix(self): [0 1 0] [0 0 1] + The incidence matrix has base ring integers. This way one can express various + counting questions:: + + sage: P = polytopes.twenty_four_cell() + sage: M = P.incidence_matrix() + sage: sum(sum(x) for x in M) == P.flag_f_vector(0,3) + True + TESTS: Check that :trac:`28828` is fixed:: - sage: R.incidence_matrix().is_immutable() - True + sage: R.incidence_matrix().is_immutable() + True + + Test that this method works for inexact base ring + (``cdd`` sets the cache already):: + + sage: P = polytopes.dodecahedron(exact=False) + sage: M = P.incidence_matrix.cache + sage: P.incidence_matrix.clear_cache() + sage: M == P.incidence_matrix() + True """ + if self.base_ring() in (ZZ, QQ): + # Much faster for integers or rationals. + incidence_matrix = self.slack_matrix().zero_pattern_matrix(ZZ) + incidence_matrix.set_immutable() + return incidence_matrix + incidence_matrix = matrix(ZZ, self.n_Vrepresentation(), self.n_Hrepresentation(), 0) - Vvectors_vertices = tuple((v.vector(),v.index()) + Vvectors_vertices = tuple((v.vector(), v.index()) for v in self.Vrep_generator() if v.is_vertex()) - Vvectors_rays_lines = tuple((v.vector(),v.index()) + Vvectors_rays_lines = tuple((v.vector(), v.index()) for v in self.Vrep_generator() if not v.is_vertex()) + # Determine ``is_zero`` to save lots of time. + if self.base_ring().is_exact(): + def is_zero(x): + return not x + else: + is_zero = self._is_zero + for H in self.Hrep_generator(): Hconst = H.b() Hvec = H.A() Hindex = H.index() for Vvec, Vindex in Vvectors_vertices: - if self._is_zero(Hvec*Vvec + Hconst): - incidence_matrix[Vindex, Hindex] = 1 + if is_zero(Hvec*Vvec + Hconst): + incidence_matrix[Vindex, Hindex] = 1 # A ray or line is considered incident with a hyperplane, # if it is orthogonal to the normal vector of the hyperplane. for Vvec, Vindex in Vvectors_rays_lines: - if self._is_zero(Hvec*Vvec): - incidence_matrix[Vindex, Hindex] = 1 + if is_zero(Hvec*Vvec): + incidence_matrix[Vindex, Hindex] = 1 incidence_matrix.set_immutable() return incidence_matrix + @cached_method + def slack_matrix(self): + r""" + Return the slack matrix. + + The entries correspond to the evaluation of the Hrepresentation + elements on the Vrepresentation elements. + + .. NOTE:: + + The columns correspond to inequalities/equations in the + order :meth:`Hrepresentation`, the rows correspond to + vertices/rays/lines in the order + :meth:`Vrepresentation`. + + .. SEEALSO:: + + :meth:`incidence_matrix`. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: P.slack_matrix() + [0 2 2 2 0 0] + [0 0 2 2 0 2] + [0 0 0 2 2 2] + [0 2 0 2 2 0] + [2 2 0 0 2 0] + [2 2 2 0 0 0] + [2 0 2 0 0 2] + [2 0 0 0 2 2] + + sage: P = polytopes.cube(intervals='zero_one') + sage: P.slack_matrix() + [0 1 1 1 0 0] + [0 0 1 1 0 1] + [0 0 0 1 1 1] + [0 1 0 1 1 0] + [1 1 0 0 1 0] + [1 1 1 0 0 0] + [1 0 1 0 0 1] + [1 0 0 0 1 1] + + sage: P = polytopes.dodecahedron().faces(2)[0].as_polyhedron() + sage: P.slack_matrix() + [1/2*sqrt5 - 1/2 0 0 1 1/2*sqrt5 - 1/2 0] + [ 0 0 1/2*sqrt5 - 1/2 1/2*sqrt5 - 1/2 1 0] + [ 0 1/2*sqrt5 - 1/2 1 0 1/2*sqrt5 - 1/2 0] + [ 1 1/2*sqrt5 - 1/2 0 1/2*sqrt5 - 1/2 0 0] + [1/2*sqrt5 - 1/2 1 1/2*sqrt5 - 1/2 0 0 0] + + sage: P = Polyhedron(rays=[[1, 0], [0, 1]]) + sage: P.slack_matrix() + [0 0] + [0 1] + [1 0] + + TESTS:: + + sage: Polyhedron().slack_matrix() + [] + sage: Polyhedron(base_ring=QuadraticField(2)).slack_matrix().base_ring() + Number Field in a with defining polynomial x^2 - 2 with a = 1.41... + """ + if not self.n_Vrepresentation() or not self.n_Hrepresentation(): + slack_matrix = matrix(self.base_ring(), self.n_Vrepresentation(), + self.n_Hrepresentation(), 0) + else: + Vrep_matrix = matrix(self.base_ring(), self.Vrepresentation()) + Hrep_matrix = matrix(self.base_ring(), self.Hrepresentation()) + + # Getting homogenous coordinates of the Vrepresentation. + hom_helper = matrix(self.base_ring(), [1 if v.is_vertex() else 0 for v in self.Vrepresentation()]) + hom_Vrep = hom_helper.stack(Vrep_matrix.transpose()) + + slack_matrix = (Hrep_matrix * hom_Vrep).transpose() + + slack_matrix.set_immutable() + return slack_matrix + def base_ring(self): """ Return the base ring. @@ -2658,6 +2996,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): """ @@ -2695,6 +3125,51 @@ def representative_point(self): accumulator.set_immutable() return accumulator + def a_maximal_chain(self): + r""" + Return a maximal chain of the face lattice in increasing order. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: P.a_maximal_chain() + [A -1-dimensional face of a Polyhedron in ZZ^3, + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex, + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices, + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices, + A 3-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 8 vertices] + sage: P = polytopes.cube() + sage: chain = P.a_maximal_chain(); chain + [A -1-dimensional face of a Polyhedron in ZZ^3, + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex, + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices, + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices, + A 3-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 8 vertices] + sage: [face.ambient_V_indices() for face in chain] + [(), (5,), (0, 5), (0, 3, 4, 5), (0, 1, 2, 3, 4, 5, 6, 7)] + + TESTS:: + + Check output for the empty polyhedron:: + + sage: P = Polyhedron() + sage: P.a_maximal_chain() + [A -1-dimensional face of a Polyhedron in ZZ^0] + """ + comb_chain = self.combinatorial_polyhedron().a_maximal_chain() + + from sage.geometry.polyhedron.face import combinatorial_face_to_polyhedral_face + empty_face = self.faces(-1)[0] + universe = self.faces(self.dim())[0] + + if self.dim() == -1: + return [empty_face] + + return [empty_face] + \ + [combinatorial_face_to_polyhedral_face(self, face) + for face in comb_chain] + \ + [universe] + @cached_method def radius_square(self): """ @@ -2795,13 +3270,13 @@ def is_inscribed(self, certificate=False): sage: square.is_inscribed() Traceback (most recent call last): ... - NotImplementedError: this function is implemented for full-dimensional polyhedron only + NotImplementedError: this function is implemented for full-dimensional polyhedra only sage: p = Polyhedron(vertices=[(0,0)],rays=[(1,0),(0,1)]) sage: p.is_inscribed() Traceback (most recent call last): ... - NotImplementedError: this function is not implemented for unbounded polyhedron + NotImplementedError: this function is not implemented for unbounded polyhedra TESTS: @@ -2867,37 +3342,25 @@ def is_inscribed(self, certificate=False): """ if not self.is_compact(): - raise NotImplementedError("this function is not implemented for unbounded polyhedron") + raise NotImplementedError("this function is not implemented for unbounded polyhedra") if not self.is_full_dimensional(): - raise NotImplementedError("this function is implemented for full-dimensional polyhedron only") + raise NotImplementedError("this function is implemented for full-dimensional polyhedra only") dimension = self.dimension() vertices = self.vertices() - # We greedily construct a full-dimensional simplex made of vertices - # around some vertex of self. - vertex = vertices[0] - v0 = vertex.vector() - M = matrix(self.base_ring(), dimension, 0) - simplex_vertices = [vertex] - for v in vertex.neighbors(): - MA = M.augment(v.vector()-v0) - if MA.rank() > M.rank(): - simplex_vertices.append(v) - M = MA - if M.rank() == dimension: - break - + # We obtain vertices that are an affine basis of the affine hull. + affine_basis = self.an_affine_basis() raw_data = [] - for vertex in simplex_vertices: + for vertex in affine_basis: vertex_vector = vertex.vector() raw_data += [[sum(i**2 for i in vertex_vector)] + [i for i in vertex_vector] + [1]] matrix_data = matrix(raw_data) - # The determinant "a" should not be zero because the polytope is full - # dimensional and also the simplex. + # The determinant "a" should not be zero because + # the vertices in ``affine_basis`` are an affine basis. a = matrix_data.matrix_from_columns(range(1, dimension+2)).determinant() minors = [(-1)**(i)*matrix_data.matrix_from_columns([j for j in range(dimension+2) if j != i]).determinant() @@ -2909,11 +3372,11 @@ def is_inscribed(self, certificate=False): # Checking if the circumcenter has the correct sign if not all(sum(i**2 for i in v.vector() - circumcenter) == squared_circumradius - for v in vertices if v in simplex_vertices): + for v in vertices if v in affine_basis): circumcenter = - circumcenter is_inscribed = all(sum(i**2 for i in v.vector() - circumcenter) == squared_circumradius - for v in vertices if v not in simplex_vertices) + for v in vertices if v not in affine_basis) if certificate: if is_inscribed: @@ -2959,6 +3422,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. @@ -2973,11 +3470,43 @@ def is_simple(self): sage: p = Polyhedron([[0,0,0],[4,4,0],[4,0,0],[0,4,0],[2,2,2]]) sage: p.is_simple() False - """ - if not self.is_compact(): return False + if not self.is_compact(): + return False return self.combinatorial_polyhedron().is_simple() + def simpliciality(self): + r""" + Return the largest integer `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 @@ -3049,21 +3578,22 @@ def is_pyramid(self, certificate=False): sage: Q = polytopes.octahedron() sage: Q.is_pyramid() False + + For the `0`-dimensional polyhedron, the output is ``True``, + but it cannot be constructed as a pyramid over the empty polyhedron:: + + sage: P = Polyhedron([[0]]) + sage: P.is_pyramid() + True + sage: Polyhedron().pyramid() + Traceback (most recent call last): + ... + ZeroDivisionError: rational division by zero """ if not self.is_compact(): raise ValueError("polyhedron has to be compact") - # Find a vertex that is incident to all elements in Hrepresentation but one. - IM = self.incidence_matrix() - for index in range(self.n_vertices()): - vertex_incidences = IM.row(index) - if sum(vertex_incidences) == IM.ncols() - 1: - if certificate: - return (True, self.vertices()[index]) - return True - if certificate: - return (False, None) - return False + return self.combinatorial_polyhedron().is_pyramid(certificate) def is_bipyramid(self, certificate=False): r""" @@ -3212,14 +3742,14 @@ def is_prism(self, certificate=False): True sage: P.is_prism(certificate=True) (True, - [[A vertex at (-1, -1, 1), - A vertex at (-1, 1, 1), - A vertex at (1, -1, 1), - A vertex at (1, 1, 1)], - [A vertex at (-1, -1, -1), + [[A vertex at (1, -1, -1), + A vertex at (1, 1, -1), + A vertex at (1, 1, 1), + A vertex at (1, -1, 1)], + [A vertex at (-1, -1, 1), + A vertex at (-1, -1, -1), A vertex at (-1, 1, -1), - A vertex at (1, -1, -1), - A vertex at (1, 1, -1)]]) + A vertex at (-1, 1, 1)]]) sage: Q = polytopes.cyclic_polytope(3,8) sage: Q.is_prism() False @@ -3302,7 +3832,7 @@ def is_prism(self, certificate=False): # We have found two candidates for base faces. # Remove from each vertex ``index1`` resp. ``index2``. test_verts = set(frozenset(vert_inc.difference({index1, index2})) - for vert_inc in verts_incidences) + for vert_inc in verts_incidences) if len(test_verts) == n_verts/2: # For each vertex containing `index1` there is # another one contained in `index2` @@ -3371,6 +3901,10 @@ def gale_transform(self): Lectures in Geometric Combinatorics, R.R.Thomas, 2006, AMS Press. + .. SEEALSO:: + + :func`~sage.geometry.polyhedron.library.gale_transform_to_polyhedron`. + TESTS:: sage: P = Polyhedron(rays=[[1,0,0]]) @@ -3385,14 +3919,50 @@ def gale_transform(self): sage: sum(P.gale_transform()).norm() < 1e-15 True """ - if not self.is_compact(): raise ValueError('not a polytope') + if not self.is_compact(): + raise ValueError('not a polytope') A = matrix(self.n_vertices(), - [ [1]+x for x in self.vertex_generator()]) + [[1]+x for x in self.vertex_generator()]) A = A.transpose() A_ker = A.right_kernel_matrix(basis='computed') return tuple(A_ker.columns()) + def _test_gale_transform(self, tester=None, **options): + """ + Run tests on the method :meth:`.gale_transform` and its inverse + :meth:`~sage.geometry.polyhedron.library.gale_transform_to_polytope`. + + TESTS:: + + sage: polytopes.cross_polytope(3)._test_gale_transform() + """ + if tester is None: + tester = self._tester(**options) + + if not self.is_compact(): + with tester.assertRaises(ValueError): + self.gale_transform() + return + + # Check :trac:`29073`. + if not self.base_ring().is_exact() and self.ambient_dim() > 0: + g = self.gale_transform() + tester.assertTrue(sum(g).norm() < 1e-10 or sum(g).norm()/matrix(g).norm() < 1e-13) + return + + # Prevent very long doctests. + if self.n_vertices() + self.n_rays() > 50 or self.n_facets() > 50: + return + + if not self.is_empty(): + # ``gale_transform_to_polytope`` needs at least one vertex to work. + from sage.geometry.polyhedron.library import gale_transform_to_polytope + g = self.gale_transform() + P = gale_transform_to_polytope(g, base_ring=self.base_ring(), backend=self.backend()) + + tester.assertTrue(self.is_combinatorially_isomorphic(P)) + @cached_method def normal_fan(self, direction='inner'): r""" @@ -3619,13 +4189,15 @@ def triangulate(self, engine='auto', connected=True, fine=False, regular=None, s sage: triangulation = cube.triangulate( ....: engine='internal') # to make doctest independent of TOPCOM sage: triangulation - (<0,1,2,7>, <0,1,4,7>, <0,2,4,7>, <1,2,3,7>, <1,4,5,7>, <2,4,6,7>) + (<0,1,2,7>, <0,1,5,7>, <0,2,3,7>, <0,3,4,7>, <0,4,5,7>, <1,5,6,7>) sage: simplex_indices = triangulation[0]; simplex_indices (0, 1, 2, 7) sage: simplex_vertices = [ cube.Vrepresentation(i) for i in simplex_indices ] sage: simplex_vertices - [A vertex at (-1, -1, -1), A vertex at (-1, -1, 1), - A vertex at (-1, 1, -1), A vertex at (1, 1, 1)] + [A vertex at (1, -1, -1), + A vertex at (1, 1, -1), + A vertex at (1, 1, 1), + A vertex at (-1, 1, 1)] sage: Polyhedron(simplex_vertices) A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices @@ -3956,14 +4528,64 @@ def translation(self, displacement): sage: P = Polyhedron([[0,0],[1,0],[0,1]], base_ring=ZZ, backend='field') sage: P.translation([2,1]).backend() 'field' + + Check that precomputed data is set up correctly:: + + sage: P = polytopes.permutahedron(4)*Polyhedron(lines=[[1]]) + sage: Q = P.change_ring(P.base_ring(), backend='field') + sage: P + vector([1,2,3,4,5]) == Q + vector([1,2,3,4,5]) + True + sage: P + vector([1,2,3,4,5/2]) == Q + vector([1,2,3,4,5/2]) + True + """ + Vrep, Hrep, parent = self._translation_double_description(displacement) + + pref_rep = 'Vrep' if self.n_vertices() + self.n_rays() <= self.n_inequalities() else 'Hrep' + + return parent.element_class(parent, Vrep, Hrep, + Vrep_minimal=True, Hrep_minimal=True, pref_rep=pref_rep) + + def _translation_double_description(self, displacement): + r""" + Return the input parameters for the translation. + + INPUT: + + - ``displacement`` -- a displacement vector or a list/tuple of + coordinates that determines a displacement vector + + OUTPUT: Tuple of consisting of new Vrepresentation, Hrepresentation and parent. + + .. SEEALSO:: + + :meth:`translation` + + EXAMPLES:: + + sage: P = Polyhedron([[0,0],[1,0],[0,1]], base_ring=ZZ) + sage: Vrep, Hrep, parent = P._translation_double_description([2,1]) + sage: [tuple(x) for x in Vrep], [tuple(x) for x in Hrep], parent + ([((2, 1), (2, 2), (3, 1)), (), ()], + [((-2, 1, 0), (-1, 0, 1), (4, -1, -1)), ()], + Polyhedra in ZZ^2) """ displacement = vector(displacement) - new_vertices = [x.vector()+displacement for x in self.vertex_generator()] + new_vertices = (x.vector()+displacement for x in self.vertex_generator()) new_rays = self.rays() new_lines = self.lines() - parent = self.parent().base_extend(displacement) - return parent.element_class(parent, [new_vertices, new_rays, new_lines], None) + + # Replace a hyperplane of the form A*x + b >= 0 by + # A(x-displacement) + b >= 0 <=> Ax + b - A*displacement >= 0. + # Likewise for equations. + def get_new(x): + y = x.vector().change_ring(parent.base_ring()) + y[0] -= x.A()*displacement + return y + + new_ieqs = (get_new(x) for x in self.inequality_generator()) + new_eqns = (get_new(x) for x in self.equation_generator()) + return [new_vertices, new_rays, new_lines], [new_ieqs, new_eqns], parent def product(self, other): """ @@ -4015,28 +4637,51 @@ def product(self, other): 'ppl' sage: (P * polytopes.dodecahedron(backend='field')).backend() 'field' + + Check that double description is set up correctly:: + + sage: P = polytopes.permutahedron(4).base_extend(QQ) + sage: P1 = Polyhedron(rays=[[1,0,0,0],[0,1,1,0]], lines=[[0,1,0,1]]) + sage: Q = P.base_extend(QQ, 'field') + sage: Q1 = P1.base_extend(QQ, 'field') + sage: P * P1 == Q * Q1 + True + sage: P.polar(in_affine_span=True) * P1 == Q.polar(in_affine_span=True) * Q1 + True """ try: new_ring = self.parent()._coerce_base_ring(other) except TypeError: - raise TypeError("no common canonical parent for objects with parents: " + str(self.parent()) \ - + " and " + str(other.parent())) + raise TypeError("no common canonical parent for objects with parents: " + str(self.parent()) + + " and " + str(other.parent())) - new_vertices = [ list(x)+list(y) - for x in self.vertex_generator() for y in other.vertex_generator()] - new_rays = [] - new_rays.extend( [ r+[0]*other.ambient_dim() - for r in self.ray_generator() ] ) - new_rays.extend( [ [0]*self.ambient_dim()+r - for r in other.ray_generator() ] ) - new_lines = [] - new_lines.extend( [ l+[0]*other.ambient_dim() - for l in self.line_generator() ] ) - new_lines.extend( [ [0]*self.ambient_dim()+l - for l in other.line_generator() ] ) + from itertools import chain + + new_vertices = (tuple(x) + tuple(y) + for x in self.vertex_generator() for y in other.vertex_generator()) + + self_zero = tuple(0 for _ in range( self.ambient_dim())) + other_zero = tuple(0 for _ in range(other.ambient_dim())) + + rays = chain((tuple(r) + other_zero for r in self.ray_generator()), + (self_zero + tuple(r) for r in other.ray_generator())) + + lines = chain((tuple(l) + other_zero for l in self.line_generator()), + (self_zero + tuple(l) for l in other.line_generator())) + + ieqs = chain((tuple(i) + other_zero for i in self.inequality_generator()), + ((i.b(),) + self_zero + tuple(i.A()) for i in other.inequality_generator())) + + eqns = chain((tuple(e) + other_zero for e in self.equation_generator()), + ((e.b(),) + self_zero + tuple(e.A()) for e in other.equation_generator())) + + pref_rep = 'Vrep' if self.n_vertices() + self.n_rays() + other.n_vertices() + other.n_rays() \ + <= self.n_inequalities() + other.n_inequalities() else 'Hrep' parent = self.parent().change_ring(new_ring, ambient_dim=self.ambient_dim() + other.ambient_dim()) - return parent.element_class(parent, [new_vertices, new_rays, new_lines], None) + return parent.element_class(parent, [new_vertices, rays, lines], + [ieqs, eqns], + Vrep_minimal=True, Hrep_minimal=True, pref_rep=pref_rep) _mul_ = product @@ -4291,36 +4936,101 @@ def dilation(self, scalar): () sage: (0*p).lines() () - - Dilation respects backend:: - - sage: P = polytopes.simplex(backend='field') - sage: P.dilation(3).backend() - 'field' """ - if scalar > 0: - new_vertices = [ list(scalar*v.vector()) for v in self.vertex_generator() ] - new_rays = self.rays() - new_lines = self.lines() - elif scalar < 0: - new_vertices = [ list(scalar*v.vector()) for v in self.vertex_generator() ] - new_rays = [ list(-r.vector()) for r in self.ray_generator()] - new_lines = self.lines() - else: - new_vertices = [ self.ambient_space().zero() for v in self.vertex_generator() ] + parent = self.parent().base_extend(scalar) + + if scalar == 0: + new_vertices = tuple(self.ambient_space().zero() for v in self.vertex_generator()) new_rays = [] new_lines = [] + return parent.element_class(parent, [new_vertices, new_rays, new_lines], None) - parent = self.parent().base_extend(scalar) - return parent.element_class(parent, [new_vertices, new_rays, new_lines], None) + one = parent.base_ring().one() + sign = one if scalar > 0 else -one + + make_new_Hrep = lambda h: tuple(scalar*sign*x if i == 0 else sign*x + for i, x in enumerate(h._vector)) + + new_vertices = (tuple(scalar*x for x in v._vector) for v in self.vertex_generator()) + new_rays = (tuple(sign*x for x in r._vector) for r in self.ray_generator()) + new_lines = self.line_generator() + new_inequalities = map(make_new_Hrep, self.inequality_generator()) + new_equations = map(make_new_Hrep, self.equation_generator()) + + pref_rep = 'Vrep' if self.n_vertices() + self.n_rays() <= self.n_inequalities() else 'Hrep' + + return parent.element_class(parent, [new_vertices, new_rays, new_lines], + [new_inequalities, new_equations], + Vrep_minimal=True, Hrep_minimal=True, pref_rep=pref_rep) + + def _test_dilation(self, tester=None, **options): + """ + Run tests on the method :meth:`.dilation`. + + TESTS:: + + sage: polytopes.cross_polytope(3)._test_dilation() + """ + if tester is None: + tester = self._tester(**options) + + # Testing that the backend is preserved. + tester.assertEqual(self.dilation(2*self.base_ring().gen()).backend(), self.backend()) + tester.assertEqual(self.dilation(ZZ(3)).backend(), self.backend()) + + if self.n_vertices() > 40: + # Avoid long time computations. + return + + # Testing that the double description is set up correctly. + if self.base_ring().is_exact(): + p = self.base_extend(self.base_ring(), backend='field') + (ZZ(2)*p)._test_basic_properties(tester) + (ZZ(2)/2*p)._test_basic_properties(tester) + (ZZ(-3)*p)._test_basic_properties(tester) + (ZZ(-1)/2*p)._test_basic_properties(tester) + else: + tester.assertIsInstance(ZZ(1)/3*self, Polyhedron_base) + + if self.n_vertices() > 20: + # Avoid long time computations. + return + + # Some sanity check on the volume (only run for relativly small instances). + if self.dim() > -1 and self.is_compact() and self.base_ring().is_exact(): + tester.assertEqual(self.dilation(3).volume(measure='induced'), self.volume(measure='induced')*3**self.dim()) + + # Testing coercion with algebraic numbers. + from sage.rings.number_field.number_field import QuadraticField + K1 = QuadraticField(2, embedding=AA(2).sqrt()) + sqrt2 = K1.gen() + K2 = QuadraticField(3, embedding=AA(3).sqrt()) + sqrt3 = K2.gen() + + if self.base_ring() in (QQ,ZZ,AA,RDF): + tester.assertIsInstance(sqrt2*self, Polyhedron_base) + tester.assertIsInstance(sqrt3*self, Polyhedron_base) + elif hasattr(self.base_ring(), "composite_fields"): + for scalar, K in ((sqrt2, K1), (sqrt3, K2)): + new_ring = None + try: + new_ring = self.base_ring().composite_fields()[0] + except: + # This isn't about testing composite fields. + pass + if new_ring: + p = self.change_ring(new_ring) + tester.assertIsInstance(scalar*p, Polyhedron_base) - def linear_transformation(self, linear_transf): + def linear_transformation(self, linear_transf, new_base_ring=None): """ Return the linear transformation of ``self``. INPUT: - ``linear_transf`` -- a matrix, not necessarily in :meth:`base_ring` + - ``new_base_ring`` -- ring (optional); specify the new base ring; + may avoid coercion failure OUTPUT: @@ -4340,10 +5050,26 @@ def linear_transformation(self, linear_transf): sage: transf = matrix([[1,1],[0,1]]) sage: sheared = transf * square sage: sheared.vertices_list() - [[-1, 0], [-1, -1], [1, 1], [1, 0]] + [[-1, -1], [1, 0], [-1, 0], [1, 1]] sage: sheared == square.linear_transformation(transf) True + Specifying the new base ring may avoid coercion failure:: + + sage: K. = QuadraticField(2) + sage: L. = QuadraticField(3) + sage: P = polytopes.cube()*sqrt2 + sage: M = matrix([[sqrt3, 0, 0], [0, sqrt3, 0], [0, 0, 1]]) + sage: P.linear_transformation(M, new_base_ring=K.composite_fields(L)[0]) + A 3-dimensional polyhedron in (Number Field in sqrt2sqrt3 with defining polynomial x^4 - 10*x^2 + 1 with sqrt2sqrt3 = 0.3178372451957823?)^3 defined as the convex hull of 8 vertices + + Linear transformation without specified new base ring fails in this case:: + + sage: M*P + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for *: 'Full MatrixSpace of 3 by 3 dense matrices over Number Field in sqrt3 with defining polynomial x^2 - 3 with sqrt3 = 1.732050807568878?' and 'Full MatrixSpace of 3 by 8 dense matrices over Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.414213562373095?' + TESTS: Linear transformation respects backend:: @@ -4373,18 +5099,80 @@ def linear_transformation(self, linear_transf): sage: Matrix([[0 for _ in range(8)]]) * b3 Traceback (most recent call last): ... - TypeError: unsupported operand parent(s) for *: 'Full MatrixSpace of 1 by 8 dense matrices over Integer Ring' and 'Ambient free module of rank 9 over the principal ideal domain Integer Ring' + TypeError: unsupported operand parent(s) for *: 'Full MatrixSpace of 1 by 8 dense matrices over Integer Ring' and 'Full MatrixSpace of 9 by 6 dense matrices over Integer Ring' sage: Matrix(ZZ, []) * b3 A 0-dimensional polyhedron in ZZ^0 defined as the convex hull of 1 vertex sage: Matrix(ZZ, [[],[]]) * b3 Traceback (most recent call last): ... - TypeError: unsupported operand parent(s) for *: 'Full MatrixSpace of 2 by 0 dense matrices over Integer Ring' and 'Ambient free module of rank 9 over the principal ideal domain Integer Ring' + TypeError: unsupported operand parent(s) for *: 'Full MatrixSpace of 2 by 0 dense matrices over Integer Ring' and 'Full MatrixSpace of 9 by 6 dense matrices over Integer Ring' + + Check that the precomputed double description is correct:: + + sage: P = polytopes.permutahedron(4) + sage: Q = P.change_ring(QQ, backend='field') + sage: P.affine_hull_projection() == Q.affine_hull_projection() + True + + sage: M = matrix([[1, 2, 3, 4], [2, 3, 4, 5], [0, 0, 5, 1], [0, 2, 0, 3]]) + sage: M*P == M*Q + True + + sage: M = matrix([[1, 2, 3, 4], [2, 3, 4, 5], [0, 0, 5, 1], [0, 2, 0, 3], [0, 1, 0, -3]]) + sage: M*P == M*Q + True """ + is_injective = False if linear_transf.nrows() != 0: - new_vertices = [ list(linear_transf*v.vector()) for v in self.vertex_generator() ] - new_rays = [ list(linear_transf*r.vector()) for r in self.ray_generator() ] - new_lines = [ list(linear_transf*l.vector()) for l in self.line_generator() ] + if new_base_ring: + R = new_base_ring + else: + R = self.base_ring() + + # Multiplying a matrix with a vector is slow. + # So we multiply the entire vertex matrix etc. + # Still we create generators, as possibly the Vrepresentation will be discarded later on. + if self.n_vertices(): + new_vertices = ( v for v in ((linear_transf*self.vertices_matrix(R)).transpose()) ) + else: + new_vertices = () + if self.n_rays(): + new_rays = ( r for r in matrix(R, self.rays())*linear_transf.transpose() ) + else: + new_rays = () + if self.n_lines(): + new_lines = ( l for l in matrix(R, self.lines())*linear_transf.transpose() ) + else: + new_lines = () + + if self.is_compact() and self.n_vertices() and self.n_inequalities(): + homogenous_basis = matrix(R, ( [1] + list(v) for v in self.an_affine_basis() )).transpose() + + # To convert first to a list and then to a matrix seems to be necesarry to obtain a meaningful error, + # in case the number of columns doesn't match the dimension. + new_homogenous_basis = matrix(list( [1] + list(linear_transf*vector(R, v)) for v in self.an_affine_basis()) ).transpose() + + if self.dim() + 1 == new_homogenous_basis.rank(): + # The transformation is injective on the polytope. + is_injective = True + + # Let V be the homogenous vertex matrix (each vertex a column) + # and M the linear transformation. + # Then M*V is the new homogenous vertex matrix. + + # Let H be the inequalities matrix (each inequality a row). + # If we find N such that N*M*V = V than the new inequalities are + # given by H*N. + + # Note that such N must exist, as our map is injective on the polytope. + # It is uniquely defined by considering a basis of the homogenous vertices. + N = new_homogenous_basis.solve_left(homogenous_basis) + new_inequalities = ( h for h in matrix(R, self.inequalities())*N ) + + # The equations are the left kernel matrix of the homogenous vertices + # or equivalently a basis thereof. + new_equations = (new_homogenous_basis.transpose()).right_kernel_matrix() + else: new_vertices = [[] for v in self.vertex_generator() ] new_rays = [] @@ -4392,9 +5180,36 @@ def linear_transformation(self, linear_transf): new_dim = linear_transf.nrows() par = self.parent() - new_parent = par.base_extend(linear_transf.base_ring(), ambient_dim=new_dim) - return new_parent.element_class(new_parent, [new_vertices, new_rays, new_lines], None) + if new_base_ring: + new_parent = par.change_ring(new_base_ring, ambient_dim=new_dim) + else: + new_parent = par.base_extend(linear_transf.base_ring(), ambient_dim=new_dim) + + if is_injective: + # Set up with both Vrepresentation and Hrepresentation. + pref_rep = 'Vrep' if self.n_vertices() <= self.n_inequalities() else 'Hrep' + + return new_parent.element_class(new_parent, [new_vertices, new_rays, new_lines], + [new_inequalities, new_equations], + Vrep_minimal=True, Hrep_minimal=True, pref_rep=pref_rep) + + return new_parent.element_class(new_parent, [tuple(new_vertices), tuple(new_rays), tuple(new_lines)], None) + + def _test_linear_transformation(self, tester=None, **options): + """ + Run some tests on linear transformation. + + TESTS:: + + sage: Polyhedron(rays=[(0,1)])._test_linear_transformation() + """ + if tester is None: + tester = self._tester(**options) + + # Check that :trac:`30146` is fixed. + from sage.matrix.special import identity_matrix + tester.assertEqual(self, self.linear_transformation(identity_matrix(self.ambient_dim()))) def _acted_upon_(self, actor, self_on_left): """ @@ -4502,8 +5317,6 @@ def __truediv__(self, scalar): """ return self.dilation(1/scalar) - __div__ = __truediv__ - @coerce_binop def convex_hull(self, other): """ @@ -4739,13 +5552,13 @@ def face_truncation(self, face, linear_coefficients=None, cut_frac=None): sage: edge_trunc.f_vector() (1, 10, 15, 7, 1) sage: tuple(f.ambient_V_indices() for f in edge_trunc.faces(2)) - ((0, 4, 5, 6), - (0, 1, 6, 7), - (5, 6, 7, 8, 9), - (3, 4, 5, 9), - (1, 2, 7, 8), - (2, 3, 8, 9), - (0, 1, 2, 3, 4)) + ((0, 5, 6, 7), + (1, 4, 5, 6, 8), + (6, 7, 8, 9), + (0, 2, 3, 7, 9), + (1, 2, 8, 9), + (0, 3, 4, 5), + (1, 2, 3, 4)) sage: face_trunc = Cube.face_truncation(Cube.faces(2)[2]) sage: face_trunc.vertices() (A vertex at (1, -1, -1), @@ -4790,15 +5603,14 @@ def face_truncation(self, face, linear_coefficients=None, cut_frac=None): normal_vectors.append(facet.A()) if linear_coefficients is not None: - normal_vector = sum(linear_coefficients[i]*normal_vectors[i] for i - in range(len(normal_vectors))) + normal_vector = sum(linear_coefficients[i]*normal_vectors[i] + for i in range(len(normal_vectors))) else: normal_vector = sum(normal_vectors) B = - normal_vector * (face_vertices[0].vector()) - linear_evaluation = set(-normal_vector * (v.vector()) for v in - self.vertices()) + linear_evaluation = set(-normal_vector * (v.vector()) for v in self.vertices()) if B == max(linear_evaluation): C = max(linear_evaluation.difference(set([B]))) @@ -5166,7 +5978,7 @@ def lawrence_extension(self, v): raise ValueError("{} must not be a vertex or outside self".format(v)) lambda_V = [u + [0] for u in V if u != v] + [v+[1]] + [v+[2]] - parent = self.parent().change_ring(self.base_ring(), ambient_dim = self.ambient_dim() + 1) + parent = self.parent().change_ring(self.base_ring(), ambient_dim=self.ambient_dim() + 1) return parent.element_class(parent, [lambda_V, [], []], None) def lawrence_polytope(self): @@ -5228,7 +6040,7 @@ def lawrence_polytope(self): n = self.n_vertices() I_n = matrix.identity(n) lambda_V = block_matrix([[V, I_n], [V, 2*I_n]]) - parent = self.parent().change_ring(self.base_ring(), ambient_dim = self.ambient_dim() + n) + parent = self.parent().change_ring(self.base_ring(), ambient_dim=self.ambient_dim() + n) return parent.element_class(parent, [lambda_V, [], []], None) def is_lawrence_polytope(self): @@ -5257,18 +6069,7 @@ def is_lawrence_polytope(self): if not self.is_compact(): raise NotImplementedError("self must be a polytope") - n = self.n_vertices() - vertices = list(range(n)) - facets = self.incidence_matrix().columns() - - for facet in facets: - facet_vertices = facet.nonzero_positions() - if len(facet_vertices) == n-1 or len(facet_vertices) == n-2: - facet_non_vertices = [i for i in range(n) if i not in facet_vertices] - if all(vertex in vertices for vertex in facet_non_vertices): - for vertex in facet_non_vertices: - vertices.remove(vertex) - return not vertices + return self.combinatorial_polyhedron().is_lawrence_polytope() def barycentric_subdivision(self, subdivision_frac=None): r""" @@ -5456,20 +6257,20 @@ def face_lattice(self): sage: fl = square.face_lattice();fl Finite lattice containing 10 elements with distinguished linear extension sage: list(f.ambient_V_indices() for f in fl) - [(), (0,), (1,), (2,), (3,), (0, 1), (0, 2), (2, 3), (1, 3), (0, 1, 2, 3)] + [(), (0,), (1,), (2,), (3,), (0, 1), (1, 2), (2, 3), (0, 3), (0, 1, 2, 3)] sage: poset_element = fl[6] sage: a_face = poset_element sage: a_face A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: a_face.ambient_V_indices() - (0, 2) + (1, 2) sage: set(a_face.ambient_Vrepresentation()) == \ - ....: set([square.Vrepresentation(0), square.Vrepresentation(2)]) + ....: set([square.Vrepresentation(1), square.Vrepresentation(2)]) True sage: a_face.ambient_Vrepresentation() - (A vertex at (-1, -1), A vertex at (1, -1)) + (A vertex at (1, 1), A vertex at (-1, 1)) sage: a_face.ambient_Hrepresentation() - (An inequality (0, 1) x + 1 >= 0,) + (An inequality (0, -1) x + 1 >= 0,) A more complicated example:: @@ -5551,9 +6352,9 @@ def face_constructor(atoms, coatoms): if not atoms: Vindices = () else: - Vindices = tuple(sorted([ atom_to_Vindex[i] for i in atoms ]+lines)) - Hindices = tuple(sorted([ coatom_to_Hindex[i] for i in coatoms ]+equations)) - return PolyhedronFace(self,Vindices, Hindices) + Vindices = tuple(sorted([atom_to_Vindex[i] for i in atoms] + lines)) + Hindices = tuple(sorted([coatom_to_Hindex[i] for i in coatoms] + equations)) + return PolyhedronFace(self, Vindices, Hindices) from sage.geometry.hasse_diagram import lattice_from_incidences return lattice_from_incidences(atoms_incidences, coatoms_incidences, @@ -5720,23 +6521,27 @@ def faces(self, face_dimension): sage: p = polytopes.hypercube(4) sage: list(f.ambient_V_indices() for f in p.faces(3)) - [(0, 2, 4, 6, 8, 10, 12, 14), - (0, 1, 4, 5, 8, 9, 12, 13), - (0, 1, 2, 3, 8, 9, 10, 11), - (0, 1, 2, 3, 4, 5, 6, 7), - (1, 3, 5, 7, 9, 11, 13, 15), - (2, 3, 6, 7, 10, 11, 14, 15), - (4, 5, 6, 7, 12, 13, 14, 15), - (8, 9, 10, 11, 12, 13, 14, 15)] + [(0, 5, 6, 7, 8, 9, 14, 15), + (1, 4, 5, 6, 10, 13, 14, 15), + (1, 2, 6, 7, 8, 10, 11, 15), + (8, 9, 10, 11, 12, 13, 14, 15), + (0, 3, 4, 5, 9, 12, 13, 14), + (0, 2, 3, 7, 8, 9, 11, 12), + (1, 2, 3, 4, 10, 11, 12, 13), + (0, 1, 2, 3, 4, 5, 6, 7)] sage: face = p.faces(3)[3] sage: face.ambient_Hrepresentation() (An inequality (1, 0, 0, 0) x + 1 >= 0,) sage: face.vertices() - (A vertex at (-1, -1, -1, -1), A vertex at (-1, -1, -1, 1), - A vertex at (-1, -1, 1, -1), A vertex at (-1, -1, 1, 1), - A vertex at (-1, 1, -1, -1), A vertex at (-1, 1, -1, 1), - A vertex at (-1, 1, 1, -1), A vertex at (-1, 1, 1, 1)) + (A vertex at (-1, -1, 1, -1), + A vertex at (-1, -1, 1, 1), + A vertex at (-1, 1, -1, -1), + A vertex at (-1, 1, 1, -1), + A vertex at (-1, 1, 1, 1), + A vertex at (-1, 1, -1, 1), + A vertex at (-1, -1, -1, 1), + A vertex at (-1, -1, -1, -1)) You can use the :meth:`~sage.geometry.polyhedron.representation.PolyhedronRepresentation.index` @@ -5746,19 +6551,19 @@ def faces(self, face_dimension): sage: [get_idx(_) for _ in face.ambient_Hrepresentation()] [4] sage: [get_idx(_) for _ in face.ambient_Vrepresentation()] - [0, 1, 2, 3, 4, 5, 6, 7] + [8, 9, 10, 11, 12, 13, 14, 15] sage: [ ([get_idx(_) for _ in face.ambient_Vrepresentation()], ....: [get_idx(_) for _ in face.ambient_Hrepresentation()]) ....: for face in p.faces(3) ] - [([0, 2, 4, 6, 8, 10, 12, 14], [7]), - ([0, 1, 4, 5, 8, 9, 12, 13], [6]), - ([0, 1, 2, 3, 8, 9, 10, 11], [5]), - ([0, 1, 2, 3, 4, 5, 6, 7], [4]), - ([1, 3, 5, 7, 9, 11, 13, 15], [3]), - ([2, 3, 6, 7, 10, 11, 14, 15], [2]), - ([4, 5, 6, 7, 12, 13, 14, 15], [1]), - ([8, 9, 10, 11, 12, 13, 14, 15], [0])] + [([0, 5, 6, 7, 8, 9, 14, 15], [7]), + ([1, 4, 5, 6, 10, 13, 14, 15], [6]), + ([1, 2, 6, 7, 8, 10, 11, 15], [5]), + ([8, 9, 10, 11, 12, 13, 14, 15], [4]), + ([0, 3, 4, 5, 9, 12, 13, 14], [3]), + ([0, 2, 3, 7, 8, 9, 11, 12], [2]), + ([1, 2, 3, 4, 10, 11, 12, 13], [1]), + ([0, 1, 2, 3, 4, 5, 6, 7], [0])] TESTS:: @@ -5782,8 +6587,11 @@ def facets(self): r""" Return the facets of the polyhedron. + Facets are the maximal nontrivial faces of polyhedra. + The empty face and the polyhedron itself are trivial. + A facet of a `d`-dimensional polyhedron is a face of dimension - `d-1`. + `d-1`. For `d \neq 0` the converse is true as well. OUTPUT: @@ -5823,10 +6631,18 @@ def facets(self): A 3-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 8 vertices, A 3-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 8 vertices, A 3-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 8 vertices) + + The ``0``-dimensional polyhedron does not have facets:: + + sage: P = Polyhedron([[0]]) + sage: P.facets() + () """ + if self.dimension() == 0: + return () return self.faces(self.dimension()-1) - @cached_method + @cached_method(do_pickle=True) def f_vector(self): r""" Return the f-vector. @@ -5876,14 +6692,171 @@ def f_vector(self): sage: P.f_vector().is_immutable() True + + The cache of the f-vector is being pickled:: + + sage: P = polytopes.cube() + sage: P.f_vector() + (1, 8, 12, 6, 1) + sage: Q = loads(dumps(P)) + sage: Q.f_vector.is_in_cache() + True """ return self.combinatorial_polyhedron().f_vector() + def flag_f_vector(self, *args): + r""" + Return the flag f-vector. + + For each `-1 < i_0 < \dots < i_n < d` the flag f-vector + counts the number of flags `F_0 \subset \dots \subset F_n` + with `F_j` of dimension `i_j` for each `0 \leq j \leq n`, + where `d` is the dimension of the polyhedron. + + INPUT: + + - ``args`` -- integers (optional); specify an entry of the + flag-f-vector; must be an increasing sequence of integers + + OUTPUT: + + - a dictionary, if no arguments were given + + - an Integer, if arguments were given + + EXAMPLES: + + Obtain the entire flag-f-vector:: + + sage: P = polytopes.twenty_four_cell() + sage: P.flag_f_vector() + {(-1,): 1, + (0,): 24, + (0, 1): 192, + (0, 1, 2): 576, + (0, 1, 2, 3): 1152, + (0, 1, 3): 576, + (0, 2): 288, + (0, 2, 3): 576, + (0, 3): 144, + (1,): 96, + (1, 2): 288, + (1, 2, 3): 576, + (1, 3): 288, + (2,): 96, + (2, 3): 192, + (3,): 24, + (4,): 1} + + Specify an entry:: + + sage: P.flag_f_vector(0,3) + 144 + sage: P.flag_f_vector(2) + 96 + + Leading ``-1`` and trailing entry of dimension are allowed:: + + sage: P.flag_f_vector(-1,0,3) + 144 + sage: P.flag_f_vector(-1,0,3,4) + 144 + + One can get the number of trivial faces:: + + sage: P.flag_f_vector(-1) + 1 + sage: P.flag_f_vector(4) + 1 + + Polyhedra with lines, have ``0`` entries accordingly:: + + sage: P = (Polyhedron(lines=[[1]]) * polytopes.cross_polytope(3)) + sage: P.flag_f_vector() + {(-1,): 1, + (0, 1): 0, + (0, 1, 2): 0, + (0, 1, 3): 0, + (0, 2): 0, + (0, 2, 3): 0, + (0, 3): 0, + (0,): 0, + (1, 2): 24, + (1, 2, 3): 48, + (1, 3): 24, + (1,): 6, + (2, 3): 24, + (2,): 12, + (3,): 8, + 4: 1} + + If the arguments are not stricly increasing or out of range, a key error is raised:: + + sage: P.flag_f_vector(-1,0,3,6) + Traceback (most recent call last): + ... + KeyError: (0, 3, 6) + sage: P.flag_f_vector(-1,3,0) + Traceback (most recent call last): + ... + KeyError: (3, 0) + """ + flag = self._flag_f_vector() + if len(args) == 0: + return flag + elif len(args) == 1: + return flag[(args[0],)] + else: + dim = self.dimension() + if args[0] == -1: + args = args[1:] + if args[-1] == dim: + args = args[:-1] + return flag[tuple(args)] + + @cached_method(do_pickle=True) + def _flag_f_vector(self): + r""" + Return the flag-f-vector. + + See :meth:`flag_f_vector`. + + TESTS:: + + sage: polytopes.hypercube(4)._flag_f_vector() + {(-1,): 1, + (0,): 16, + (0, 1): 64, + (0, 1, 2): 192, + (0, 1, 2, 3): 384, + (0, 1, 3): 192, + (0, 2): 96, + (0, 2, 3): 192, + (0, 3): 64, + (1,): 32, + (1, 2): 96, + (1, 2, 3): 192, + (1, 3): 96, + (2,): 24, + (2, 3): 48, + (3,): 8, + (4,): 1} + """ + return self.combinatorial_polyhedron()._flag_f_vector() + 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 @@ -5894,37 +6867,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 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) + The graph of an unbounded polyhedron + is the graph of the bounded complex:: - n = len(vertices) + sage: open_triangle = Polyhedron(vertices=[[1,0], [0,1]], + ....: rays =[[1,1]]) + sage: open_triangle.vertex_graph() + Graph on 2 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)) + The graph of a polyhedron with lines has no vertices:: - 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 @@ -6053,15 +7011,73 @@ def polar(self, in_affine_span=False): Traceback (most recent call last): ... ValueError: not full-dimensional; try with 'in_affine_span=True' + + Check that the double description is set up correctly:: + + sage: P = Polyhedron([[1,0],[0,1],[-1,-1]], backend='field') + sage: Q = P.change_ring(QQ, backend='ppl') + sage: P.polar() == Q.polar() + True + + sage: P = polytopes.simplex(4, backend='field') + sage: Q = P.change_ring(QQ, backend='ppl') + sage: P.polar(in_affine_span=True) == Q.polar(in_affine_span=True) + True + + Check that it works, even when the equations are not orthogonal to each other:: + + sage: P = polytopes.cube()*Polyhedron([[0,0,0]]) + sage: P = P.change_ring(QQ) + + sage: from sage.geometry.polyhedron.backend_field import Polyhedron_field + sage: from sage.geometry.polyhedron.parent import Polyhedra_field + sage: parent = Polyhedra_field(QQ, 6, 'field') + sage: equations = [[0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 0, -1, 1, -1], [0, 0, 0, 0, 1, -1, -1]] + sage: Q = Polyhedron_field(parent, [P.vertices(), [], []], [P.inequalities(), equations], + ....: Vrep_minimal=True, Hrep_minimal=True) + sage: Q == P + True + sage: Q.polar(in_affine_span=True) == P.polar(in_affine_span=True) + True """ if not self.is_compact(): raise ValueError("not a polytope") if not in_affine_span and not self.dim() == self.ambient_dim(): raise ValueError("not full-dimensional; try with 'in_affine_span=True'") - verts = [list(self.center() - v.vector()) for v in self.vertex_generator()] - parent = self.parent().base_extend(self.center().parent()) - return parent.element_class(parent, None, [[[1] + list(v) for v in verts], self.equations()]) + t_Vrep, t_Hrep, parent = self._translation_double_description(-self.center()) + t_verts = t_Vrep[0] + t_ieqs = t_Hrep[0] + t_eqns = t_Hrep[1] + + new_ieqs = ((1,) + tuple(-v) for v in t_verts) + if self.n_vertices() == 1: + new_verts = self.vertices() + elif not self.n_equations(): + new_verts = ((-h/h[0])[1:] for h in t_ieqs) + else: + # Transform the equations such that the normals are pairwise orthogonal. + t_eqns = list(t_eqns) + for i,h in enumerate(t_eqns): + for h1 in t_eqns[:i]: + a = h[1:]*h1[1:] + if a: + b = h1[1:]*h1[1:] + t_eqns[i] = b*h - a*h1 + + def move_vertex_to_subspace(vertex): + for h in t_eqns: + offset = vertex*h[1:]+h[0] + vertex = vertex-h[1:]*offset/(h[1:]*h[1:]) + return vertex + + new_verts = (move_vertex_to_subspace((-h/h[0])[1:]) for h in t_ieqs) + + pref_rep = 'Hrep' if self.n_vertices() <= self.n_inequalities() else 'Vrep' + + return parent.element_class(parent, [new_verts, [], []], + [new_ieqs, t_eqns], + Vrep_minimal=True, Hrep_minimal=True, pref_rep=pref_rep) def is_self_dual(self): r""" @@ -6229,7 +7245,7 @@ def one_point_suspension(self, vertex): It works with a polyhedral face as well:: - sage: vv = cube.faces(0)[0] + sage: vv = cube.faces(0)[1] sage: ops_cube2 = cube.one_point_suspension(vv) sage: ops_cube == ops_cube2 True @@ -6405,13 +7421,13 @@ def schlegel_projection(self, projection_dir=None, height=1.1): sage: schlegel_edge_indices = sch_proj.lines sage: schlegel_edges = [sch_proj.coordinates_of(x) for x in schlegel_edge_indices] sage: len([x for x in schlegel_edges if x[0][0] > 0]) - 4 + 5 """ proj = self.projection() if projection_dir is None: vertices = self.vertices() facet = self.Hrepresentation(0) - f0 = [ v.index() for v in facet.incident() ] + f0 = [v.index() for v in facet.incident()] projection_dir = [sum([vertices[f0[i]][j]/len(f0) for i in range(len(f0))]) for j in range(self.ambient_dim())] return proj.schlegel(projection_direction=projection_dir, height=height) @@ -6427,11 +7443,11 @@ def _volume_lrs(self, verbose=False): EXAMPLES:: - sage: polytopes.hypercube(3)._volume_lrs() #optional - lrslib + sage: polytopes.hypercube(3)._volume_lrs() # optional - lrslib 8.0 - sage: (polytopes.hypercube(3)*2)._volume_lrs() #optional - lrslib + sage: (polytopes.hypercube(3)*2)._volume_lrs() # optional - lrslib 64.0 - sage: polytopes.twenty_four_cell()._volume_lrs() #optional - lrslib + sage: polytopes.twenty_four_cell()._volume_lrs() # optional - lrslib 2.0 REFERENCES: @@ -6450,7 +7466,8 @@ def _volume_lrs(self, verbose=False): in_file = open(in_filename, 'w') in_file.write(in_str) in_file.close() - if verbose: print(in_str) + if verbose: + print(in_str) lrs_procs = Popen(['lrs', in_filename], stdin=PIPE, stdout=PIPE, stderr=PIPE) @@ -6497,30 +7514,30 @@ def _volume_latte(self, verbose=False, algorithm='triangulate', **kwargs): EXAMPLES:: - sage: polytopes.hypercube(3)._volume_latte() #optional - latte_int + sage: polytopes.hypercube(3)._volume_latte() # optional - latte_int 8 - sage: (polytopes.hypercube(3)*2)._volume_latte() #optional - latte_int + sage: (polytopes.hypercube(3)*2)._volume_latte() # optional - latte_int 64 - sage: polytopes.twenty_four_cell()._volume_latte() #optional - latte_int + sage: polytopes.twenty_four_cell()._volume_latte() # optional - latte_int 2 - sage: polytopes.cuboctahedron()._volume_latte() #optional - latte_int + sage: polytopes.cuboctahedron()._volume_latte() # optional - latte_int 20/3 TESTS: Testing triangulate algorithm:: - sage: polytopes.cuboctahedron()._volume_latte(algorithm='triangulate') #optional - latte_int + sage: polytopes.cuboctahedron()._volume_latte(algorithm='triangulate') # optional - latte_int 20/3 Testing cone decomposition algorithm:: - sage: polytopes.cuboctahedron()._volume_latte(algorithm='cone-decompose') #optional - latte_int + sage: polytopes.cuboctahedron()._volume_latte(algorithm='cone-decompose') # optional - latte_int 20/3 Testing raw output:: - sage: polytopes.cuboctahedron()._volume_latte(raw_output=True) #optional - latte_int + sage: polytopes.cuboctahedron()._volume_latte(raw_output=True) # optional - latte_int '20/3' Testing inexact rings:: @@ -6567,7 +7584,7 @@ def _volume_normaliz(self, measure='induced'): """ raise TypeError("the backend should be normaliz") - @cached_method + @cached_method(do_pickle=True) def volume(self, measure='ambient', engine='auto', **kwds): """ Return the volume of the polytope. @@ -6613,10 +7630,10 @@ def volume(self, measure='ambient', engine='auto', **kwds): reasons, Sage casts lrs's exact answer to a float:: sage: I3 = polytopes.hypercube(3) - sage: I3.volume(engine='lrs') #optional - lrslib + sage: I3.volume(engine='lrs') # optional - lrslib 8.0 sage: C24 = polytopes.twenty_four_cell() - sage: C24.volume(engine='lrs') #optional - lrslib + sage: C24.volume(engine='lrs') # optional - lrslib 2.0 If the base ring is exact, the answer is exact:: @@ -6685,7 +7702,7 @@ def volume(self, measure='ambient', engine='auto', **kwds): sage: Dinexact = polytopes.dodecahedron(exact=False) sage: w = Dinexact.faces(2)[2].as_polyhedron().volume(measure='induced', engine='internal'); RDF(w) # abs tol 1e-9 - 1.534062710738235 + 1.5340627082974878 sage: [polytopes.simplex(d).volume(measure='induced') for d in range(1,5)] == [sqrt(d+1)/factorial(d) for d in range(1,5)] True @@ -6739,6 +7756,17 @@ def volume(self, measure='ambient', engine='auto', **kwds): The empty polyhedron in ZZ^0 sage: P.volume() 0 + + TESTS: + + The cache of the volume is being pickled:: + + sage: P = polytopes.cube() + sage: P.volume() + 8 + sage: Q = loads(dumps(P)) + sage: Q.volume.is_in_cache() + True """ from sage.features import FeatureNotPresentError, PythonModule if measure == 'induced_rational' and engine not in ['auto', 'latte', 'normaliz']: @@ -6771,6 +7799,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() @@ -6785,7 +7816,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() @@ -6801,9 +7832,9 @@ def volume(self, measure='ambient', engine='auto', **kwds): if engine == 'normaliz': return self._volume_normaliz(measure='euclidean') # use an orthogonal transformation, which preserves volume up to a factor provided by the transformation matrix - A, b = self.affine_hull(orthogonal=True, as_affine_map=True) + A, b = self.affine_hull_projection(orthogonal=True, as_affine_map=True) Adet = (A.matrix().transpose() * A.matrix()).det() - return self.affine_hull(orthogonal=True).volume(measure='ambient', engine=engine, **kwds) / sqrt(Adet) + return self.affine_hull_projection(orthogonal=True).volume(measure='ambient', engine=engine, **kwds) / sqrt(Adet) elif measure == 'induced_rational': # if the polyhedron is unbounded, return infinity if not self.is_compact(): @@ -7109,6 +8140,9 @@ def is_simplex(self): r""" Return whether the polyhedron is a simplex. + A simplex is a bounded polyhedron with `d+1` vertices, where + `d` is the dimension. + EXAMPLES:: sage: Polyhedron([(0,0,0), (1,0,0), (0,1,0)]).is_simplex() @@ -7122,11 +8156,12 @@ def is_simplex(self): def neighborliness(self): r""" - Returns the largest ``k``, such that the polyhedron is ``k``-neighborly. + Return the largest ``k``, such that the polyhedron is ``k``-neighborly. - In case of the ``d``-dimensional simplex, it returns ``d + 1``. + A polyhedron is `k`-neighborly if every set of `n` vertices forms a face + for `n` up to `k`. - See :wikipedia:`Neighborly_polytope` + In case of the `d`-dimensional simplex, it returns `d + 1`. .. SEEALSO:: @@ -7161,22 +8196,17 @@ def neighborliness(self): [6, 2, 2, 2] """ - if self.is_simplex(): - return self.dim() + 1 - else: - k = 1 - while len(self.faces(k)) == binomial(self.n_vertices(), k + 1): - k += 1 - return k + return self.combinatorial_polyhedron().neighborliness() def is_neighborly(self, k=None): r""" Return whether the polyhedron is neighborly. - If the input ``k`` is provided then return whether the polyhedron is ``k``-neighborly - - See :wikipedia:`Neighborly_polytope` + If the input ``k`` is provided, then return whether the polyhedron is ``k``-neighborly + A polyhedron is neighborly if every set of `n` vertices forms a face + for `n` up to floor of half the dimension of the polyhedron. + It is `k`-neighborly if this is true for `n` up to `k`. INPUT: @@ -7186,7 +8216,7 @@ def is_neighborly(self, k=None): OUTPUT: - - ``True`` if the every set of up to ``k`` vertices forms a face, + - ``True`` if every set of up to ``k`` vertices forms a face, - ``False`` otherwise .. SEEALSO:: @@ -7216,10 +8246,7 @@ def is_neighborly(self, k=None): [True, True, True] """ - if k is None: - k = self.dim() // 2 - return all(len(self.faces(i)) == binomial(self.n_vertices(), i + 1) - for i in range(1, k)) + return self.combinatorial_polyhedron().is_neighborly() @cached_method def is_lattice_polytope(self): @@ -7370,6 +8397,111 @@ def _integral_points_PALP(self): return list(lp.points()) return [p for p in lp.points() if self.contains(p)] + @cached_method(do_pickle=True) + def h_star_vector(self): + r""" + Return the `h^*`-vector of the lattice polytope. + + The `h^*`-vector records the coefficients of the polynomial in the + numerator of the Ehrhart series of a lattice polytope. + + INPUT: + + - ``self`` -- A lattice polytope. + + OUTPUT: + + A list whose entries give the `h^*`-vector. + + .. NOTE: + + The backend of ``self`` should be ``'normaliz'``. + This function depends on Normaliz (i.e. the ``'pynormaliz'`` optional + package). See the Normaliz documentation for further details. + + EXAMPLES: + + The `h^*`-vector of a unimodular simplex S (a simplex with + volume = `\frac{1}{dim(S)!}`) is always 1. Here we test this on + simplices up to dimension 3:: + + sage: s1 = polytopes.simplex(1,backend='normaliz') # optional - pynormaliz + sage: s2 = polytopes.simplex(2,backend='normaliz') # optional - pynormaliz + sage: s3 = polytopes.simplex(3,backend='normaliz') # optional - pynormaliz + sage: [s1.h_star_vector(),s2.h_star_vector(),s3.h_star_vector()] # optional - pynormaliz + [[1], [1], [1]] + + For a less trivial example, we compute the `h^*`-vector of the + `0/1`-cube, which has the Eulerian numbers `(3,i)` for `i \in [0,2]` + as an `h^*`-vector:: + + sage: cube = polytopes.cube(intervals='zero_one', backend='normaliz') # optional - pynormaliz + sage: cube.h_star_vector() # optional - pynormaliz + [1, 4, 1] + sage: from sage.combinat.combinat import eulerian_number + sage: [eulerian_number(3,i) for i in range(3)] + [1, 4, 1] + + TESTS:: + + sage: s3 = polytopes.simplex(3) + sage: s3.h_star_vector() + Traceback (most recent call last): + ... + TypeError: The backend of self must be normaliz + + sage: t = Polyhedron(vertices=[[0],[1/2]]) + sage: t.h_star_vector() + Traceback (most recent call last): + ... + TypeError: The h_star vector is only defined for lattice polytopes + + sage: t2 = Polyhedron(vertices=[[AA(sqrt(2))],[1/2]]) + sage: t2.h_star_vector() + Traceback (most recent call last): + ... + TypeError: The h_star vector is only defined for lattice polytopes + + Check that the `h^*`-vector is pickled:: + + sage: new_cube = loads(dumps(cube)) # optional - pynormaliz + sage: new_cube.h_star_vector.is_in_cache() # optional - pynormaliz + True + """ + if self.is_empty(): + return 0 + if not self.is_lattice_polytope(): + raise TypeError('The h_star vector is only defined for lattice polytopes') + if not self.backend() == 'normaliz': + raise TypeError('The backend of self must be normaliz') + return self._h_star_vector_normaliz() + + def _h_star_vector_normaliz(self): + r""" + Return the `h^*`-vector of a lattice polytope with backend = 'normaliz'. + + INPUT: + + - ``self`` -- A lattice polytope. + + OUTPUT: + + The `h^*`-vector as a list. + + .. NOTE: + + The backend of ``self`` should be ``'normaliz'``. + + TESTS:: + + sage: s3 = polytopes.simplex(3) + sage: s3._h_star_vector_normaliz() + Traceback (most recent call last): + ... + TypeError: the backend should be normaliz + """ + raise TypeError("the backend should be normaliz") + @cached_method def bounding_box(self, integral=False, integral_hull=False): r""" @@ -7418,7 +8550,7 @@ def bounding_box(self, integral=False, integral_hull=False): if self.n_vertices() == 0: raise ValueError("empty polytope is not allowed") for i in range(self.ambient_dim()): - coords = [ v[i] for v in self.vertex_generator() ] + coords = [v[i] for v in self.vertex_generator()] max_coord = max(coords) min_coord = min(coords) if integral_hull: @@ -7530,8 +8662,8 @@ def integral_points(self, threshold=100000): A case where rounding in the right direction goes a long way:: - sage: P = 1/10*polytopes.hypercube(14) # long time - sage: P.integral_points() # long time + sage: P = 1/10*polytopes.hypercube(14, backend='field') + sage: P.integral_points() ((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),) Finally, the 3-d reflexive polytope number 4078:: @@ -7610,7 +8742,7 @@ def integral_points(self, threshold=100000): triangulation = self.triangulate() points = set() for simplex in triangulation: - triang_vertices = [ self.Vrepresentation(i) for i in simplex ] + triang_vertices = [self.Vrepresentation(i) for i in simplex] new_points = simplex_points(triang_vertices) for p in new_points: p.set_immutable() @@ -7819,12 +8951,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:: @@ -8143,6 +9274,7 @@ def rational_approximation(c): return c else: c_list = [] + def rational_approximation(c): # Implementation detail: Return unique integer if two # c-values are the same up to machine precision. But @@ -8359,14 +9491,38 @@ def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'): else: return self.face_lattice().is_isomorphic(other.face_lattice()) - def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, extend=False): + def _test_is_combinatorially_isomorphic(self, tester=None, **options): """ - Return the affine hull. + Run tests on the method :meth:`.is_combinatorially_isomorphic`. + + TESTS:: + + sage: polytopes.cross_polytope(3)._test_is_combinatorially_isomorphic() + """ + if tester is None: + tester = self._tester(**options) + + if not self.is_compact(): + with tester.assertRaises(AssertionError): + self.is_combinatorially_isomorphic(self) + return + + tester.assertTrue(self.is_combinatorially_isomorphic(ZZ(4)*self)) + if self.n_vertices(): + tester.assertTrue(self.is_combinatorially_isomorphic(self + self.center())) + + if self.n_vertices() < 20: + tester.assertTrue(self.is_combinatorially_isomorphic(ZZ(4)*self, algorithm='face_lattice')) + if self.n_vertices(): + tester.assertTrue(self.is_combinatorially_isomorphic(self + self.center(), algorithm='face_lattice')) + + def affine_hull_projection(self, as_affine_map=False, orthogonal=False, orthonormal=False, extend=False): + """ + Return the polyhedron projected into its affine hull. Each polyhedron is contained in some smallest affine subspace - (possibly the entire ambient space). The affine hull is the - same polyhedron but thought of as a full-dimensional - polyhedron in this subspace. We provide a projection of the ambient + (possibly the entire ambient space) -- its affine hull. + We provide a projection of the ambient space of the polyhedron to Euclidian space of dimension of the polyhedron. Then the image of the polyhedron under this projection (or, depending on the parameter ``as_affine_map``, @@ -8395,46 +9551,45 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, OUTPUT: - A full-dimensional polyhedron or a linear transformation, + A full-dimensional polyhedron or an affine transformation, depending on the parameter ``as_affine_map``. .. TODO:: - make the parameters ``orthogonal`` and ``orthonormal`` work with unbounded polyhedra. - - allow to return ``as_affine_map=True`` for default setting EXAMPLES:: sage: triangle = Polyhedron([(1,0,0), (0,1,0), (0,0,1)]); triangle A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices - sage: triangle.affine_hull() + sage: triangle.affine_hull_projection() A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices sage: half3d = Polyhedron(vertices=[(3,2,1)], rays=[(1,0,0)]) - sage: half3d.affine_hull().Vrepresentation() + sage: half3d.affine_hull_projection().Vrepresentation() (A ray in the direction (1), A vertex at (3)) The resulting affine hulls depend on the parameter ``orthogonal`` and ``orthonormal``:: sage: L = Polyhedron([[1,0],[0,1]]); L A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices - sage: A = L.affine_hull(); A + sage: A = L.affine_hull_projection(); A A 1-dimensional polyhedron in ZZ^1 defined as the convex hull of 2 vertices sage: A.vertices() (A vertex at (0), A vertex at (1)) - sage: A = L.affine_hull(orthogonal=True); A + sage: A = L.affine_hull_projection(orthogonal=True); A A 1-dimensional polyhedron in QQ^1 defined as the convex hull of 2 vertices sage: A.vertices() (A vertex at (0), A vertex at (2)) - sage: A = L.affine_hull(orthonormal=True) + sage: A = L.affine_hull_projection(orthonormal=True) Traceback (most recent call last): ... ValueError: the base ring needs to be extended; try with "extend=True" - sage: A = L.affine_hull(orthonormal=True, extend=True); A + sage: A = L.affine_hull_projection(orthonormal=True, extend=True); A A 1-dimensional polyhedron in AA^1 defined as the convex hull of 2 vertices sage: A.vertices() - (A vertex at (0), A vertex at (1.414213562373095?)) + (A vertex at (1.414213562373095?), A vertex at (0.?e-18)) More generally:: @@ -8445,41 +9600,42 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, A vertex at (0, 0, 1, 0), A vertex at (0, 1, 0, 0), A vertex at (1, 0, 0, 0)) - sage: A = S.affine_hull(); A + sage: A = S.affine_hull_projection(); A A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices sage: A.vertices() (A vertex at (0, 0, 0), A vertex at (0, 0, 1), A vertex at (0, 1, 0), A vertex at (1, 0, 0)) - sage: A = S.affine_hull(orthogonal=True); A + sage: A = S.affine_hull_projection(orthogonal=True); A A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 4 vertices sage: A.vertices() (A vertex at (0, 0, 0), A vertex at (2, 0, 0), A vertex at (1, 3/2, 0), A vertex at (1, 1/2, 4/3)) - sage: A = S.affine_hull(orthonormal=True, extend=True); A + sage: A = S.affine_hull_projection(orthonormal=True, extend=True); A A 3-dimensional polyhedron in AA^3 defined as the convex hull of 4 vertices sage: A.vertices() - (A vertex at (0, 0, 0), - A vertex at (1.414213562373095?, 0, 0), - A vertex at (0.7071067811865475?, 1.224744871391589?, 0), - A vertex at (0.7071067811865475?, 0.4082482904638630?, 1.154700538379252?)) + (A vertex at (0.7071067811865475?, 0.4082482904638630?, 1.154700538379252?), + A vertex at (0.7071067811865475?, 1.224744871391589?, 0.?e-18), + A vertex at (1.414213562373095?, 0.?e-18, 0.?e-18), + A vertex at (0.?e-18, 0.?e-18, 0.?e-18)) + More examples with the ``orthonormal`` parameter:: sage: P = polytopes.permutahedron(3); P A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 6 vertices - sage: set([F.as_polyhedron().affine_hull(orthonormal=True, extend=True).volume() for F in P.affine_hull().faces(1)]) == {1, sqrt(AA(2))} + sage: set([F.as_polyhedron().affine_hull_projection(orthonormal=True, extend=True).volume() for F in P.affine_hull_projection().faces(1)]) == {1, sqrt(AA(2))} True - sage: set([F.as_polyhedron().affine_hull(orthonormal=True, extend=True).volume() for F in P.affine_hull(orthonormal=True, extend=True).faces(1)]) == {sqrt(AA(2))} + sage: set([F.as_polyhedron().affine_hull_projection(orthonormal=True, extend=True).volume() for F in P.affine_hull_projection(orthonormal=True, extend=True).faces(1)]) == {sqrt(AA(2))} True sage: D = polytopes.dodecahedron() sage: F = D.faces(2)[0].as_polyhedron() - sage: F.affine_hull(orthogonal=True) + sage: F.affine_hull_projection(orthogonal=True) A 2-dimensional polyhedron in (Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^2 defined as the convex hull of 5 vertices - sage: F.affine_hull(orthonormal=True, extend=True) + sage: F.affine_hull_projection(orthonormal=True, extend=True) A 2-dimensional polyhedron in AA^2 defined as the convex hull of 5 vertices sage: K. = QuadraticField(2) sage: P = Polyhedron([2*[K.zero()],2*[sqrt2]]) @@ -8488,7 +9644,7 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, A 1-dimensional polyhedron in (Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.414213562373095?)^2 defined as the convex hull of 2 vertices sage: P.vertices() (A vertex at (0, 0), A vertex at (sqrt2, sqrt2)) - sage: A = P.affine_hull(orthonormal=True); A + sage: A = P.affine_hull_projection(orthonormal=True); A A 1-dimensional polyhedron in (Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.414213562373095?)^1 defined as the convex hull of 2 vertices sage: A.vertices() (A vertex at (0), A vertex at (2)) @@ -8497,11 +9653,11 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, A 1-dimensional polyhedron in (Number Field in sqrt3 with defining polynomial x^2 - 3 with sqrt3 = 1.732050807568878?)^2 defined as the convex hull of 2 vertices sage: P.vertices() (A vertex at (0, 0), A vertex at (sqrt3, sqrt3)) - sage: A = P.affine_hull(orthonormal=True) + sage: A = P.affine_hull_projection(orthonormal=True) Traceback (most recent call last): ... ValueError: the base ring needs to be extended; try with "extend=True" - sage: A = P.affine_hull(orthonormal=True, extend=True); A + sage: A = P.affine_hull_projection(orthonormal=True, extend=True); A A 1-dimensional polyhedron in AA^1 defined as the convex hull of 2 vertices sage: A.vertices() (A vertex at (0), A vertex at (2.449489742783178?)) @@ -8512,18 +9668,18 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, The affine hull is combinatorially equivalent to the input:: - sage: P.is_combinatorially_isomorphic(P.affine_hull()) + sage: P.is_combinatorially_isomorphic(P.affine_hull_projection()) True - sage: P.is_combinatorially_isomorphic(P.affine_hull(orthogonal=True)) + sage: P.is_combinatorially_isomorphic(P.affine_hull_projection(orthogonal=True)) True - sage: P.is_combinatorially_isomorphic(P.affine_hull(orthonormal=True, extend=True)) + sage: P.is_combinatorially_isomorphic(P.affine_hull_projection(orthonormal=True, extend=True)) True The ``orthonormal=True`` parameter preserves volumes; it provides an isometric copy of the polyhedron:: sage: Pentagon = polytopes.dodecahedron().faces(2)[0].as_polyhedron() - sage: P = Pentagon.affine_hull(orthonormal=True, extend=True) + sage: P = Pentagon.affine_hull_projection(orthonormal=True, extend=True) sage: _, c= P.is_inscribed(certificate=True) sage: c (0.4721359549995794?, 0.6498393924658126?) @@ -8544,9 +9700,9 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, affine transformation times its transpose:: sage: Pentagon = polytopes.dodecahedron().faces(2)[0].as_polyhedron() - sage: Pnormal = Pentagon.affine_hull(orthonormal=True, extend=True) - sage: Pgonal = Pentagon.affine_hull(orthogonal=True) - sage: A, b = Pentagon.affine_hull(orthogonal=True, as_affine_map=True) + sage: Pnormal = Pentagon.affine_hull_projection(orthonormal=True, extend=True) + sage: Pgonal = Pentagon.affine_hull_projection(orthogonal=True) + sage: A, b = Pentagon.affine_hull_projection(orthogonal=True, as_affine_map=True) sage: Adet = (A.matrix().transpose()*A.matrix()).det() sage: Pnormal.volume() 1.53406271079097? @@ -8560,14 +9716,13 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, An other example with ``as_affine_map=True``:: sage: P = polytopes.permutahedron(4) - sage: A, b = P.affine_hull(orthonormal=True, as_affine_map=True, extend=True) - sage: Q = P.affine_hull(orthonormal=True, extend=True) + sage: A, b = P.affine_hull_projection(orthonormal=True, as_affine_map=True, extend=True) + sage: Q = P.affine_hull_projection(orthonormal=True, extend=True) sage: Q.center() - (0.7071067811865475?, 1.224744871391589?, 1.732050807568878?) + (0.7071067811865475?, 0.7071067811865475?, 2.000000000000000?) sage: A(P.center()) + b == Q.center() True - For unbounded, non full-dimensional polyhedra, the ``orthogonal=True`` and ``orthonormal=True`` is not implemented:: @@ -8577,35 +9732,41 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, False sage: P.is_full_dimensional() False - sage: P.affine_hull(orthogonal=True) + sage: P.affine_hull_projection(orthogonal=True) Traceback (most recent call last): ... NotImplementedError: "orthogonal=True" and "orthonormal=True" work only for compact polyhedra - sage: P.affine_hull(orthonormal=True) + sage: P.affine_hull_projection(orthonormal=True) Traceback (most recent call last): ... NotImplementedError: "orthogonal=True" and "orthonormal=True" work only for compact polyhedra - Setting ``as_affine_map`` to ``True`` only works in combination - with ``orthogonal`` or ``orthonormal`` set to ``True``:: + Setting ``as_affine_map`` to ``True`` + without ``orthogonal`` or ``orthonormal`` set to ``True``:: sage: S = polytopes.simplex() - sage: S.affine_hull(as_affine_map=True) - Traceback (most recent call last): - ... - NotImplementedError: "as_affine_map=True" only works with "orthogonal=True" and "orthonormal=True" + sage: S.affine_hull_projection(as_affine_map=True) + (Vector space morphism represented by the matrix: + [1 0 0] + [0 1 0] + [0 0 1] + [0 0 0] + Domain: Vector space of dimension 4 over Rational Field + Codomain: Vector space of dimension 3 over Rational Field, + (0, 0, 0)) If the polyhedron is full-dimensional, it is returned:: - sage: polytopes.cube().affine_hull() + sage: polytopes.cube().affine_hull_projection() A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices - sage: polytopes.cube().affine_hull(as_affine_map=True) + sage: polytopes.cube().affine_hull_projection(as_affine_map=True) (Vector space morphism represented by the matrix: [1 0 0] [0 1 0] [0 0 1] Domain: Vector space of dimension 3 over Rational Field - Codomain: Vector space of dimension 3 over Rational Field, (0, 0, 0)) + Codomain: Vector space of dimension 3 over Rational Field, + (0, 0, 0)) TESTS: @@ -8613,11 +9774,11 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, sage: P = Polyhedron([[7]]); P A 0-dimensional polyhedron in ZZ^1 defined as the convex hull of 1 vertex - sage: P.affine_hull() + sage: P.affine_hull_projection() A 0-dimensional polyhedron in ZZ^0 defined as the convex hull of 1 vertex - sage: P.affine_hull(orthonormal='True') + sage: P.affine_hull_projection(orthonormal='True') A 0-dimensional polyhedron in QQ^0 defined as the convex hull of 1 vertex - sage: P.affine_hull(orthogonal='True') + sage: P.affine_hull_projection(orthogonal='True') A 0-dimensional polyhedron in QQ^0 defined as the convex hull of 1 vertex Check that :trac:`24047` is fixed:: @@ -8625,19 +9786,45 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, sage: P1 = Polyhedron(vertices=([[-1, 1], [0, -1], [0, 0], [-1, -1]])) sage: P2 = Polyhedron(vertices=[[1, 1], [1, -1], [0, -1], [0, 0]]) sage: P = P1.intersection(P2) - sage: A, b = P.affine_hull(as_affine_map=True, orthonormal=True, extend=True) + sage: A, b = P.affine_hull_projection(as_affine_map=True, orthonormal=True, extend=True) - sage: Polyhedron([(2,3,4)]).affine_hull() + sage: Polyhedron([(2,3,4)]).affine_hull_projection() A 0-dimensional polyhedron in ZZ^0 defined as the convex hull of 1 vertex Check that backend is preserved:: - sage: polytopes.simplex(backend='field').affine_hull().backend() + sage: polytopes.simplex(backend='field').affine_hull_projection().backend() 'field' sage: P = Polyhedron(vertices=[[0,0], [1,0]], backend='field') - sage: P.affine_hull(orthogonal=True, orthonormal=True, extend=True).backend() + sage: P.affine_hull_projection(orthogonal=True, orthonormal=True, extend=True).backend() 'field' + + Check that :trac:`29116` is fixed:: + + sage: V =[ + ....: [1, 0, -1, 0, 0], + ....: [1, 0, 0, -1, 0], + ....: [1, 0, 0, 0, -1], + ....: [1, 0, 0, +1, 0], + ....: [1, 0, 0, 0, +1], + ....: [1, +1, 0, 0, 0] + ....: ] + sage: P = Polyhedron(V) + sage: P.affine_hull_projection() + A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 6 vertices + sage: P.affine_hull_projection(orthonormal=True) + Traceback (most recent call last): + ... + ValueError: the base ring needs to be extended; try with "extend=True" + sage: P.affine_hull_projection(orthonormal=True, extend=True) + A 4-dimensional polyhedron in AA^4 defined as the convex hull of 6 vertices + + ``affine_hull`` is a deprecated alias:: + + sage: _ = P.affine_hull() + doctest:...: DeprecationWarning: affine_hull is deprecated. Please use affine_hull_projection instead. + See https://trac.sagemath.org/29326 for details. """ # handle trivial full-dimensional case if self.ambient_dim() == self.dim(): @@ -8649,13 +9836,11 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, # see TODO if not self.is_compact(): raise NotImplementedError('"orthogonal=True" and "orthonormal=True" work only for compact polyhedra') - # translate 0th vertex to the origin - Q = self.translation(-vector(self.vertices()[0])) - v = next((_ for _ in Q.vertices() if _.vector() == Q.ambient_space().zero()), None) - # finding the zero in Q; checking that Q actually has a vertex zero - assert v.vector() == Q.ambient_space().zero() - # choose as an affine basis the neighbors of the origin vertex in Q - M = matrix(self.base_ring(), self.dim(), self.ambient_dim(), [list(w) for w in itertools.islice(v.neighbors(), self.dim())]) + affine_basis = self.an_affine_basis() + # We implicitly translate the first vertex of the affine basis to zero. + M = matrix(self.base_ring(), self.dim(), self.ambient_dim(), + [v.vector() - affine_basis[0].vector() for v in affine_basis[1:]]) + # Switch base_ring to AA if necessary, # since gram_schmidt needs to be able to take square roots. # Pick orthonormal basis and transform all vertices accordingly @@ -8668,10 +9853,16 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, M = matrix(AA, M) A = M.gram_schmidt(orthonormal=orthonormal)[0] if as_affine_map: - return linear_transformation(A, side='right'), -A*vector(A.base_ring(), self.vertices()[0]) - parent = self.parent().change_ring(A.base_ring(), ambient_dim=self.dim()) - new_vertices = [A*vector(A.base_ring(), w) for w in Q.vertices()] - return parent.element_class(parent, [new_vertices, [], []], None) + return linear_transformation(A, side='right'), -A*vector(A.base_ring(), affine_basis[0]) + + translate_vector = vector(A.base_ring(), affine_basis[0]) + + # Note the order. We compute ``A*self`` and then substract the translation vector. + # ``A*self`` uses the incidence matrix and we avoid recomputation. + # Also, if the new base ring is ``AA``, we want to avoid computing the incidence matrix in that ring. + + # ``convert=True`` takes care of the case, where there might be no coercion (``AA`` and quadratic field). + return self.linear_transformation(A, new_base_ring=A.base_ring()) - A*translate_vector # translate one vertex to the origin v0 = self.vertices()[0].vector() @@ -8686,15 +9877,13 @@ def affine_hull(self, as_affine_map=False, orthogonal=False, orthonormal=False, # Pick subset of coordinates to coordinatize the affine span pivots = matrix(gens).pivots() - def pivot(indexed): - return [indexed[i] for i in pivots] - - vertices = [pivot(_) for _ in self.vertices()] - rays = [pivot(_) for _ in self.rays()] - lines = [pivot(_) for _ in self.lines()] + A = matrix([[1 if j == i else 0 for j in range(self.ambient_dim())] for i in pivots]) if as_affine_map: - raise NotImplementedError('"as_affine_map=True" only works with "orthogonal=True" and "orthonormal=True"') - return Polyhedron(vertices=vertices, rays=rays, lines=lines, base_ring=self.base_ring(), backend=self.backend()) + return linear_transformation(A, side='right'), vector(self.base_ring(), self.dim()) + else: + return A*self + + affine_hull = deprecated_function_alias(29326, affine_hull_projection) def _polymake_init_(self): """ diff --git a/src/sage/geometry/polyhedron/base_QQ.py b/src/sage/geometry/polyhedron/base_QQ.py index 5917c7a262c..917c617ac76 100644 --- a/src/sage/geometry/polyhedron/base_QQ.py +++ b/src/sage/geometry/polyhedron/base_QQ.py @@ -4,7 +4,7 @@ from __future__ import absolute_import from sage.rings.all import QQ -from sage.misc.all import prod +from sage.misc.all import cached_method, prod from .base import Polyhedron_base @@ -200,7 +200,7 @@ def integral_points_count(self, verbose=False, use_Hrepresentation=False, lp.set_min(x[i], a) for i, b in enumerate(box_max): lp.set_max(x[i], b) - p = lp.polyhedron() # this recomputes the double description, which is wasteful + p = lp.polyhedron() # this recomputes the double description, which is wasteful if p.is_empty(): return 0 if p.dimension() == 0: @@ -217,7 +217,8 @@ def integral_points_count(self, verbose=False, use_Hrepresentation=False, verbose=verbose, **kwds) - def ehrhart_polynomial(self,engine=None,variable='t',verbose=False, + @cached_method(do_pickle=True) + def ehrhart_polynomial(self, engine=None, variable='t', verbose=False, dual=None, irrational_primal=None, irrational_all_primal=None, maxdet=None, no_decomposition=None, compute_vertex_cones=None, smith_form=None, dualization=None, triangulation=None, @@ -338,6 +339,17 @@ def ehrhart_polynomial(self,engine=None,variable='t',verbose=False, Traceback (most recent call last): ... TypeError: the polytope has nonintegral vertices, use ehrhart_quasipolynomial with backend 'normaliz' + + TESTS: + + The cache of the Ehrhart polynomial is being pickled:: + + sage: P = polytopes.cube().change_ring(QQ) # optional - latte_int + sage: P.ehrhart_polynomial() # optional - latte_int + 8*t^3 + 12*t^2 + 6*t + 1 + sage: Q = loads(dumps(P)) # optional - latte_int + sage: Q.ehrhart_polynomial.is_in_cache() # optional - latte_int + True """ # check if ``self`` is compact and has vertices in ZZ if self.is_empty(): @@ -370,6 +382,7 @@ def ehrhart_polynomial(self,engine=None,variable='t',verbose=False, else: raise ValueError("engine must be 'latte' or 'normaliz'") + @cached_method(do_pickle=True) def ehrhart_quasipolynomial(self, variable='t', engine=None, verbose=False, dual=None, irrational_primal=None, irrational_all_primal=None, maxdet=None, no_decomposition=None, compute_vertex_cones=None, @@ -397,10 +410,11 @@ def ehrhart_quasipolynomial(self, variable='t', engine=None, verbose=False, - ``engine`` -- string; The backend to use. Allowed values are: * ``None`` (default); When no input is given the Ehrhart polynomial - is computed using LattE Integrale (optional) - * ``'latte'``; use LattE integrale program (optional) - * ``'normaliz'``; use Normaliz program (optional package pynormaliz). - The backend of ``self`` must be set to 'normaliz'. + is computed using Normaliz (optional) + * ``'latte'``; use LattE Integrale program (requires optional package + 'latte_int') + * ``'normaliz'``; use the Normaliz program (requires optional package + 'pynormaliz'). The backend of ``self`` must be set to 'normaliz'. - When the ``engine`` is 'latte', the additional input values are: @@ -514,6 +528,24 @@ def ehrhart_quasipolynomial(self, variable='t', engine=None, verbose=False, 7/2*t^3 + 2*t^2 - 1/2*t + 1 sage: simplex.ehrhart_polynomial() # optional - pynormaliz latte_int 7/2*t^3 + 2*t^2 - 1/2*t + 1 + + TESTS: + + The cache of the Ehrhart quasipolynomial is being pickled:: + + sage: P = polytopes.cuboctahedron(backend='normaliz')/2 # optional - pynormaliz + sage: P.ehrhart_quasipolynomial() # optional - pynormaliz + (5/6*t^3 + 2*t^2 + 5/3*t + 1, 5/6*t^3 + 1/2*t^2 + 1/6*t - 1/2) + sage: Q = loads(dumps(P)) # optional - pynormaliz + sage: Q.ehrhart_quasipolynomial.is_in_cache() # optional - pynormaliz + True + + sage: P = polytopes.cuboctahedron().change_ring(QQ) # optional - latte_int + sage: P.ehrhart_quasipolynomial(engine='latte') # optional - latte_int + 20/3*t^3 + 8*t^2 + 10/3*t + 1 + sage: Q = loads(dumps(P)) # optional - latte_int + sage: Q.ehrhart_quasipolynomial.is_in_cache(engine='latte') # optional - latte_int + True """ if self.is_empty(): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing diff --git a/src/sage/geometry/polyhedron/base_ZZ.py b/src/sage/geometry/polyhedron/base_ZZ.py index 9de5609628d..5f346963bbe 100644 --- a/src/sage/geometry/polyhedron/base_ZZ.py +++ b/src/sage/geometry/polyhedron/base_ZZ.py @@ -18,7 +18,6 @@ from sage.modules.free_module_element import vector from .base_QQ import Polyhedron_QQ from sage.arith.all import gcd -from .constructor import Polyhedron ######################################################################### @@ -290,7 +289,7 @@ def _ehrhart_polynomial_normaliz(self, variable='t'): """ raise TypeError("The polyhedron's backend should be 'normaliz'") - @cached_method + @cached_method(do_pickle=True) def ehrhart_polynomial(self, engine=None, variable='t', verbose=False, dual=None, irrational_primal=None, irrational_all_primal=None, maxdet=None, no_decomposition=None, compute_vertex_cones=None, smith_form=None, @@ -439,6 +438,17 @@ def ehrhart_polynomial(self, engine=None, variable='t', verbose=False, dual=None Traceback (most recent call last): ... ValueError: Ehrhart polynomial only defined for compact polyhedra + + TESTS: + + The cache of the Ehrhart polynomial is being pickled:: + + sage: P = polytopes.cube() + sage: P.ehrhart_polynomial() # optional - latte_int + 8*t^3 + 12*t^2 + 6*t + 1 + sage: Q = loads(dumps(P)) + sage: Q.ehrhart_polynomial.is_in_cache() # optional - latte_int + True """ if self.is_empty(): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -502,24 +512,66 @@ def polar(self): if not self.has_IP_property(): raise ValueError('The polytope must have the IP property.') - vertices = [ ieq.A()/ieq.b() for - ieq in self.inequality_generator() ] + vertices = tuple( ieq.A()/ieq.b() for + ieq in self.inequality_generator() ) + + ieqs = ((1,) + tuple(v[:]) for v in self.vertices()) + + pref_rep = 'Hrep' if self.n_vertices() <= self.n_inequalities() else 'Vrep' if all( all(v_i in ZZ for v_i in v) for v in vertices): - return Polyhedron(vertices=vertices, base_ring=ZZ, backend=self.backend()) + parent = self.parent() + vertices = (v.change_ring(ZZ) for v in vertices) else: - return Polyhedron(vertices=vertices, base_ring=QQ, backend=self.backend()) + parent = self.parent().change_ring(QQ) + + return parent.element_class(parent, [vertices, [], []], [ieqs, []], + Vrep_minimal=True, Hrep_minimal=True, pref_rep=pref_rep) @cached_method def is_reflexive(self): - """ + r""" + A lattice polytope is reflexive if it contains the origin in its interior + and its polar with respect to the origin is a lattice polytope. + + Equivalently, it is reflexive if it is of the form `\{x \in \mathbb{R}^d: Ax \leq 1\}` + for some integer matrix `A` and `d` the ambient dimension. + EXAMPLES:: sage: p = Polyhedron(vertices=[(1,0,0),(0,1,0),(0,0,1),(-1,-1,-1)], base_ring=ZZ) sage: p.is_reflexive() True + sage: polytopes.hypercube(4).is_reflexive() + True + sage: p = Polyhedron(vertices=[(1,0), (0,2), (-1,0), (0,-1)], base_ring=ZZ) + sage: p.is_reflexive() + False + sage: p = Polyhedron(vertices=[(1,0), (0,2), (-1,0)], base_ring=ZZ) + sage: p.is_reflexive() + False + + An error is raised, if the polyhedron is not compact:: + + sage: p = Polyhedron(rays=[(1,)], base_ring=ZZ) + sage: p.is_reflexive() + Traceback (most recent call last): + ... + ValueError: the polyhedron is not compact """ - return self.polar().is_lattice_polytope() + if not self.is_compact(): + raise ValueError("the polyhedron is not compact") + + for H in self.Hrepresentation(): + if H.is_equation(): + return False + b = H.b() + if b < 1: + return False + if not all(v_i/b in ZZ for v_i in H.A()): + return False + + return True @cached_method def has_IP_property(self): diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd index c795e9196ac..9d0417dfd7e 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd @@ -15,13 +15,13 @@ cdef class CombinatorialPolyhedron(SageObject): cdef tuple _facet_names # the names of HRep without equalities, if they exist cdef tuple _equalities # stores equalities, given on input (might belong to Hrep) cdef int _dimension # stores dimension, -2 on init - cdef unsigned int _n_Hrepresentation # Hrepr might include equalities - cdef unsigned int _n_Vrepresentation # Vrepr might include rays/lines + cdef unsigned int _n_Hrepresentation # Hrep might include equalities + cdef unsigned int _n_Vrepresentation # Vrep might include rays/lines cdef size_t _n_facets # length Hrep without equalities cdef bint _bounded # ``True`` iff Polyhedron is bounded cdef ListOfFaces _bitrep_facets # facets in bit representation - cdef ListOfFaces _bitrep_Vrepr # vertices in bit representation - cdef ListOfFaces _far_face # a 'face' containing all none-vertices of Vrepr + cdef ListOfFaces _bitrep_Vrep # vertices in bit representation + cdef ListOfFaces _far_face # a 'face' containing all none-vertices of Vrep cdef tuple _far_face_tuple cdef tuple _f_vector @@ -49,10 +49,13 @@ cdef class CombinatorialPolyhedron(SageObject): cdef unsigned int n_Hrepresentation(self) cdef bint is_bounded(self) cdef ListOfFaces bitrep_facets(self) - cdef ListOfFaces bitrep_Vrepr(self) + cdef ListOfFaces bitrep_Vrep(self) cdef ListOfFaces far_face(self) cdef tuple far_face_tuple(self) + # Methods to obtain a different combinatorial polyhedron. + cpdef CombinatorialPolyhedron dual(self) + # Space for edges, ridges, etc. is allocated with ``MemoryAllocators``. # Upon success they are copied to ``_mem_tuple``. # Thus deallocation (at the correct time) is taken care of. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 9e7431935a3..56d6e981f2d 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -11,18 +11,18 @@ the ridges and the face lattice. Terminology used in this module: -- Vrepr -- ``[vertices, rays, lines]`` of the polyhedron. -- Hrepr -- inequalities and equalities of the polyhedron. +- Vrep -- ``[vertices, rays, lines]`` of the polyhedron. +- Hrep -- inequalities and equalities of the polyhedron. - Facets -- facets of the polyhedron. -- Vrepresentation -- represents a face by the list of Vrepr it contains. -- Hrepresentation -- represents a face by a list of Hrepr it is contained in. +- Vrepresentation -- represents a face by the list of Vrep it contains. +- Hrepresentation -- represents a face by a list of Hrep it is contained in. - bit representation -- represents incidences as ``uint64_t``-array, where each bit represents one incidence. There might be trailing zeros, to fit alignment requirements. In most instances, faces are represented by the bit representation, where each bit corresponds to - a Vrepr or facet. Thus a bit representation can either be - a Vrepr or facet representation depending on context. + a Vrep or facet. Thus a bit representation can either be + a Vrep or facet representation depending on context. EXAMPLES: @@ -35,16 +35,16 @@ Construction:: Obtaining edges and ridges:: sage: C.edges()[:2] - ((A vertex at (1, 1, 1, -1), A vertex at (1, 1, 1, 1)), - (A vertex at (1, 1, -1, 1), A vertex at (1, 1, 1, 1))) + ((A vertex at (-1, -1, -1, 1), A vertex at (-1, -1, -1, -1)), + (A vertex at (-1, 1, -1, -1), A vertex at (-1, -1, -1, -1))) sage: C.edges(names=False)[:2] - ((14, 15), (13, 15)) + ((14, 15), (10, 15)) sage: C.ridges()[:2] ((An inequality (0, 0, 1, 0) x + 1 >= 0, - An inequality (0, 0, 0, 1) x + 1 >= 0), - (An inequality (0, 1, 0, 0) x + 1 >= 0, - An inequality (0, 0, 0, 1) x + 1 >= 0)) + An inequality (0, 1, 0, 0) x + 1 >= 0), + (An inequality (0, 0, 0, 1) x + 1 >= 0, + An inequality (0, 1, 0, 0) x + 1 >= 0)) sage: C.ridges(names=False)[:2] ((6, 7), (5, 7)) @@ -90,17 +90,19 @@ from sage.graphs.digraph import DiGraph from sage.combinat.posets.lattices import FiniteLatticePoset from sage.geometry.polyhedron.base import Polyhedron_base from sage.geometry.lattice_polytope import LatticePolytopeClass +from sage.geometry.cone import ConvexRationalPolyhedralCone from sage.structure.element import Matrix from sage.misc.misc import is_iterator from .conversions \ - import incidence_matrix_to_bit_repr_of_facets, \ - incidence_matrix_to_bit_repr_of_Vrepr, \ - facets_tuple_to_bit_repr_of_facets, \ - facets_tuple_to_bit_repr_of_Vrepr + import incidence_matrix_to_bit_rep_of_facets, \ + incidence_matrix_to_bit_rep_of_Vrep, \ + facets_tuple_to_bit_rep_of_facets, \ + facets_tuple_to_bit_rep_of_Vrep from sage.misc.cachefunc import cached_method -from sage.rings.integer cimport smallInteger -from cysignals.signals cimport sig_check, sig_block, sig_unblock +from sage.rings.integer cimport smallInteger +from cysignals.signals cimport sig_check, sig_block, sig_unblock +from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -114,19 +116,22 @@ cdef class CombinatorialPolyhedron(SageObject): - ``data`` -- an instance of * :class:`~sage.geometry.polyhedron.parent.Polyhedron_base` * or a :class:`~sage.geometry.lattice_polytope.LatticePolytopeClass` + * or a :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` * or an ``incidence_matrix`` as in :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.incidence_matrix` - In this case you should also specify the ``Vrepr`` and ``facets`` arguments + In this case you should also specify the ``Vrep`` and ``facets`` arguments * or list of facets, each facet given as a list of ``[vertices, rays, lines]`` if the polyhedron is unbounded, then rays and lines and the extra argument ``nr_lines`` are required if the polyhedron contains no lines, the rays can be thought of as the vertices of the facets deleted from a bounded polyhedron see :class:`~sage.geometry.polyhedron.parent.Polyhedron_base` on how to use - rays and lines. + rays and lines * or an integer, representing the dimension of a polyhedron equal to its affine hull - - ``Vrepr`` -- (optional) when ``data`` is an incidence matrix, it should + * or a tuple consisting of facets and vertices as two + :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces`. + - ``Vrep`` -- (optional) when ``data`` is an incidence matrix, it should be the list of ``[vertices, rays, lines]``, if the rows in the incidence_matrix should correspond to names - ``facets`` -- (optional) when ``data`` is an incidence matrix or a list of facets, @@ -135,9 +140,9 @@ cdef class CombinatorialPolyhedron(SageObject): - ``unbounded`` -- value will be overwritten if ``data`` is a polyhedron; if ``unbounded`` and ``data`` is incidence matrix or a list of facets, need to specify ``far_face`` - - ``far_face`` -- (semi-optional) when ``data` is an incidence matrix or a - list of facets and the polyhedron is unbounded this needs to be set to - the list of indices of the rays and lines + - ``far_face`` -- (semi-optional); if the polyhedron is unbounded this + needs to be set to the list of indices of the rays and line unless ``data`` is + an instance of :class:`~sage.geometry.polyhedron.parent.Polyhedron_base`. EXAMPLES: @@ -155,6 +160,12 @@ cdef class CombinatorialPolyhedron(SageObject): sage: CombinatorialPolyhedron(L) A 3-dimensional combinatorial polyhedron with 8 facets + a cone:: + + sage: M = Cone([(1,0), (0,1)]) + sage: CombinatorialPolyhedron(M) + A 2-dimensional combinatorial polyhedron with 2 facets + an incidence matrix:: sage: P = Polyhedron(rays=[[0,1]]) @@ -162,7 +173,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: far_face = [i for i in range(2) if not P.Vrepresentation()[i].is_vertex()] sage: CombinatorialPolyhedron(data, unbounded=True, far_face=far_face) A 1-dimensional combinatorial polyhedron with 1 facet - sage: C = CombinatorialPolyhedron(data, Vrepr=['myvertex'], + sage: C = CombinatorialPolyhedron(data, Vrep=['myvertex'], ....: facets=['myfacet'], unbounded=True, far_face=far_face) sage: C.Vrepresentation() ('myvertex',) @@ -190,6 +201,20 @@ cdef class CombinatorialPolyhedron(SageObject): sage: CombinatorialPolyhedron(5).f_vector() (1, 0, 0, 0, 0, 0, 1) + tuple of ``ListOfFaces``:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import facets_tuple_to_bit_rep_of_facets, \ + ....: facets_tuple_to_bit_rep_of_Vrep + sage: bi_pyr = ((0,1,4), (1,2,4), (2,3,4), (3,0,4), + ....: (0,1,5), (1,2,5), (2,3,5), (3,0,5)) + sage: facets = facets_tuple_to_bit_rep_of_facets(bi_pyr, 6) + sage: Vrep = facets_tuple_to_bit_rep_of_Vrep(bi_pyr, 6) + sage: C = CombinatorialPolyhedron((facets, Vrep)); C + A 3-dimensional combinatorial polyhedron with 8 facets + sage: C.f_vector() + (1, 6, 12, 8, 1) + Specifying that a polyhedron is unbounded is important. The following with a polyhedron works fine:: @@ -202,7 +227,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: data = P.incidence_matrix() sage: vert = P.Vrepresentation() - sage: C = CombinatorialPolyhedron(data, Vrepr=vert) + sage: C = CombinatorialPolyhedron(data, Vrep=vert) sage: C A 2-dimensional combinatorial polyhedron with 2 facets sage: C.f_vector() @@ -215,7 +240,7 @@ cdef class CombinatorialPolyhedron(SageObject): The correct usage is:: sage: far_face = [i for i in range(3) if not P.Vrepresentation()[i].is_vertex()] - sage: C = CombinatorialPolyhedron(data, Vrepr=vert, unbounded=True, far_face=far_face) + sage: C = CombinatorialPolyhedron(data, Vrep=vert, unbounded=True, far_face=far_face) sage: C A 2-dimensional combinatorial polyhedron with 2 facets sage: C.f_vector() @@ -255,7 +280,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: data = P.incidence_matrix() sage: vert = P.Vrepresentation() sage: far_face = [i for i in range(3) if not P.Vrepresentation()[i].is_vertex()] - sage: C = CombinatorialPolyhedron(data, Vrepr=vert, unbounded=True, far_face=far_face) + sage: C = CombinatorialPolyhedron(data, Vrep=vert, unbounded=True, far_face=far_face) sage: C A 2-dimensional combinatorial polyhedron with 2 facets sage: C.f_vector() @@ -269,7 +294,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: data = P.incidence_matrix() sage: vert = P.Vrepresentation() - sage: C = CombinatorialPolyhedron(data, Vrepr=vert) + sage: C = CombinatorialPolyhedron(data, Vrep=vert) sage: C.f_vector() Traceback (most recent call last): ... @@ -286,7 +311,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: CombinatorialPolyhedron(LatticePolytope([], lattice=ToricLattice(3))) A -1-dimensional combinatorial polyhedron with 0 facets """ - def __init__(self, data, Vrepr=None, facets=None, unbounded=False, far_face=None): + def __init__(self, data, Vrep=None, facets=None, unbounded=False, far_face=None, Vrepr=None): r""" Initialize :class:`CombinatorialPolyhedron`. @@ -298,7 +323,15 @@ cdef class CombinatorialPolyhedron(SageObject): ....: [0,2,3],[1,2,3]]) # indirect doctest sage: TestSuite(sage.geometry.polyhedron.combinatorial_polyhedron.base.CombinatorialPolyhedron).run() + + sage: C = CombinatorialPolyhedron(Matrix([[1,0],[0,1]]), Vrepr=['zero', 'one']) + doctest:...: DeprecationWarning: the keyword ``Vrepr`` is deprecated; use ``Vrep`` + See https://trac.sagemath.org/28608 for details. """ + if Vrepr: + from sage.misc.superseded import deprecation + deprecation(28608, "the keyword ``Vrepr`` is deprecated; use ``Vrep``", 3) + Vrep = Vrepr self._dimension = -2 # a "NULL" value self._edges = NULL self._ridges = NULL @@ -316,9 +349,11 @@ cdef class CombinatorialPolyhedron(SageObject): # ``self._length_edges_list*2*sizeof(size_t *)``. self._length_edges_list = 16348 + data_modified = None + if isinstance(data, Polyhedron_base): # input is ``Polyhedron`` - Vrepr = data.Vrepresentation() + Vrep = data.Vrepresentation() facets = tuple(inequality for inequality in data.Hrepresentation()) self._dimension = data.dimension() @@ -328,16 +363,35 @@ cdef class CombinatorialPolyhedron(SageObject): else: self._bounded = True + P = data data = data.incidence_matrix() + + # Delete equations + if P.n_equations(): + data_modified = data.delete_columns([e.index() for e in P.equations()]) + else: + data_modified = data elif isinstance(data, LatticePolytopeClass): # input is ``LatticePolytope`` self._bounded = True - Vrepr = data.vertices() - self._n_Vrepresentation = len(Vrepr) - facets = data.facets() + Vrep = data.vertices() + self._n_Vrepresentation = len(Vrep) + facets = tuple(data.facet_normals()) + self._n_Hrepresentation = len(facets) + data = data.incidence_matrix() + elif isinstance(data, ConvexRationalPolyhedralCone): + # input is ``Cone`` + self._bounded = False + Vrep = tuple(data.rays()) + (data.lattice().zero(),) + self._n_Vrepresentation = len(Vrep) + facets = tuple(data.facet_normals()) self._n_Hrepresentation = len(facets) - data = tuple(tuple(vert for vert in facet.vertices()) - for facet in facets) + far_face = tuple(i for i in range(len(Vrep) - 1)) + self._dimension = data.dim() + from sage.matrix.all import matrix + from sage.rings.all import ZZ + data = matrix(ZZ, data.incidence_matrix().rows() + + [[ZZ.one() for _ in range(len(facets))]]) else: # Input is different from ``Polyhedron`` and ``LatticePolytope``. if not unbounded: @@ -348,9 +402,9 @@ cdef class CombinatorialPolyhedron(SageObject): else: self._bounded = False - if Vrepr: + if Vrep: # store vertices names - self._Vrep = tuple(Vrepr) + self._Vrep = tuple(Vrep) Vinv = {v: i for i,v in enumerate(self._Vrep)} else: self._Vrep = None @@ -383,17 +437,34 @@ cdef class CombinatorialPolyhedron(SageObject): self._n_Hrepresentation = data.ncols() self._n_Vrepresentation = data.nrows() + if not isinstance(data, Matrix_integer_dense): + from sage.rings.all import ZZ + from sage.matrix.all import matrix + data = matrix(ZZ, data, sparse=False) + assert isinstance(data, Matrix_integer_dense), "conversion to ``Matrix_integer_dense`` didn't work" + + # Store the incidence matrix. + if not data.is_immutable(): + data = data.__copy__() + data.set_immutable() + self.incidence_matrix.set_cache(data) + + + if data_modified is None: + # Delete equations. + data_modified = data.delete_columns([i for i in range(data.ncols()) if all(data[j,i] for j in range(data.nrows()))], check=False) + # Initializing the facets in their Bit-representation. - self._bitrep_facets = incidence_matrix_to_bit_repr_of_facets(data) + self._bitrep_facets = incidence_matrix_to_bit_rep_of_facets(data_modified) - # Initializing the Vrepr as their Bit-representation. - self._bitrep_Vrepr = incidence_matrix_to_bit_repr_of_Vrepr(data) + # Initializing the Vrep as their Bit-representation. + self._bitrep_Vrep = incidence_matrix_to_bit_rep_of_Vrep(data_modified) self._n_facets = self.bitrep_facets().n_faces # Initialize far_face if unbounded. if not self._bounded: - self._far_face = facets_tuple_to_bit_repr_of_facets((tuple(far_face),), self._n_Vrepresentation) + self._far_face = facets_tuple_to_bit_rep_of_facets((tuple(far_face),), self._n_Vrepresentation) else: self._far_face = None @@ -402,17 +473,38 @@ cdef class CombinatorialPolyhedron(SageObject): # one can give an Integer as Input. if data < -1: ValueError("any polyhedron must have dimension at least -1") - self._n_facets = 0 self._dimension = data + if self._dimension == 0: + self._n_facets = 1 + self._n_Vrepresentation = 1 + else: + self._n_facets = 0 + self._n_Vrepresentation = 0 + # Initializing the facets in their Bit-representation. - self._bitrep_facets = facets_tuple_to_bit_repr_of_facets((), 0) + self._bitrep_facets = facets_tuple_to_bit_rep_of_facets((), 0) - # Initializing the Vrepr as their Bit-representation. - self._bitrep_Vrepr = facets_tuple_to_bit_repr_of_Vrepr((), 0) + # Initializing the Vrep as their Bit-representation. + self._bitrep_Vrep = facets_tuple_to_bit_rep_of_Vrep((), 0) self._far_face = None + elif isinstance(data, (tuple, list)) and len(data) == 2 and isinstance(data[0], ListOfFaces) and isinstance(data[1], ListOfFaces): + # Initialize self from two ``ListOfFaces``. + self._bitrep_facets = data[0] + self._bitrep_Vrep = data[1] + + self._n_Hrepresentation = self._bitrep_facets.n_faces + self._n_Vrepresentation = self._bitrep_Vrep.n_faces + self._n_facets = self._n_Hrepresentation + + # Initialize far_face if unbounded. + if not self._bounded: + self._far_face = facets_tuple_to_bit_rep_of_facets((tuple(far_face),), self._n_Vrepresentation) + else: + self._far_face = None + else: # Input is a "list" of facets. # The facets given by its ``[vertices, rays, lines]``. @@ -421,11 +513,11 @@ cdef class CombinatorialPolyhedron(SageObject): data = tuple(data) if self._Vrep is None: - # Get the names of the Vrepr. - Vrepr = sorted(set.union(*map(set, data))) - n_Vrepresentation = len(Vrepr) - if Vrepr != range(len(Vrepr)): - self._Vrep = tuple(Vrepr) + # Get the names of the Vrep. + Vrep = sorted(set.union(*map(set, data))) + n_Vrepresentation = len(Vrep) + if Vrep != range(len(Vrep)): + self._Vrep = tuple(Vrep) Vinv = {v: i for i,v in enumerate(self._Vrep)} else: # Assuming the user gave as correct names for the vertices @@ -434,7 +526,7 @@ cdef class CombinatorialPolyhedron(SageObject): self._n_Vrepresentation = n_Vrepresentation - # Relabel the Vrepr to be `0,...,n`. + # Relabel the Vrep to be `0,...,n`. if self._Vrep is not None: def f(v): return Vinv[v] else: @@ -445,14 +537,14 @@ cdef class CombinatorialPolyhedron(SageObject): self._n_Hrepresentation = len(facets) # Initializing the facets in their Bit-representation. - self._bitrep_facets = facets_tuple_to_bit_repr_of_facets(facets, n_Vrepresentation) + self._bitrep_facets = facets_tuple_to_bit_rep_of_facets(facets, n_Vrepresentation) - # Initializing the Vrepr as their Bit-representation. - self._bitrep_Vrepr = facets_tuple_to_bit_repr_of_Vrepr(facets, n_Vrepresentation) + # Initializing the Vrep as their Bit-representation. + self._bitrep_Vrep = facets_tuple_to_bit_rep_of_Vrep(facets, n_Vrepresentation) # Initialize far_face if unbounded. if not self._bounded: - self._far_face = facets_tuple_to_bit_repr_of_facets((tuple(far_face),), n_Vrepresentation) + self._far_face = facets_tuple_to_bit_rep_of_facets((tuple(far_face),), n_Vrepresentation) else: self._far_face = None @@ -480,7 +572,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: P = Polyhedron(vertices=[[0,0]]) sage: C = CombinatorialPolyhedron(P) sage: C._repr_() - 'A 0-dimensional combinatorial polyhedron with 1 facet' + 'A 0-dimensional combinatorial polyhedron with 0 facets' sage: P = Polyhedron(lines=[[0,0,1],[0,1,0]]) sage: C = CombinatorialPolyhedron(P) @@ -568,6 +660,17 @@ cdef class CombinatorialPolyhedron(SageObject): A ray in the direction (1, 0, 0), A vertex at (0, 0, 0), A ray in the direction (0, 1, 0)) + + sage: points = [(1,0,0), (0,1,0), (0,0,1), + ....: (-1,0,0), (0,-1,0), (0,0,-1)] + sage: L = LatticePolytope(points) + sage: C = CombinatorialPolyhedron(L) + sage: C.Vrepresentation() + (M(1, 0, 0), M(0, 1, 0), M(0, 0, 1), M(-1, 0, 0), M(0, -1, 0), M(0, 0, -1)) + + sage: M = Cone([(1,0), (0,1)]) + sage: CombinatorialPolyhedron(M).Vrepresentation() + (N(1, 0), N(0, 1), N(0, 0)) """ if self.Vrep() is not None: return self.Vrep() @@ -590,6 +693,24 @@ cdef class CombinatorialPolyhedron(SageObject): An inequality (0, 1, 0) x - 1 >= 0, An inequality (0, 1, 1) x - 3 >= 0, An inequality (0, 0, 1) x - 1 >= 0) + + sage: points = [(1,0,0), (0,1,0), (0,0,1), + ....: (-1,0,0), (0,-1,0), (0,0,-1)] + sage: L = LatticePolytope(points) + sage: C = CombinatorialPolyhedron(L) + sage: C.Hrepresentation() + (N(1, -1, -1), + N(1, 1, -1), + N(1, 1, 1), + N(1, -1, 1), + N(-1, -1, 1), + N(-1, -1, -1), + N(-1, 1, -1), + N(-1, 1, 1)) + + sage: M = Cone([(1,0), (0,1)]) + sage: CombinatorialPolyhedron(M).Hrepresentation() + (M(0, 1), M(1, 0)) """ if self.facet_names() is not None: return self.equalities() + self.facet_names() @@ -634,6 +755,7 @@ cdef class CombinatorialPolyhedron(SageObject): dim = dimension + @cached_method def n_vertices(self): r""" Return the number of vertices. @@ -678,7 +800,7 @@ cdef class CombinatorialPolyhedron(SageObject): # This specific trivial polyhedron needs special attention. return smallInteger(1) if not self.is_bounded(): - # Some elements in the ``Vrepr`` might not correspond to actual combinatorial vertices. + # Some elements in the ``Vrep`` might not correspond to actual combinatorial vertices. return len(self.vertices()) else: return smallInteger(self.n_Vrepresentation()) @@ -740,7 +862,7 @@ cdef class CombinatorialPolyhedron(SageObject): it = self.face_iter(0) try: # The Polyhedron has at least one vertex. - # In this case every element in the ``Vrepr`` + # In this case every element in the ``Vrep`` # that is not contained in the far face # is a vertex. next(it) @@ -748,7 +870,7 @@ cdef class CombinatorialPolyhedron(SageObject): # The Polyhedron has no vertex. return () if names and self.Vrep(): - return tuple(self.Vrep()[i] for i in range(self.n_Vrepresentation()) if not i in self.far_face_tuple()) + return tuple(self.Vrep()[i] for i in range(self.n_Vrepresentation()) if not i in self.far_face_tuple()) else: return tuple(smallInteger(i) for i in range(self.n_Vrepresentation()) if not i in self.far_face_tuple()) @@ -786,15 +908,18 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C.n_facets() 0 + Facets are defined to be the maximal nontrivial faces. + The ``0``-dimensional polyhedron does not have nontrivial faces:: + sage: C = CombinatorialPolyhedron(0) sage: C.f_vector() (1, 1) sage: C.n_facets() - 1 + 0 """ if unlikely(self._dimension == 0): # This trivial polyhedron needs special attention. - return smallInteger(1) + return smallInteger(0) return smallInteger(self._n_facets) def facets(self, names=True): @@ -804,47 +929,57 @@ cdef class CombinatorialPolyhedron(SageObject): If ``names`` is ``False``, then the Vrepresentatives in the facets are given by their indices in the Vrepresentation. + The facets are the maximal nontrivial faces. + EXAMPLES:: sage: P = polytopes.cube() sage: C = CombinatorialPolyhedron(P) sage: C.facets() - ((A vertex at (-1, -1, 1), - A vertex at (-1, 1, 1), - A vertex at (1, -1, 1), - A vertex at (1, 1, 1)), - (A vertex at (-1, 1, -1), - A vertex at (-1, 1, 1), + ((A vertex at (1, -1, -1), A vertex at (1, 1, -1), - A vertex at (1, 1, 1)), - (A vertex at (1, -1, -1), + A vertex at (1, 1, 1), + A vertex at (1, -1, 1)), + (A vertex at (1, 1, -1), + A vertex at (1, 1, 1), + A vertex at (-1, 1, -1), + A vertex at (-1, 1, 1)), + (A vertex at (1, 1, 1), A vertex at (1, -1, 1), - A vertex at (1, 1, -1), - A vertex at (1, 1, 1)), - (A vertex at (-1, -1, -1), A vertex at (-1, -1, 1), - A vertex at (-1, 1, -1), A vertex at (-1, 1, 1)), - (A vertex at (-1, -1, -1), + (A vertex at (-1, -1, 1), + A vertex at (-1, -1, -1), A vertex at (-1, 1, -1), - A vertex at (1, -1, -1), - A vertex at (1, 1, -1)), - (A vertex at (-1, -1, -1), + A vertex at (-1, 1, 1)), + (A vertex at (1, -1, -1), + A vertex at (1, 1, -1), + A vertex at (-1, -1, -1), + A vertex at (-1, 1, -1)), + (A vertex at (1, -1, -1), + A vertex at (1, -1, 1), A vertex at (-1, -1, 1), - A vertex at (1, -1, -1), - A vertex at (1, -1, 1))) + A vertex at (-1, -1, -1))) sage: C.facets(names=False) - ((1, 3, 5, 7), - (2, 3, 6, 7), + ((0, 1, 2, 3), + (1, 2, 6, 7), + (2, 3, 4, 7), (4, 5, 6, 7), - (0, 1, 2, 3), - (0, 2, 4, 6), - (0, 1, 4, 5)) + (0, 1, 5, 6), + (0, 3, 4, 5)) + + The empty face is trivial and hence the ``0``-dimensional + polyhedron does not have facets:: + + sage: C = CombinatorialPolyhedron(0) + sage: C.facets() + () """ if unlikely(self.dimension() == 0): # Special attention for this trivial case. - # There is actually one facet, but we have not initialized it. - return ((),) + # Facets are defined to be nontrivial faces of codimension 1. + # The empty face is trivial. + return () # It is essential to have the facets in the exact same order as # on input, so that pickle/unpickle by :meth:`reduce` works. @@ -861,6 +996,7 @@ cdef class CombinatorialPolyhedron(SageObject): return tuple(facets) + @cached_method def incidence_matrix(self): """ Return the incidence matrix. @@ -881,26 +1017,76 @@ cdef class CombinatorialPolyhedron(SageObject): sage: P = polytopes.cube() sage: C = P.combinatorial_polyhedron() sage: C.incidence_matrix() + [1 0 0 0 1 1] + [1 1 0 0 1 0] + [1 1 1 0 0 0] + [1 0 1 0 0 1] + [0 0 1 1 0 1] [0 0 0 1 1 1] - [1 0 0 1 0 1] [0 1 0 1 1 0] - [1 1 0 1 0 0] - [0 0 1 0 1 1] - [1 0 1 0 0 1] - [0 1 1 0 1 0] - [1 1 1 0 0 0] - sage: P.incidence_matrix() == C.incidence_matrix() + [0 1 1 1 0 0] + + In this case the incidence matrix is only computed once:: + + sage: P.incidence_matrix() is C.incidence_matrix() + True + sage: C.incidence_matrix.clear_cache() + sage: C.incidence_matrix() is P.incidence_matrix() + False + sage: C.incidence_matrix() == P.incidence_matrix() True + :: + sage: P = polytopes.permutahedron(5) sage: C = P.combinatorial_polyhedron() + sage: C.incidence_matrix.clear_cache() sage: C.incidence_matrix() == P.incidence_matrix() True + + The incidence matrix is consistent with + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.incidence_matrix`:: + + sage: P = Polyhedron([[0,0]]) + sage: P.incidence_matrix() + [1 1] + sage: C = P.combinatorial_polyhedron() + sage: C.incidence_matrix.clear_cache() + sage: P.combinatorial_polyhedron().incidence_matrix() + [1 1] + + TESTS: + + Check that :trac:`29455` is fixed:: + + sage: C = Polyhedron([[0]]).combinatorial_polyhedron() + sage: C.incidence_matrix.clear_cache() + sage: C.incidence_matrix() + [1] + sage: C = CombinatorialPolyhedron(-1) + sage: C.incidence_matrix.clear_cache() + sage: C.incidence_matrix() + [] + + Check that the base ring is ``ZZ``, see :trac:`29840`:: + + sage: C = CombinatorialPolyhedron([[0,1,2], [0,1,3], [0,2,3], [1,2,3]]) + sage: C.incidence_matrix().base_ring() + Integer Ring """ from sage.rings.all import ZZ from sage.matrix.constructor import matrix - incidence_matrix = matrix(ZZ, self.n_Vrepresentation(), - self.n_Hrepresentation(), 0) + cdef Matrix_integer_dense incidence_matrix = matrix( + ZZ, self.n_Vrepresentation(), self.n_Hrepresentation(), 0) + + if self.dim() < 1: + # Small cases. + if self.dim() == 0: + # To be consistent with ``Polyhedron_base``, + for i in range(self.n_Hrepresentation()): + incidence_matrix.set_unsafe_si(0, i, 1) + incidence_matrix.set_immutable() + return incidence_matrix # If equalities are present, we add them as first columns. n_equalities = 0 @@ -908,13 +1094,15 @@ cdef class CombinatorialPolyhedron(SageObject): n_equalities = len(self.equalities()) for Hindex in range(n_equalities): for Vindex in range(self.n_Vrepresentation()): - incidence_matrix[Vindex, Hindex] = 1 + incidence_matrix.set_unsafe_si(Vindex, Hindex, 1) facet_iter = self.face_iter(self.dimension() - 1, dual=False) for facet in facet_iter: Hindex = facet.ambient_H_indices()[0] + n_equalities for Vindex in facet.ambient_V_indices(): - incidence_matrix[Vindex, Hindex] = 1 + incidence_matrix.set_unsafe_si(Vindex, Hindex, 1) + + incidence_matrix.set_immutable() return incidence_matrix @@ -997,7 +1185,7 @@ cdef class CombinatorialPolyhedron(SageObject): # Hence, edges are stored in an array of arrays, # with each array containing ``len_edge_list`` of edges. - # Mapping the indices of the Vrepr to the names, if requested. + # Mapping the indices of the Vrep to the names, if requested. if self.Vrep() is not None and names is True: def f(size_t i): return self.Vrep()[i] else: @@ -1273,6 +1461,115 @@ 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] + + TESTS: + + Test that :trac:`29898` is fixed:: + + sage: Polyhedron().vertex_facet_graph() + Digraph on 0 vertices + sage: Polyhedron([[0]]).vertex_facet_graph() + Digraph on 1 vertex + sage: Polyhedron([[0]]).vertex_facet_graph(False) + Digraph on 1 vertex + """ + if self.dimension() == -1: + return DiGraph() + if self.dimension() == 0: + if not names: + return DiGraph(1) + else: + Vrep = self.Vrep() + if Vrep: + v = Vrep[0] + else: + v = ("V", 0) + return DiGraph([[v], []]) + + # 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 were provided at initialisation. + facet_names = [("H", i) for i in range(n_facets)] + + Vrep = self.Vrep() + if Vrep is None: + # No names were provided at initialisation. + 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) + + @cached_method def f_vector(self): r""" Compute the ``f_vector`` of the polyhedron. @@ -1312,6 +1609,252 @@ cdef class CombinatorialPolyhedron(SageObject): f_vector.set_immutable() return f_vector + def flag_f_vector(self, *args): + r""" + Return the flag f-vector. + + For each `-1 < i_0 < \dots < i_n < d` the flag f-vector + counts the number of flags `F_0 \subset \dots \subset F_n` + with `F_j` of dimension `i_j` for each `0 \leq j \leq n`, + where `d` is the dimension of the polyhedron. + + INPUT: + + - ``args`` -- integers (optional); specify an entry of the + flag-f-vector; must be an increasing sequence of integers + + OUTPUT: + + - a dictionary, if no arguments were given + + - an Integer, if arguments were given + + EXAMPLES: + + Obtain the entire flag-f-vector:: + + sage: C = polytopes.hypercube(4).combinatorial_polyhedron() + sage: C.flag_f_vector() + {(-1,): 1, + (0,): 16, + (0, 1): 64, + (0, 1, 2): 192, + (0, 1, 2, 3): 384, + (0, 1, 3): 192, + (0, 2): 96, + (0, 2, 3): 192, + (0, 3): 64, + (1,): 32, + (1, 2): 96, + (1, 2, 3): 192, + (1, 3): 96, + (2,): 24, + (2, 3): 48, + (3,): 8, + (4,): 1} + + Specify an entry:: + + sage: C.flag_f_vector(0,3) + 64 + sage: C.flag_f_vector(2) + 24 + + Leading ``-1`` and trailing entry of dimension are allowed:: + + sage: C.flag_f_vector(-1,0,3) + 64 + sage: C.flag_f_vector(-1,0,3,4) + 64 + + One can get the number of trivial faces:: + + sage: C.flag_f_vector(-1) + 1 + sage: C.flag_f_vector(4) + 1 + + Polyhedra with lines, have ``0`` entries accordingly:: + + sage: C = (Polyhedron(lines=[[1]]) * polytopes.hypercube(2)).combinatorial_polyhedron() + sage: C.flag_f_vector() + {(-1,): 1, (0, 1): 0, (0, 2): 0, (0,): 0, (1, 2): 8, (1,): 4, (2,): 4, 3: 1} + + If the arguments are not stricly increasing or out of range, a key error is raised:: + + sage: C.flag_f_vector(-1,0,3,5) + Traceback (most recent call last): + ... + KeyError: (0, 3, 5) + sage: C.flag_f_vector(-1,3,0) + Traceback (most recent call last): + ... + KeyError: (3, 0) + """ + flag = self._flag_f_vector() + if len(args) == 0: + return flag + elif len(args) == 1: + return flag[(args[0],)] + else: + dim = self.dimension() + if args[0] == -1: + args = args[1:] + if args[-1] == dim: + args = args[:-1] + return flag[tuple(args)] + + @cached_method + def _flag_f_vector(self): + r""" + Obtain the flag-f-vector from the flag-f-polynomial from the face lattice. + + See :meth:`flag_f_vector`. + + TESTS:: + + sage: C = CombinatorialPolyhedron(3) + sage: C._flag_f_vector() + {(-1,): 1, (0, 1): 0, (0, 2): 0, (0,): 0, (1, 2): 0, (1,): 0, (2,): 0, 3: 1} + """ + poly = self.face_lattice().flag_f_polynomial() + variables = poly.variables() + dim = self.dimension() + flag = {(smallInteger(-1),): smallInteger(1)} + for term in poly.monomials(): + index = tuple([variables.index(var) for var in term.variables()[:-1]]) + if index == (): + flag[(dim,)] = smallInteger(1) + else: + flag[index] = poly.monomial_coefficient(term) + + n_lines = sum([1 for x in self.f_vector() if x == 0]) + if n_lines: + # The polyhedron has lines and we have to account for that. + # So we basically shift all entries up by the number of lines + # and add zero entries for the lines. + from itertools import combinations + flag_old = flag + flag = {(smallInteger(-1),): smallInteger(1)} + ran = [smallInteger(i) for i in range(self.dim())] + for k in range(1, self.dim()): + for comb in combinations(ran, self.dim() - k): + if comb[0] < n_lines: + # There are no faces of dimension 0,...,n_lines. + flag[comb] = smallInteger(0) + else: + # Shift the old entires up by the number of lines. + flag[comb] = flag_old[tuple(i - n_lines for i in comb)] + + flag[self.dimension()] = smallInteger(1) + + return flag + + @cached_method + def neighborliness(self): + r""" + Return the largest ``k``, such that the polyhedron is ``k``-neighborly. + + A polyhedron is `k`-neighborly if every set of `n` vertices forms a face + for `n` up to `k`. + + In case of the `d`-dimensional simplex, it returns `d + 1`. + + .. SEEALSO:: + + :meth:`is_neighborly` + + EXAMPLES:: + + sage: P = polytopes.cyclic_polytope(8,12) + sage: C = P.combinatorial_polyhedron() + sage: C.neighborliness() + 4 + sage: P = polytopes.simplex(6) + sage: C = P.combinatorial_polyhedron() + sage: C.neighborliness() + 7 + sage: P = polytopes.cyclic_polytope(4,10) + sage: P = P.join(P) + sage: C = P.combinatorial_polyhedron() + sage: C.neighborliness() + 2 + """ + if self.is_simplex(): + return self.dim() + 1 + else: + from sage.functions.other import binomial + k = 1 + while self.f_vector()[k+1] == binomial(self.n_vertices(), k + 1): + k += 1 + return k + + @cached_method + def is_neighborly(self, k=None): + r""" + Return whether the polyhedron is neighborly. + + If the input `k` is provided, then return whether the polyhedron is `k`-neighborly. + + A polyhedron is neighborly if every set of `n` vertices forms a face + for `n` up to floor of half the dimension of the polyhedron. + It is `k`-neighborly if this is true for `n` up to `k`. + + INPUT: + + - ``k`` -- the dimension up to which to check if every set of ``k`` + vertices forms a face. If no ``k`` is provided, check up to floor + of half the dimension of the polyhedron. + + OUTPUT: + + - ``True`` if the every set of up to ``k`` vertices forms a face, + - ``False`` otherwise + + .. SEEALSO:: + + :meth:`neighborliness` + + EXAMPLES:: + + sage: P = polytopes.cyclic_polytope(8,12) + sage: C = P.combinatorial_polyhedron() + sage: C.is_neighborly() + True + sage: P = polytopes.simplex(6) + sage: C = P.combinatorial_polyhedron() + sage: C.is_neighborly() + True + sage: P = polytopes.cyclic_polytope(4,10) + sage: P = P.join(P) + sage: C = P.combinatorial_polyhedron() + sage: C.is_neighborly() + False + sage: C.is_neighborly(k=2) + True + """ + from sage.functions.other import binomial + if k is None: + k = self.dim() // 2 + return all(self.f_vector()[i+1] == binomial(self.n_vertices(), i + 1) + for i in range(1, k)) + + def is_simplex(self): + r""" + Return whether the polyhedron is a simplex. + + A simplex is a bounded polyhedron with `d+1` vertices, where + `d` is the dimension. + + EXAMPLES:: + + sage: CombinatorialPolyhedron(2).is_simplex() + False + sage: CombinatorialPolyhedron([[0,1],[0,2],[1,2]]).is_simplex() + True + """ + return self.is_bounded() and (self.dim()+1 == self.n_vertices()) + def is_simplicial(self): r""" Test whether the polytope is simplicial. @@ -1404,7 +1947,7 @@ cdef class CombinatorialPolyhedron(SageObject): cdef simpliciality = dim - 1 # For each face in the iterator, check if its a simplex. - face_iter.lowest_dimension = 2 # every 1-face is a simplex + face_iter.structure.lowest_dimension = 2 # every 1-face is a simplex d = face_iter.next_dimension() while (d < dim): sig_check() @@ -1472,7 +2015,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. @@ -1518,7 +2061,7 @@ cdef class CombinatorialPolyhedron(SageObject): cdef simplicity = dim - 1 # For each coface in the iterator, check if its a simplex. - coface_iter.lowest_dimension = 2 # every coface of dimension 1 is a simplex + coface_iter.structure.lowest_dimension = 2 # every coface of dimension 1 is a simplex d = coface_iter.next_dimension() while (d < dim): sig_check() @@ -1535,6 +2078,191 @@ cdef class CombinatorialPolyhedron(SageObject): d = dim return smallInteger(simplicity) + @cached_method + def is_lawrence_polytope(self): + """ + Return ``True`` if ``self`` is a Lawrence polytope. + + A polytope is called a Lawrence polytope if it has a centrally + symmetric (normalized) Gale diagram. + + Equivalently, there exists a partition `P_1,\dots,P_k` + of the vertices `V` such that each part + `P_i` has size `2` or `1` and for each part there exists + a facet with vertices exactly `V \setminus P_i`. + + EXAMPLES:: + + sage: C = polytopes.simplex(5).combinatorial_polyhedron() + sage: C.is_lawrence_polytope() + True + sage: P = polytopes.hypercube(4).lawrence_polytope() + sage: C = P.combinatorial_polyhedron() + sage: C.is_lawrence_polytope() + True + sage: P = polytopes.hypercube(4) + sage: C = P.combinatorial_polyhedron() + sage: C.is_lawrence_polytope() + False + + For unbounded polyhedra, an error is raised:: + + sage: C = CombinatorialPolyhedron([[0,1], [0,2]], far_face=[1,2], unbounded=True) + sage: C.is_lawrence_polytope() + Traceback (most recent call last): + ... + NotImplementedError: this function is implemented for polytopes only + + AUTHORS: + + - Laith Rastanawi + - Jonathan Kliem + + REFERENCES: + + For more information, see [BaSt1990]_. + """ + if not self.is_compact(): + raise NotImplementedError("this function is implemented for polytopes only") + if self.n_Vrepresentation() <= 2: + return True + + cdef FaceIterator facet_iterator = self._face_iter(False, self.dimension()-1) + cdef CombinatorialFace facet + cdef size_t n_vertices = self.n_Vrepresentation() + cdef size_t one, two, length, counter + cdef list vertices = [1 for _ in range(n_vertices)] + + for facet in facet_iterator: + length = facet.n_atom_rep() + if length >= n_vertices - 2: + # The facet has at most two non-vertices and corresponds to + # two symmetric vertices or a vertex at the origin + # in the Gale transform. + facet.set_atom_rep() + counter = 0 + while counter < length: + if facet.atom_rep[counter] != counter: + # We have found our first non-vertex. + one = counter + break + counter += 1 + else: + # The facet contains the first ``length`` vertices. + one = length + + if length == n_vertices - 1: + # The facet corresponds to a vertex at the origin + # of the Gale transform. + vertices[one] = 0 + else: + # The facet corresponds to two symmetric vertices + # of the Gale transform. + while counter < length: + if facet.atom_rep[counter] != counter + 1: + # We have found our second non-vertex. + two = counter + 1 + break + counter += 1 + else: + # The second non-vertex is the very last vertex. + two = length + 1 + + if vertices[one] == vertices[two]: + # Possibly the Gale transform contains duplicates, + # we must make sure that the mulitplicites are symmetric as well. + # (And not two vertices are symmetric to just one). + vertices[one] = 0 + vertices[two] = 0 + + return not any(vertices) + + @cached_method + def is_pyramid(self, certificate=False): + r""" + Test whether the polytope is a pyramid over one of its facets. + + INPUT: + + - ``certificate`` -- boolean (default: ``False``); specifies whether + to return a vertex of the polytope which is the apex of a pyramid, + if found + + OUTPUT: + + If ``certificate`` is ``True``, returns a tuple containing: + + 1. Boolean. + 2. The apex of the pyramid or ``None``. + + If ``certificate`` is ``False`` returns a boolean. + + AUTHORS: + + - Laith Rastanawi + - Jonathan Kliem + + EXAMPLES:: + + sage: C = polytopes.cross_polytope(4).combinatorial_polyhedron() + sage: C.is_pyramid() + False + sage: C.is_pyramid(certificate=True) + (False, None) + sage: C = polytopes.cross_polytope(4).pyramid().combinatorial_polyhedron() + sage: C.is_pyramid() + True + sage: C.is_pyramid(certificate=True) + (True, A vertex at (0, -1, 0, 0, 0)) + sage: C = polytopes.simplex(5).combinatorial_polyhedron() + sage: C.is_pyramid(certificate=True) + (True, A vertex at (0, 0, 0, 0, 0, 1)) + + For unbounded polyhedra, an error is raised:: + + sage: C = CombinatorialPolyhedron([[0,1], [0,2]], far_face=[1,2], unbounded=True) + sage: C.is_pyramid() + Traceback (most recent call last): + ... + ValueError: polyhedron has to be compact + + TESTS:: + + sage: CombinatorialPolyhedron(-1).is_pyramid() + False + sage: CombinatorialPolyhedron(-1).is_pyramid(True) + (False, None) + sage: CombinatorialPolyhedron(0).is_pyramid() + True + sage: CombinatorialPolyhedron(0).is_pyramid(True) + (True, 0) + """ + if not self.is_bounded(): + raise ValueError("polyhedron has to be compact") + + if self.dim() == -1: + if certificate: + return (False, None) + return False + + if self.dim() == 0: + if certificate: + return (True, self.Vrepresentation()[0]) + return True + + # Find a vertex that is incident to all elements in Hrepresentation but one. + vertex_iter = self._face_iter(True, 0) + n_facets = self.n_facets() + for index, vertex in enumerate(vertex_iter): + if vertex.n_ambient_Hrepresentation() == n_facets - 1: + if certificate: + return (True, self.Vrepresentation()[index]) + return True + + if certificate: + return (False, None) + return False + def face_iter(self, dimension=None, dual=None): r""" Iterator over all proper faces of specified dimension. @@ -1833,6 +2561,99 @@ cdef class CombinatorialPolyhedron(SageObject): # Let ``_all_faces`` determine Vrepresentation. return self._all_faces.get_face(dim, newindex) + def a_maximal_chain(self): + r""" + Return a maximal chain of the face lattice in increasing order + without empty face and whole polyhedron/maximal face. + + Each face is given as + :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`. + + EXAMPLES:: + + sage: P = polytopes.cross_polytope(4) + sage: C = P.combinatorial_polyhedron() + sage: chain = C.a_maximal_chain(); chain + [A 0-dimensional face of a 4-dimensional combinatorial polyhedron, + A 1-dimensional face of a 4-dimensional combinatorial polyhedron, + A 2-dimensional face of a 4-dimensional combinatorial polyhedron, + A 3-dimensional face of a 4-dimensional combinatorial polyhedron] + sage: [face.ambient_V_indices() for face in chain] + [(7,), (6, 7), (5, 6, 7), (4, 5, 6, 7)] + + sage: P = polytopes.hypercube(4) + sage: C = P.combinatorial_polyhedron() + sage: chain = C.a_maximal_chain(); chain + [A 0-dimensional face of a 4-dimensional combinatorial polyhedron, + A 1-dimensional face of a 4-dimensional combinatorial polyhedron, + A 2-dimensional face of a 4-dimensional combinatorial polyhedron, + A 3-dimensional face of a 4-dimensional combinatorial polyhedron] + sage: [face.ambient_V_indices() for face in chain] + [(15,), (6, 15), (5, 6, 14, 15), (0, 5, 6, 7, 8, 9, 14, 15)] + + sage: P = polytopes.permutahedron(4) + sage: C = P.combinatorial_polyhedron() + sage: chain = C.a_maximal_chain(); chain + [A 0-dimensional face of a 3-dimensional combinatorial polyhedron, + A 1-dimensional face of a 3-dimensional combinatorial polyhedron, + A 2-dimensional face of a 3-dimensional combinatorial polyhedron] + sage: [face.ambient_V_indices() for face in chain] + [(13,), (13, 15), (13, 15, 19, 21)] + + sage: P = Polyhedron(rays=[[1,0]], lines=[[0,1]]) + sage: C = P.combinatorial_polyhedron() + sage: chain = C.a_maximal_chain() + sage: [face.ambient_V_indices() for face in chain] + [(0, 1)] + + sage: P = Polyhedron(rays=[[1,0,0],[0,0,1]], lines=[[0,1,0]]) + sage: C = P.combinatorial_polyhedron() + sage: chain = C.a_maximal_chain() + sage: [face.ambient_V_indices() for face in chain] + [(0, 1), (0, 1, 3)] + + sage: P = Polyhedron(rays=[[1,0,0]], lines=[[0,1,0],[0,0,1]]) + sage: C = P.combinatorial_polyhedron() + sage: chain = C.a_maximal_chain() + sage: [face.ambient_V_indices() for face in chain] + [(0, 1, 2)] + """ + if self.n_facets() == 0 or self.dimension() == 0: + return [] + + # We take a face iterator and do one depth-search. + # Depending on whether it is dual or not, + # the search will be from the top or bottom. + it = self.face_iter() + chain = [None]*(self.dimension()) + dual = it.dual + final_dim = 0 if not dual else self.dimension()-1 + + # For each dimension we save the first face we see. + # This is the face whose sub-/supfaces we visit in the next step. + current_dim = self.dimension() + for face in it: + if face.dimension() == current_dim: + continue + current_dim = face.dimension() + if chain[current_dim] is None: + chain[current_dim] = face + else: + # The polyhedron contains lines and has + # no zero-dimensional faces. + current_dim -= 1 + break + + if current_dim == final_dim: + # The chain is complete. + break + if current_dim != final_dim: + # The polyhedron contains lines. + # Note that the iterator was always not dual + # in this case. + return chain[current_dim:] + return chain + cdef tuple Vrep(self): r""" Return the names of the Vrepresentation, if they exist. Else return ``None``. @@ -1900,11 +2721,11 @@ cdef class CombinatorialPolyhedron(SageObject): """ return self._bitrep_facets - cdef ListOfFaces bitrep_Vrepr(self): + cdef ListOfFaces bitrep_Vrep(self): r""" Return the Vrepresentations in bit representation. """ - return self._bitrep_Vrepr + return self._bitrep_Vrep cdef ListOfFaces far_face(self): r""" @@ -1920,6 +2741,55 @@ cdef class CombinatorialPolyhedron(SageObject): """ return self._far_face_tuple + + # Methods to obtain a different combinatorial polyhedron. + + cpdef CombinatorialPolyhedron dual(self): + r""" + Return the dual/polar of self. + + Only defined for bounded polyhedra. + + .. SEEALSO:: + + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.polar`. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: C = P.combinatorial_polyhedron() + sage: D = C.dual() + sage: D.f_vector() + (1, 6, 12, 8, 1) + sage: D1 = P.polar().combinatorial_polyhedron() + sage: D1.face_lattice().is_isomorphic(D.face_lattice()) + True + + Polar is an alias to be consistent with :class:`~sage.geometry.polyhedron.base.Polyhedron_base`:: + + sage: C.polar().f_vector() + (1, 6, 12, 8, 1) + + For unbounded polyhedra, an error is raised:: + + sage: C = CombinatorialPolyhedron([[0,1], [0,2]], far_face=[1,2], unbounded=True) + sage: C.dual() + Traceback (most recent call last): + ... + ValueError: self must be bounded + """ + if not self.is_bounded(): + raise ValueError("self must be bounded") + cdef ListOfFaces new_facets = self.bitrep_Vrep().__copy__() + cdef ListOfFaces new_Vrep = self.bitrep_facets().__copy__() + + return CombinatorialPolyhedron((new_facets, new_Vrep)) + + polar = dual + + + # Internal methods. + cdef int _compute_f_vector(self) except -1: r""" Compute the ``f_vector`` of the polyhedron. @@ -2082,12 +2952,12 @@ cdef class CombinatorialPolyhedron(SageObject): edges[one] = mem.allocarray(2 * len_edge_list, sizeof(size_t)) - # Set up face_iter.atom_repr - face_iter.set_atom_repr() + # Set up face_iter.atom_rep + face_iter.set_atom_rep() # Copy the information. - edges[one][2*two] = face_iter.atom_repr[0] - edges[one][2*two + 1] = face_iter.atom_repr[1] + edges[one][2*two] = face_iter.structure.atom_rep[0] + edges[one][2*two + 1] = face_iter.structure.atom_rep[1] counter += 1 # Success, copy the data to ``CombinatorialPolyhedron``. @@ -2134,12 +3004,12 @@ cdef class CombinatorialPolyhedron(SageObject): edges[one] = mem.allocarray(2 * len_edge_list, sizeof(size_t)) - # Set up face_iter.atom_repr - face_iter.set_atom_repr() + # Set up face_iter.atom_rep + face_iter.set_atom_rep() # Copy the information. - edges[one][2*two] = face_iter.atom_repr[0] - edges[one][2*two + 1] = face_iter.atom_repr[1] + edges[one][2*two] = face_iter.structure.atom_rep[0] + edges[one][2*two + 1] = face_iter.structure.atom_rep[1] counter += 1 d = face_iter.next_dimension() # Go to next face. @@ -2166,7 +3036,7 @@ cdef class CombinatorialPolyhedron(SageObject): r""" Compute the ridges of the polyhedron. - If ``dual`` is ``True``, compute the ridges of the polar. + If ``dual`` is ``True``, compute the ridges of the dual. See :meth:`edges` and :meth:`ridges`. """ @@ -2255,12 +3125,12 @@ cdef class CombinatorialPolyhedron(SageObject): ridges[one] = mem.allocarray(2 * len_ridge_list, sizeof(size_t)) - # Set up face_iter.coatom_repr - face_iter.set_coatom_repr() + # Set up face_iter.coatom_rep + face_iter.set_coatom_rep() # Copy the information. - ridges[one][2*two] = face_iter.coatom_repr[0] - ridges[one][2*two + 1] = face_iter.coatom_repr[1] + ridges[one][2*two] = face_iter.structure.coatom_rep[0] + ridges[one][2*two + 1] = face_iter.structure.coatom_rep[1] counter += 1 # Success, copy the data to ``CombinatorialPolyhedron``. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc b/src/sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc index aa042d1c0da..b9c88f8dc24 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc @@ -171,7 +171,7 @@ size_t get_next_level(\ return n_newfaces; } -size_t bit_repr_to_coatom_repr(uint64_t *face, uint64_t **coatoms, \ +size_t bit_rep_to_coatom_rep(uint64_t *face, uint64_t **coatoms, \ size_t n_coatoms, size_t face_length, \ size_t *output){ /* @@ -184,7 +184,7 @@ size_t bit_repr_to_coatom_repr(uint64_t *face, uint64_t **coatoms, \ for (size_t i = 0; i < n_coatoms; i++){ if (is_subset(face, coatoms[i], face_length)){ // ``face`` is contain in ``coatoms[i]``, - // then ``i`` is an element in the coatom-represention. + // then ``i`` is an element in the coatom-representation. output[count_length] = i; count_length++; } diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.pxd deleted file mode 100644 index e5533730820..00000000000 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.pxd +++ /dev/null @@ -1,63 +0,0 @@ -# distutils: language = c++ - -cimport cython -from libc.stdint cimport uint64_t - -cdef extern from "bit_vector_operations.cc": - # Any Bit-representation is assumed to be `chunksize`-Bit aligned. - cdef const size_t chunksize - 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 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_repr_to_coatom_repr( - 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``. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index ef5ea9c117c..2a9128e54f7 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd @@ -9,11 +9,11 @@ from .face_iterator cimport FaceIterator cdef class CombinatorialFace(SageObject): cdef readonly bint _dual # if 1, then iterate over dual Polyhedron cdef ListOfFaces face_mem # constructing face - cdef uint64_t *face # the face in bit-repr + cdef uint64_t *face # the face in bit-rep cdef MemoryAllocator _mem - cdef size_t *atom_repr # a place where atom-representaion of face will be stored - cdef size_t *coatom_repr # a place where coatom-representaion of face will be stored + cdef size_t *atom_rep # a place where atom-representation of face will be stored + cdef size_t *coatom_rep # a place where coatom-representation of face will be stored cdef int _dimension # dimension of current face, dual dimension if ``dual`` cdef int _ambient_dimension # dimension of the polyhedron cdef size_t face_length # stores length of the faces in terms of uint64_t @@ -27,5 +27,5 @@ cdef class CombinatorialFace(SageObject): cdef ListOfFaces atoms, coatoms cdef size_t n_atom_rep(self) except -1 - cdef size_t set_coatom_repr(self) except -1 - cdef size_t set_atom_repr(self) except -1 + cdef size_t set_coatom_rep(self) except -1 + cdef size_t set_atom_rep(self) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 08604d6c3d5..8e9b673c946 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -1,3 +1,7 @@ +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc +# distutils: language = c++ +# distutils: extra_compile_args = -std=c++11 + r""" Combinatorial face of a polyhedron @@ -68,12 +72,25 @@ from sage.misc.superseded import deprecated_function_alias import numbers from sage.rings.integer cimport smallInteger -from .conversions cimport bit_repr_to_Vrepr_list +from .conversions cimport bit_rep_to_Vrep_list from .base cimport CombinatorialPolyhedron -from .bit_vector_operations cimport count_atoms, bit_repr_to_coatom_repr 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 @@ -166,19 +183,19 @@ cdef class CombinatorialFace(SageObject): # Copy data from FaceIterator. it = data self._dual = it.dual - self.face_mem = ListOfFaces(1, it.face_length*64) + self.face_mem = ListOfFaces(1, it.structure.face_length*64) self.face = self.face_mem.data[0] - memcpy(self.face, it.face, it.face_length*8) + memcpy(self.face, it.structure.face, it.structure.face_length*8) self._mem = MemoryAllocator() - self._dimension = it.current_dimension - self._ambient_dimension = it.dimension - self.face_length = it.face_length + self._dimension = it.structure.current_dimension + self._ambient_dimension = it.structure.dimension + self.face_length = it.structure.face_length self._ambient_Vrep = it._Vrep self._ambient_facets = it._facet_names self._equalities = it._equalities self.atoms = it.atoms self.coatoms = it.coatoms - self._hash_index = it._index + self._hash_index = it.structure._index elif isinstance(data, PolyhedronFaceLattice): all_faces = data @@ -372,13 +389,13 @@ cdef class CombinatorialFace(SageObject): cdef size_t length if self._dual: # if dual, the Vrepresentation corresponds to the coatom-representation - length = self.set_coatom_repr() - return tuple(self._ambient_Vrep[self.coatom_repr[i]] + length = self.set_coatom_rep() + return tuple(self._ambient_Vrep[self.coatom_rep[i]] for i in range(length)) else: # if not dual, the Vrepresentation corresponds to the atom-representation - length = self.set_atom_repr() - return tuple(self._ambient_Vrep[self.atom_repr[i]] + length = self.set_atom_rep() + return tuple(self._ambient_Vrep[self.atom_rep[i]] for i in range(length)) def ambient_V_indices(self): @@ -422,13 +439,13 @@ cdef class CombinatorialFace(SageObject): cdef size_t length if self._dual: # if dual, the Vrepresentation corresponds to the coatom-representation - length = self.set_coatom_repr() - return tuple(smallInteger(self.coatom_repr[i]) + length = self.set_coatom_rep() + return tuple(smallInteger(self.coatom_rep[i]) for i in range(length)) else: # if not dual, the Vrepresentation corresponds to the atom-representation - length = self.set_atom_repr() - return tuple(smallInteger(self.atom_repr[i]) + length = self.set_atom_rep() + return tuple(smallInteger(self.atom_rep[i]) for i in range(length)) def Vrepr(self, names=True): @@ -493,7 +510,7 @@ cdef class CombinatorialFace(SageObject): See https://trac.sagemath.org/28614 for details. """ if self._dual: - return smallInteger(self.set_coatom_repr()) + return smallInteger(self.set_coatom_rep()) else: return smallInteger(self.n_atom_rep()) @@ -548,14 +565,14 @@ cdef class CombinatorialFace(SageObject): return self.ambient_H_indices() cdef size_t length if not self._dual: - # if not dual, the facet-represention corresponds to the coatom-representation - length = self.set_coatom_repr() # fill self.coatom_rep_face - return tuple(self._ambient_facets[self.coatom_repr[i]] + # if not dual, the facet-representation corresponds to the coatom-representation + length = self.set_coatom_rep() # fill self.coatom_repr_face + return tuple(self._ambient_facets[self.coatom_rep[i]] for i in range(length)) + self._equalities else: - # if dual, the facet-represention corresponds to the atom-representation - length = self.set_atom_repr() # fill self.atom_rep_face - return tuple(self._ambient_facets[self.atom_repr[i]] + # if dual, the facet-representation corresponds to the atom-representation + length = self.set_atom_rep() # fill self.atom_repr_face + return tuple(self._ambient_facets[self.atom_rep[i]] for i in range(length)) + self._equalities def ambient_H_indices(self): @@ -595,14 +612,14 @@ cdef class CombinatorialFace(SageObject): """ cdef size_t length if not self._dual: - # if not dual, the facet-represention corresponds to the coatom-representation - length = self.set_coatom_repr() # fill self.coatom_rep_face - return tuple(smallInteger(self.coatom_repr[i]) + # if not dual, the facet-representation corresponds to the coatom-representation + length = self.set_coatom_rep() # fill self.coatom_repr_face + return tuple(smallInteger(self.coatom_rep[i]) for i in range(length)) else: - # if dual, the facet-represention corresponds to the atom-representation - length = self.set_atom_repr() # fill self.atom_rep_face - return tuple(smallInteger(self.atom_repr[i]) + # if dual, the facet-representation corresponds to the atom-representation + length = self.set_atom_rep() # fill self.atom_repr_face + return tuple(smallInteger(self.atom_rep[i]) for i in range(length)) def Hrepr(self, names=True): @@ -670,32 +687,12 @@ cdef class CombinatorialFace(SageObject): See https://trac.sagemath.org/28614 for details. """ if not self._dual: - return smallInteger(self.set_coatom_repr()) + return smallInteger(self.set_coatom_rep()) else: return smallInteger(self.n_atom_rep()) n_Hrepr = deprecated_function_alias(28614, n_ambient_Hrepresentation) - def n_Hrepr(self): - r""" - .. SEEALSO:: - - :meth:`CombinatorialFace.n_ambient_Hrepresentation` - - TESTS:: - - sage: P = polytopes.cube() - sage: C = CombinatorialPolyhedron(P) - sage: it = C.face_iter() - sage: face = next(it) - sage: _ = face.n_Hrepr() - doctest:...: DeprecationWarning: n_Hrepr is deprecated. Please use n_ambient_Hrepresentation instead. - See https://trac.sagemath.org/28614 for details. - """ - from sage.misc.superseded import deprecation - deprecation(28614, "the method n_Hrepr of CombinatorialFace is deprecated") - return self.n_ambient_Hrepresentation() - cdef size_t n_atom_rep(self) except -1: r""" Compute the number of atoms in the current face by counting the @@ -707,26 +704,26 @@ cdef class CombinatorialFace(SageObject): # The face was not initialized properly. raise LookupError("``FaceIterator`` does not point to a face") - cdef size_t set_coatom_repr(self) except -1: + cdef size_t set_coatom_rep(self) except -1: r""" - Set ``coatom_repr`` to be the coatom-representation of the current face. + Set ``coatom_rep`` to be the coatom-representation of the current face. Return its length. """ cdef size_t n_coatoms = self.coatoms.n_faces cdef uint64_t **coatoms = self.coatoms.data cdef size_t face_length = self.face_length - if not self.coatom_repr: - self.coatom_repr = self._mem.allocarray(self.coatoms.n_faces, sizeof(size_t)) - return bit_repr_to_coatom_repr(self.face, coatoms, n_coatoms, - face_length, self.coatom_repr) + if not self.coatom_rep: + self.coatom_rep = self._mem.allocarray(self.coatoms.n_faces, sizeof(size_t)) + return bit_rep_to_coatom_rep(self.face, coatoms, n_coatoms, + face_length, self.coatom_rep) - cdef size_t set_atom_repr(self) except -1: + cdef size_t set_atom_rep(self) except -1: r""" - Set ``atom_repr`` to be the atom-representation of the current face. + Set ``atom_rep`` to be the atom-representation of the current face. Return its length. """ cdef size_t face_length = self.face_length - if not self.atom_repr: - self.atom_repr = self._mem.allocarray(self.coatoms.n_atoms, sizeof(size_t)) - return bit_repr_to_Vrepr_list(self.face, self.atom_repr, face_length) + if not self.atom_rep: + self.atom_rep = self._mem.allocarray(self.coatoms.n_atoms, sizeof(size_t)) + return bit_rep_to_Vrep_list(self.face, self.atom_rep, face_length) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd index 7ba717c3736..e36e0c2eb73 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd @@ -1,10 +1,10 @@ from libc.stdint cimport uint64_t -cdef int Vrepr_list_to_bit_repr(tuple Vrepr_list, uint64_t *output, +cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, size_t face_length) except -1 -cdef int incidences_to_bit_repr(tuple incidences, uint64_t *output, +cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, size_t face_length) except -1 -cdef size_t bit_repr_to_Vrepr_list(uint64_t *face, size_t *output, +cdef size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, size_t face_length) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx index 300b699b4ec..124858e6be3 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx @@ -20,10 +20,12 @@ EXAMPLES: Obtain the facets of a polyhedron as :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces`:: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import incidence_matrix_to_bit_repr_of_facets + ....: import incidence_matrix_to_bit_rep_of_facets sage: P = polytopes.simplex(4) - sage: face_list = incidence_matrix_to_bit_repr_of_facets(P.incidence_matrix()) - sage: face_list = incidence_matrix_to_bit_repr_of_facets(P.incidence_matrix()) + sage: inc = P.incidence_matrix() + sage: mod_inc = inc.delete_columns([i for i,V in enumerate(P.Hrepresentation()) if V.is_equation()]) + sage: face_list = incidence_matrix_to_bit_rep_of_facets(mod_inc) + sage: face_list = incidence_matrix_to_bit_rep_of_facets(mod_inc) sage: face_list.compute_dimension() 4 @@ -31,25 +33,25 @@ Obtain the Vrepresentation of a polyhedron as facet-incidences stored in :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces`:: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import incidence_matrix_to_bit_repr_of_Vrepr + ....: import incidence_matrix_to_bit_rep_of_Vrep sage: P = polytopes.associahedron(['A',4]) - sage: face_list = incidence_matrix_to_bit_repr_of_Vrepr(P.incidence_matrix()) + sage: face_list = incidence_matrix_to_bit_rep_of_Vrep(P.incidence_matrix()) sage: face_list.compute_dimension() 4 Obtain the facets of a polyhedron as :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces` from a facet list:: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import facets_tuple_to_bit_repr_of_facets + ....: import facets_tuple_to_bit_rep_of_facets sage: facets = ((0,1,2), (0,1,3), (0,2,3), (1,2,3)) - sage: face_list = facets_tuple_to_bit_repr_of_facets(facets, 4) + sage: face_list = facets_tuple_to_bit_rep_of_facets(facets, 4) -Likewise for the Vrepr as facet-incidences:: +Likewise for the Vrep as facet-incidences:: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import facets_tuple_to_bit_repr_of_Vrepr + ....: import facets_tuple_to_bit_rep_of_Vrep sage: facets = ((0,1,2), (0,1,3), (0,2,3), (1,2,3)) - sage: face_list = facets_tuple_to_bit_repr_of_Vrepr(facets, 4) + sage: face_list = facets_tuple_to_bit_rep_of_Vrep(facets, 4) AUTHOR: @@ -68,8 +70,10 @@ AUTHOR: from sage.structure.element import is_Matrix -from libc.string cimport memset -from .list_of_faces cimport ListOfFaces +from libc.string cimport memset +from .list_of_faces cimport ListOfFaces +from sage.misc.superseded import deprecated_function_alias +from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -82,7 +86,7 @@ cdef inline uint64_t vertex_to_bit_dictionary(size_t i): """ return (1) << (64 - i - 1) -cdef int Vrepr_list_to_bit_repr(tuple Vrepr_list, uint64_t *output, +cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, size_t face_length) except -1: r""" Convert a vertex list into Bit-representation. Store it in ``output``. @@ -106,38 +110,38 @@ cdef int Vrepr_list_to_bit_repr(tuple Vrepr_list, uint64_t *output, sage: cython(''' ....: from libc.stdint cimport uint64_t ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport Vrepr_list_to_bit_repr + ....: cimport Vrep_list_to_bit_rep ....: from sage.ext.memory_allocator cimport MemoryAllocator ....: from sage.rings.integer cimport smallInteger ....: - ....: def Vrepr_list_to_bit_repr_wrapper(tup): + ....: def Vrep_list_to_bit_rep_wrapper(tup): ....: cdef size_t face_length = max(tup)//64 + 1 ....: cdef MemoryAllocator mem = MemoryAllocator() ....: cdef uint64_t *output = mem.allocarray(face_length, 8) - ....: Vrepr_list_to_bit_repr(tup, output, face_length) + ....: Vrep_list_to_bit_rep(tup, output, face_length) ....: return tuple(smallInteger(output[i]) for i in range(face_length)) ....: - ....: def Vrepr_list_to_bit_repr_wrong_size(tup): + ....: def Vrep_list_to_bit_rep_wrong_size(tup): ....: cdef size_t face_length = 1 ....: cdef MemoryAllocator mem = MemoryAllocator() ....: cdef uint64_t *output = mem.allocarray(face_length, 8) - ....: Vrepr_list_to_bit_repr(tup, output, face_length) + ....: Vrep_list_to_bit_rep(tup, output, face_length) ....: return tuple(smallInteger(output[i]) for i in range(face_length)) ....: ''') # long time - sage: Vrepr_list_to_bit_repr_wrapper((62, 63)) # long time + sage: Vrep_list_to_bit_rep_wrapper((62, 63)) # long time (3,) - sage: Vrepr_list_to_bit_repr_wrapper((61, 63, 125)) # long time + sage: Vrep_list_to_bit_rep_wrapper((61, 63, 125)) # long time (5, 4) - sage: Vrepr_list_to_bit_repr_wrong_size((62, 70)) # long time + sage: Vrep_list_to_bit_rep_wrong_size((62, 70)) # long time Traceback (most recent call last): ... IndexError: output too small to represent 70 - sage: Vrepr_list_to_bit_repr_wrapper((-1, 12)) # long time + sage: Vrep_list_to_bit_rep_wrapper((-1, 12)) # long time Traceback (most recent call last): ... - OverflowError: can't convert negative value to size_t - sage: Vrepr_list_to_bit_repr_wrapper((0, 0)) # long time + OverflowError: can...t convert negative value to size_t + sage: Vrep_list_to_bit_rep_wrapper((0, 0)) # long time Traceback (most recent call last): ... ValueError: entries of ``tup`` are not distinct @@ -147,16 +151,16 @@ cdef int Vrepr_list_to_bit_repr(tuple Vrepr_list, uint64_t *output, cdef size_t value # determines which bit will be set in output[position] memset(output, 0, face_length*8) # initialize output - if unlikely(len(Vrepr_list) != len(set(Vrepr_list))): + if unlikely(len(Vrep_list) != len(set(Vrep_list))): raise ValueError("entries of ``tup`` are not distinct") - for entry in Vrepr_list: + for entry in Vrep_list: value = entry % 64 position = entry//64 if unlikely(position >= face_length): raise IndexError("output too small to represent %s"%entry) output[position] += vertex_to_bit_dictionary(value) -cdef int incidences_to_bit_repr(tuple incidences, uint64_t *output, +cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, size_t face_length) except -1: r""" @@ -181,35 +185,35 @@ cdef int incidences_to_bit_repr(tuple incidences, uint64_t *output, sage: cython(''' ....: from libc.stdint cimport uint64_t ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport incidences_to_bit_repr + ....: cimport incidences_to_bit_rep ....: from sage.ext.memory_allocator cimport MemoryAllocator ....: from sage.rings.integer cimport smallInteger ....: - ....: def incidences_to_bit_reprs_wrapper(tup): + ....: def incidences_to_bit_reps_wrapper(tup): ....: cdef size_t face_length = (len(tup)-1)//64 + 1 ....: cdef MemoryAllocator mem = MemoryAllocator() ....: cdef uint64_t *output = \ ....: mem.allocarray(face_length, 8) - ....: incidences_to_bit_repr(tup, output, face_length) + ....: incidences_to_bit_rep(tup, output, face_length) ....: return tuple(smallInteger(output[i]) for i in range(face_length)) ....: - ....: def incidences_to_bit_reprs_wrong_size(tup): + ....: def incidences_to_bit_reps_wrong_size(tup): ....: cdef size_t face_length = 1 ....: cdef MemoryAllocator mem = MemoryAllocator() ....: cdef uint64_t *output = \ ....: mem.allocarray(face_length, 8) - ....: incidences_to_bit_repr(tup, output, face_length) + ....: incidences_to_bit_rep(tup, output, face_length) ....: return tuple(smallInteger(output[i]) for i in range(face_length)) ....: ''') # long time - sage: incidences_to_bit_reprs_wrapper((0,) * 62 + (1,1)) # long time + sage: incidences_to_bit_reps_wrapper((0,) * 62 + (1,1)) # long time (3,) - sage: incidences_to_bit_reprs_wrapper((0,) * 61 + (1,0,1) + # long time + sage: incidences_to_bit_reps_wrapper((0,) * 61 + (1,0,1) + # long time ....: (0,) * 61 + (1,)) (5, 4) - sage: incidences_to_bit_reprs_wrapper((1,) * 64) # long time + sage: incidences_to_bit_reps_wrapper((1,) * 64) # long time (-1,) - sage: incidences_to_bit_reprs_wrong_size((1,) * 70) # long time + sage: incidences_to_bit_reps_wrong_size((1,) * 70) # long time Traceback (most recent call last): ... IndexError: output too small to represent all incidences @@ -224,12 +228,12 @@ cdef int incidences_to_bit_repr(tuple incidences, uint64_t *output, raise IndexError("output too small to represent all incidences") for entry in range(length): if incidences[entry]: - # Vrepr ``entry`` is contained in the face, so set the corresponding bit + # Vrep ``entry`` is contained in the face, so set the corresponding bit value = entry % 64 position = entry//64 output[position] += vertex_to_bit_dictionary(value) -def incidence_matrix_to_bit_repr_of_facets(matrix): +def incidence_matrix_to_bit_rep_of_facets(Matrix_integer_dense matrix): r""" Initialize facets in Bit-representation as :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces`. @@ -237,6 +241,8 @@ def incidence_matrix_to_bit_repr_of_facets(matrix): - ``matrix`` -- an incidence matrix as in :meth:`sage.geometry.polyhedron.base.Polyhedron_base.incidence_matrix` + with columns corresponding to equations deleted + of type :class:`sage.matrix.matrix_integer_dense.Matrix_integer_dense` OUTPUT: @@ -248,33 +254,34 @@ def incidence_matrix_to_bit_repr_of_facets(matrix): ....: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ ....: cimport ListOfFaces ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport bit_repr_to_Vrepr_list + ....: cimport bit_rep_to_Vrep_list ....: from sage.rings.integer cimport smallInteger ....: from sage.ext.memory_allocator cimport MemoryAllocator ....: from libc.stdint cimport uint64_t ....: - ....: def bit_repr_to_Vrepr_list_wrapper(list_of_faces, index): + ....: def bit_rep_to_Vrep_list_wrapper(ListOfFaces faces, index): ....: cdef MemoryAllocator mem = MemoryAllocator() ....: cdef size_t *output - ....: cdef ListOfFaces faces = list_of_faces ....: output = mem.allocarray(faces.n_atoms, ....: sizeof(size_t)) ....: cdef uint64_t * data = faces.data[index] - ....: length = bit_repr_to_Vrepr_list( + ....: length = bit_rep_to_Vrep_list( ....: data, output, faces.face_length) ....: return tuple(smallInteger(output[i]) for i in range(length)) ....: ''') # long time sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import incidence_matrix_to_bit_repr_of_facets + ....: import incidence_matrix_to_bit_rep_of_facets sage: P = polytopes.permutahedron(4) - sage: facets = incidence_matrix_to_bit_repr_of_facets(P.incidence_matrix()) + sage: inc = P.incidence_matrix() + sage: mod_inc = inc.delete_columns([i for i,V in enumerate(P.Hrepresentation()) if V.is_equation()]) + sage: facets = incidence_matrix_to_bit_rep_of_facets(mod_inc) sage: facets.n_faces 14 sage: facets.n_atoms 24 sage: for i in range(facets.n_faces): # long time - ....: print(bit_repr_to_Vrepr_list_wrapper(facets, i)) + ....: print(bit_rep_to_Vrep_list_wrapper(facets, i)) (18, 19, 20, 21, 22, 23) (9, 11, 15, 17, 21, 23) (16, 17, 22, 23) @@ -290,31 +297,35 @@ def incidence_matrix_to_bit_repr_of_facets(matrix): (8, 10, 14, 16, 20, 22) (13, 15, 19, 21) """ - - if unlikely(not is_Matrix(matrix)): - raise ValueError("input must be matrix") - - # The incidence-matrix of a polyhedron, might contain columns with all 1's. - # Those correspond to hyperplanes, the polyhedron lies in, not facets. - # We delete those from the matrix. Also we transpose. - matrix = matrix.transpose() - rg = range(matrix.nrows()) - matrix = matrix[list(i for i in rg if not all(j for j in matrix[i]))] - - # Output will be a ``ListOfFaces`` with ``matrix.nrows()`` faces and - # ``matrix.ncols()`` Vrepr. - cdef size_t length = matrix.nrows() - cdef ListOfFaces facets = ListOfFaces(length, matrix.ncols()) + # Output will be a ``ListOfFaces`` with ``matrix.ncols()`` faces and + # ``matrix.nrows()`` Vrep. + cdef size_t nrows = matrix._nrows + cdef size_t ncols = matrix._ncols + cdef ListOfFaces facets = ListOfFaces(ncols, nrows) cdef uint64_t **facets_data = facets.data + cdef uint64_t *output + cdef size_t face_length = facets.face_length + cdef size_t entry # index for the entries in tup + cdef size_t position # determines the position in output of entry + cdef size_t value # determines which bit will be set in output[position] cdef size_t i - for i in range(length): - # Filling each facet with its Vrepr-incidences, which "is" the + for i in range(ncols): + output = facets_data[i] + memset(output, 0, face_length*8) #initialize + + # Filling each facet with its Vrep-incidences, which "is" the # "i-th column" of the original matrix (but we have transposed). - incidences_to_bit_repr(tuple(matrix[i]), facets_data[i], facets.face_length) + for entry in range(nrows): + if not matrix.get_is_zero_unsafe(entry, i): + # Vrep ``entry`` is contained in the face, so set the corresponding bit + value = entry % 64 + position = entry//64 + output[position] += vertex_to_bit_dictionary(value) return facets +incidence_matrix_to_bit_repr_of_facets = deprecated_function_alias(28608, incidence_matrix_to_bit_rep_of_facets) -def incidence_matrix_to_bit_repr_of_Vrepr(matrix): +def incidence_matrix_to_bit_rep_of_Vrep(Matrix_integer_dense matrix): r""" Initialize Vrepresentatives in Bit-representation as :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces`. @@ -325,6 +336,8 @@ def incidence_matrix_to_bit_repr_of_Vrepr(matrix): - ``matrix`` -- an incidence matrix as in :meth:`sage.geometry.polyhedron.base.Polyhedron_base.incidence_matrix` + with columns corresponding to equations deleted + of type :class:`sage.matrix.matrix_integer_dense.Matrix_integer_dense` OUTPUT: @@ -336,33 +349,34 @@ def incidence_matrix_to_bit_repr_of_Vrepr(matrix): ....: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ ....: cimport ListOfFaces ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport bit_repr_to_Vrepr_list + ....: cimport bit_rep_to_Vrep_list ....: from sage.rings.integer cimport smallInteger ....: from sage.ext.memory_allocator cimport MemoryAllocator ....: from libc.stdint cimport uint64_t ....: - ....: def bit_repr_to_Vrepr_list_wrapper(list_of_faces, index): + ....: def bit_rep_to_Vrep_list_wrapper(ListOfFaces faces, index): ....: cdef MemoryAllocator mem = MemoryAllocator() ....: cdef size_t *output - ....: cdef ListOfFaces faces = list_of_faces ....: output = mem.allocarray(faces.n_atoms, ....: sizeof(size_t)) ....: cdef uint64_t * data = faces.data[index] - ....: length = bit_repr_to_Vrepr_list( + ....: length = bit_rep_to_Vrep_list( ....: data, output, faces.face_length) ....: return tuple(smallInteger(output[i]) for i in range(length)) ....: ''') sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import incidence_matrix_to_bit_repr_of_Vrepr + ....: import incidence_matrix_to_bit_rep_of_Vrep sage: P = polytopes.permutahedron(4) - sage: vertices = incidence_matrix_to_bit_repr_of_Vrepr(P.incidence_matrix()) + sage: inc = P.incidence_matrix() + sage: mod_inc = inc.delete_columns([i for i,V in enumerate(P.Hrepresentation()) if V.is_equation()]) + sage: vertices = incidence_matrix_to_bit_rep_of_Vrep(mod_inc) sage: vertices.n_faces 24 sage: vertices.n_atoms 14 sage: for i in range(vertices.n_faces): - ....: print(bit_repr_to_Vrepr_list_wrapper(vertices, i)) + ....: print(bit_rep_to_Vrep_list_wrapper(vertices, i)) (3, 5, 9) (3, 5, 8) (3, 4, 9) @@ -388,38 +402,18 @@ def incidence_matrix_to_bit_repr_of_Vrepr(matrix): (0, 2, 12) (0, 1, 2) """ - if unlikely(not is_Matrix(matrix)): - raise ValueError("input must be matrix") - - # The incidence-matrix of a polyhedron, might contain columns with all 1's. - # Those correspond to hyperplanes, the polyhedron lies in, not facets. - # We delete those from the matrix. Also we transpose. - matrix = matrix.transpose() - rg = range(matrix.nrows()) - matrix = matrix[list(i for i in rg if not all(j for j in matrix[i]))] - - # Output will be a ``ListOfFaces`` with ``matrix.ncols()`` faces and - # ``matrix.nrows()`` Vrepr. - cdef size_t length = matrix.ncols() - cdef ListOfFaces Vrepr = ListOfFaces(length, matrix.nrows()) - cdef uint64_t **Vrepr_data = Vrepr.data - - cdef size_t i - for i in range(length): - # Filling each facet with its Vrepr-incidences, which "is" the - # "i-th row" of the original matrix (but we have transposed). - incidences_to_bit_repr(tuple(matrix.column(i)), Vrepr_data[i], Vrepr.face_length) - return Vrepr + return incidence_matrix_to_bit_rep_of_facets(matrix.transpose()) +incidence_matrix_to_bit_repr_of_Vrepr = deprecated_function_alias(28608, incidence_matrix_to_bit_rep_of_Vrep) -def facets_tuple_to_bit_repr_of_facets(tuple facets_input, size_t n_Vrepr): +def facets_tuple_to_bit_rep_of_facets(tuple facets_input, size_t n_Vrep): r""" Initializes facets in Bit-representation as :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces`. INPUT: - - ``facets_input`` -- tuple of facets, each facet a tuple of Vrepr, - Vrepr must be exactly ``range(n_Vrepr)`` - - ``n_Vrepr`` + - ``facets_input`` -- tuple of facets, each facet a tuple of Vrep, + Vrep must be exactly ``range(n_Vrep)`` + - ``n_Vrep`` OUTPUT: @@ -431,30 +425,29 @@ def facets_tuple_to_bit_repr_of_facets(tuple facets_input, size_t n_Vrepr): ....: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ ....: cimport ListOfFaces ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport bit_repr_to_Vrepr_list + ....: cimport bit_rep_to_Vrep_list ....: from sage.rings.integer cimport smallInteger ....: from sage.ext.memory_allocator cimport MemoryAllocator ....: from libc.stdint cimport uint64_t ....: - ....: def bit_repr_to_Vrepr_list_wrapper(list_of_faces, index): + ....: def bit_rep_to_Vrep_list_wrapper(ListOfFaces faces, index): ....: cdef MemoryAllocator mem = MemoryAllocator() ....: cdef size_t *output - ....: cdef ListOfFaces faces = list_of_faces ....: output = mem.allocarray(faces.n_atoms, ....: sizeof(size_t)) ....: cdef uint64_t * data = faces.data[index] - ....: length = bit_repr_to_Vrepr_list( + ....: length = bit_rep_to_Vrep_list( ....: data, output, faces.face_length) ....: return tuple(smallInteger(output[i]) for i in range(length)) ....: ''') # long time sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import facets_tuple_to_bit_repr_of_facets + ....: import facets_tuple_to_bit_rep_of_facets sage: bi_pyr = ((0,1,4), (1,2,4), (2,3,4), (3,0,4), ....: (0,1,5), (1,2,5), (2,3,5), (3,0,5)) - sage: facets = facets_tuple_to_bit_repr_of_facets(bi_pyr, 6) + sage: facets = facets_tuple_to_bit_rep_of_facets(bi_pyr, 6) sage: for i in range(8): # long time - ....: print(bit_repr_to_Vrepr_list_wrapper(facets, i)) + ....: print(bit_rep_to_Vrep_list_wrapper(facets, i)) (0, 1, 4) (1, 2, 4) (2, 3, 4) @@ -465,15 +458,16 @@ def facets_tuple_to_bit_repr_of_facets(tuple facets_input, size_t n_Vrepr): (0, 3, 5) """ cdef Py_ssize_t i - cdef ListOfFaces facets = ListOfFaces(len(facets_input), n_Vrepr) + cdef ListOfFaces facets = ListOfFaces(len(facets_input), n_Vrep) cdef size_t face_length = facets.face_length cdef uint64_t **facets_data = facets.data for i in range(len(facets_input)): # filling each facet with the data from the corresponding facet - Vrepr_list_to_bit_repr(facets_input[i], facets_data[i], face_length) + Vrep_list_to_bit_rep(facets_input[i], facets_data[i], face_length) return facets +facets_tuple_to_bit_repr_of_facets = deprecated_function_alias(28608, facets_tuple_to_bit_rep_of_facets) -def facets_tuple_to_bit_repr_of_Vrepr(tuple facets_input, size_t n_Vrepr): +def facets_tuple_to_bit_rep_of_Vrep(tuple facets_input, size_t n_Vrep): r""" Initialize Vrepresentatives in Bit-representation as :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces`. @@ -482,9 +476,9 @@ def facets_tuple_to_bit_repr_of_Vrepr(tuple facets_input, size_t n_Vrepr): INPUT: - - ``facets_input`` -- tuple of facets, each facet a tuple of Vrepr, - Vrepr must be exactly ``range(n_Vrepr)`` - - ``n_Vrepr`` + - ``facets_input`` -- tuple of facets, each facet a tuple of Vrep, + Vrep must be exactly ``range(n_Vrep)`` + - ``n_Vrep`` OUTPUT: @@ -497,30 +491,29 @@ def facets_tuple_to_bit_repr_of_Vrepr(tuple facets_input, size_t n_Vrepr): ....: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ ....: cimport ListOfFaces ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport bit_repr_to_Vrepr_list + ....: cimport bit_rep_to_Vrep_list ....: from sage.rings.integer cimport smallInteger ....: from sage.ext.memory_allocator cimport MemoryAllocator ....: from libc.stdint cimport uint64_t ....: - ....: def bit_repr_to_Vrepr_list_wrapper(list_of_faces, index): + ....: def bit_rep_to_Vrep_list_wrapper(ListOfFaces faces, index): ....: cdef MemoryAllocator mem = MemoryAllocator() ....: cdef size_t *output - ....: cdef ListOfFaces faces = list_of_faces ....: output = mem.allocarray(faces.n_atoms, ....: sizeof(size_t)) ....: cdef uint64_t * data = faces.data[index] - ....: length = bit_repr_to_Vrepr_list( + ....: length = bit_rep_to_Vrep_list( ....: data, output, faces.face_length) ....: return tuple(smallInteger(output[i]) for i in range(length)) ....: ''') sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import facets_tuple_to_bit_repr_of_Vrepr + ....: import facets_tuple_to_bit_rep_of_Vrep sage: bi_pyr = ((0,1,4), (1,2,4), (2,3,4), (3,0,4), ....: (0,1,5), (1,2,5), (2,3,5), (3,0,5)) - sage: vertices = facets_tuple_to_bit_repr_of_Vrepr(bi_pyr, 6) + sage: vertices = facets_tuple_to_bit_rep_of_Vrep(bi_pyr, 6) sage: for i in range(6): - ....: print(bit_repr_to_Vrepr_list_wrapper(vertices, i)) + ....: print(bit_rep_to_Vrep_list_wrapper(vertices, i)) (0, 3, 4, 7) (0, 1, 4, 5) (1, 2, 5, 6) @@ -531,50 +524,51 @@ def facets_tuple_to_bit_repr_of_Vrepr(tuple facets_input, size_t n_Vrepr): cdef size_t n_facets = len(facets_input) # Vertices in facet-representation will be a ``ListOfFaces`` - # with number of Vrepr faces and - # number of facets "Vrepr"/atoms. - cdef ListOfFaces Vrepr = ListOfFaces(n_Vrepr, n_facets) - cdef uint64_t **Vrepr_data = Vrepr.data + # with number of Vrep faces and + # number of facets "Vrep"/atoms. + cdef ListOfFaces Vrep = ListOfFaces(n_Vrep, n_facets) + cdef uint64_t **Vrep_data = Vrep.data # Initializing the data of ListOfFaces. - cdef size_t face_length = Vrepr.face_length + cdef size_t face_length = Vrep.face_length cdef size_t i - for i in range(n_Vrepr): - memset(Vrepr_data[i], 0, face_length*8) + for i in range(n_Vrep): + memset(Vrep_data[i], 0, face_length*8) cdef size_t input_facet # will iterate over indices of facets_input cdef size_t position # determines the position in output of entry cdef size_t value # determines which bit will be set in output[position] - cdef size_t input_Vrepr # will iterate over vertices in facet ``input_facet`` + cdef size_t input_Vrep # will iterate over vertices in facet ``input_facet`` for input_facet in range(n_facets): value = input_facet % 64 position = input_facet//64 - for input_Vrepr in facets_input[input_facet]: - # Iff the input-Vrepr is in the input-facet, - # then in facet-representation of the Vrepr - # input-facet is a Vrepr of intput-Vrepr. - Vrepr_data[input_Vrepr][position] += vertex_to_bit_dictionary(value) - return Vrepr - -cdef inline size_t bit_repr_to_Vrepr_list(uint64_t *face, size_t *output, + for input_Vrep in facets_input[input_facet]: + # Iff the input-Vrep is in the input-facet, + # then in facet-representation of the Vrep + # input-facet is a Vrep of intput-Vrep. + Vrep_data[input_Vrep][position] += vertex_to_bit_dictionary(value) + return Vrep +facets_tuple_to_bit_repr_of_Vrepr = deprecated_function_alias(28608, facets_tuple_to_bit_rep_of_Vrep) + +cdef inline size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, size_t face_length) except -1: r""" - Convert a bitrep-representation to a list of Vrepr. Return length of representation. + Convert a bitrep-representation to a list of Vrep. Return length of representation. - Basically this is an inverse to :meth:`Vrepr_list_to_bit_repr`. - Instead of returning a tuple, it stores the Vrepr in ``output``. + Basically this is an inverse to :meth:`Vrep_list_to_bit_rep`. + Instead of returning a tuple, it stores the Vrep in ``output``. INPUT: - ``face`` -- a Bit-representation of a face - - ``output`` -- an array of ``size_t`` long enough to contain all Vrepr + - ``output`` -- an array of ``size_t`` long enough to contain all Vrep of that face (``face_length*64`` will suffice) - ``face_length`` -- the length of ``face`` OUTPUT: - - store Vrepr in ``output`` + - store Vrep in ``output`` - return "length" of ``output`` EXAMPLES:: @@ -583,12 +577,12 @@ cdef inline size_t bit_repr_to_Vrepr_list(uint64_t *face, size_t *output, ....: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ ....: cimport ListOfFaces ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport bit_repr_to_Vrepr_list, Vrepr_list_to_bit_repr + ....: cimport bit_rep_to_Vrep_list, Vrep_list_to_bit_rep ....: from sage.rings.integer cimport smallInteger ....: from sage.ext.memory_allocator cimport MemoryAllocator ....: from libc.stdint cimport uint64_t ....: - ....: def bit_repr_to_Vrepr_list_wrapper(tup): + ....: def bit_rep_to_Vrep_list_wrapper(tup): ....: cdef MemoryAllocator mem = MemoryAllocator() ....: cdef size_t *output ....: cdef length = len(tup) @@ -598,27 +592,27 @@ cdef inline size_t bit_repr_to_Vrepr_list(uint64_t *face, size_t *output, ....: data = mem.allocarray(length, 8) ....: for i in range(len(tup)): ....: data[i] = tup[i] - ....: outputlength = bit_repr_to_Vrepr_list( + ....: outputlength = bit_rep_to_Vrep_list( ....: data, output, length) ....: return tuple(smallInteger(output[i]) for i in range(outputlength)) ....: ''') # long time - sage: bit_repr_to_Vrepr_list_wrapper((17, 31)) # long time + sage: bit_rep_to_Vrep_list_wrapper((17, 31)) # long time (59, 63, 123, 124, 125, 126, 127) - sage: bit_repr_to_Vrepr_list_wrapper((13,)) # long time + sage: bit_rep_to_Vrep_list_wrapper((13,)) # long time (60, 61, 63) - sage: bit_repr_to_Vrepr_list_wrapper((0, 61)) # long time + sage: bit_rep_to_Vrep_list_wrapper((0, 61)) # long time (122, 123, 124, 125, 127) TESTS: - Testing that :meth`bit_repr_to_Vrepr_list` is the - inverse to :meth:`Vrepr_list_to_bit_repr`:: + Testing that :meth`bit_rep_to_Vrep_list` is the + inverse to :meth:`Vrep_list_to_bit_rep`:: sage: cython(''' ....: from libc.stdint cimport uint64_t ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport bit_repr_to_Vrepr_list, Vrepr_list_to_bit_repr + ....: cimport bit_rep_to_Vrep_list, Vrep_list_to_bit_rep ....: from sage.misc.prandom import randint ....: ....: cdef uint64_t[2] face @@ -628,12 +622,12 @@ cdef inline size_t bit_repr_to_Vrepr_list(uint64_t *face, size_t *output, ....: for _ in range(10): ....: st = set(randint(0,127) for i in range(40)) ....: tup = tuple(sorted(tuple(st))) - ....: Vrepr_list_to_bit_repr(tup, face, 2) - ....: length = bit_repr_to_Vrepr_list(face, output, 2) + ....: Vrep_list_to_bit_rep(tup, face, 2) + ....: length = bit_rep_to_Vrep_list(face, output, 2) ....: tup2 = tuple(output[i] for i in range(length)) ....: if not tup == tup2: - ....: print('``bit_repr_to_Vrepr_list`` does not behave', - ....: 'as the inverse of ``Vrepr_list_to_bit_repr``') + ....: print('``bit_rep_to_Vrep_list`` does not behave', + ....: 'as the inverse of ``Vrep_list_to_bit_rep``') ....: ''') # long time """ cdef size_t i, j @@ -645,7 +639,7 @@ cdef inline size_t bit_repr_to_Vrepr_list(uint64_t *face, size_t *output, for j in range(64): if copy >= vertex_to_bit_dictionary(j): # Then face[i] has the j-th bit set to 1. - # This corresponds to face containing Vrepr i*64 + j. + # This corresponds to face containing Vrep i*64 + j. output[output_length] = i*64 + j output_length += 1 copy -= vertex_to_bit_dictionary(j) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 14e5253c98f..4b601ec4217 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -5,27 +5,17 @@ from sage.structure.sage_object cimport SageObject from .list_of_faces cimport ListOfFaces from .combinatorial_face cimport CombinatorialFace -@cython.final -cdef class FaceIterator(SageObject): - cdef readonly bint dual # if 1, then iterate over dual Polyhedron - cdef uint64_t *face # the current face of the iterator - cdef size_t *atom_repr # a place where atom-representaion of face will be stored - cdef size_t *coatom_repr # a place where coatom-representaion of face will be stored - cdef int current_dimension # dimension of current face, dual dimension if ``dual`` - cdef int dimension # dimension of the polyhedron - cdef int output_dimension # only faces of this (dual?) dimension are considered - cdef int lowest_dimension # don't consider faces below this (dual?) dimension - cdef size_t _index # this counts the number of seen faces, useful for hasing the faces - cdef MemoryAllocator _mem - cdef tuple newfaces_lists # tuple to hold the ListOfFaces corresponding to maybe_newfaces - cdef size_t face_length # stores length of the faces in terms of uint64_t - - # some copies from ``CombinatorialPolyhedron`` - cdef tuple _Vrep, _facet_names, _equalities - - # Atoms and coatoms are the vertices/facets of the Polyedron. - # If ``dual == 0``, then coatoms are facets, atoms vertices and vice versa. - cdef ListOfFaces atoms, coatoms +cdef struct iter_struct: + bint dual # if 1, then iterate over dual Polyhedron + uint64_t *face # the current face of the iterator + size_t *atom_rep # a place where atom-representaion of face will be stored + size_t *coatom_rep # a place where coatom-representaion of face will be stored + int current_dimension # dimension of current face, dual dimension if ``dual`` + int dimension # dimension of the polyhedron + int output_dimension # only faces of this (dual?) dimension are considered + int lowest_dimension # don't consider faces below this (dual?) dimension + size_t _index # this counts the number of seen faces, useful for hasing the faces + size_t face_length # stores length of the faces in terms of uint64_t # ``visited_all`` points to faces, of which we have visited all faces already. # The number of faces in ``visited_all` might depend on the current dimension: @@ -37,18 +27,18 @@ cdef class FaceIterator(SageObject): # In this way, we will append ``visited_all`` in lower dimension, but # will ignore those changes when going up in dimension again. # This is why the number of faces in ``visited_all``depends on dimension. - cdef uint64_t **visited_all - cdef size_t *n_visited_all + uint64_t **visited_all + size_t *n_visited_all # ``maybe_newfaces`` is where all possible facets of a face are stored. # In dimension ``dim`` when visiting all faces of some face, # the intersections with other faces are stored in ``newfaces2[dim]``. - cdef uint64_t ***maybe_newfaces + uint64_t ***maybe_newfaces # ``newfaces`` will point to those faces in ``maybe_newfaces`` # that are of codimension 1 and not already visited. - cdef uint64_t ***newfaces - cdef size_t *n_newfaces # number of newfaces for each dimension + uint64_t ***newfaces + size_t *n_newfaces # number of newfaces for each dimension # After having visited a face completely, we want to add it to ``visited_all``. # ``first_dim[i]`` will indicate, wether there is one more face in @@ -56,15 +46,30 @@ cdef class FaceIterator(SageObject): # that has to be added to ``visited_all``. # If ``first_time[i] == False``, we still need to # add ``newfaces[i][n_newfaces[i]]`` to ``visited_all``. - cdef bint *first_time + bint *first_time # The number of elements in newfaces[current_dimension], # that have not been visited yet. - cdef size_t yet_to_visit + size_t yet_to_visit + + +@cython.final +cdef class FaceIterator(SageObject): + cdef iter_struct structure + cdef readonly bint dual # if 1, then iterate over dual Polyhedron + cdef MemoryAllocator _mem + cdef tuple newfaces_lists # tuple to hold the ListOfFaces corresponding to maybe_newfaces + + # some copies from ``CombinatorialPolyhedron`` + cdef tuple _Vrep, _facet_names, _equalities + + # Atoms and coatoms are the vertices/facets of the Polyedron. + # If ``dual == 0``, then coatoms are facets, atoms vertices and vice versa. + cdef ListOfFaces atoms, coatoms cdef inline CombinatorialFace next_face(self) cdef inline int next_dimension(self) except -1 cdef inline int next_face_loop(self) except -1 cdef size_t n_atom_rep(self) except -1 - cdef size_t set_coatom_repr(self) except -1 - cdef size_t set_atom_repr(self) except -1 + cdef size_t set_coatom_rep(self) except -1 + cdef size_t set_atom_rep(self) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index e2978baf132..fbe253de5fc 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1,3 +1,7 @@ +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc +# distutils: language = c++ +# distutils: extra_compile_args = -std=c++11 + r""" Face iterator for polyhedra @@ -11,14 +15,14 @@ A (slightly generalized) description of the algorithm can be found in [KS2019]_. Terminology in this module: - Coatoms -- the faces from which all others are constructed in - the face iterator. This will be facets or Vrepr. + the face iterator. This will be facets or Vrep. In non-dual mode, faces are constructed as intersections of the facets. In dual mode, the are constructed theoretically as joins of vertices. - The coatoms are reprsented as incidences with the + The coatoms are repsented as incidences with the atoms they contain. -- Atoms -- facets or Vrepr depending on application of algorithm. - Atoms are reprsented as incidences of coatoms they +- Atoms -- facets or Vrep depending on application of algorithm. + Atoms are repsented as incidences of coatoms they are contained in. .. SEEALSO:: @@ -159,9 +163,59 @@ AUTHOR: from sage.rings.integer cimport smallInteger from cysignals.signals cimport sig_check, sig_on, sig_off -from .conversions cimport bit_repr_to_Vrepr_list +from .conversions cimport bit_rep_to_Vrep_list from .base cimport CombinatorialPolyhedron -from .bit_vector_operations cimport get_next_level, count_atoms, bit_repr_to_coatom_repr + +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 @@ -258,13 +312,13 @@ cdef class FaceIterator(SageObject): (1,), (0,), (3, 4), - (2, 4), (1, 4), + (0, 4), (1, 3, 4), - (1, 2, 4), + (0, 1, 4), + (2, 3), (1, 3), - (0, 3), - (0, 1, 3), + (1, 2, 3), (1, 2), (0, 2), (0, 1, 2), @@ -294,22 +348,22 @@ cdef class FaceIterator(SageObject): (1,), (0,), (6, 7), - (5, 7), - (3, 7), + (4, 7), + (2, 7), (4, 5, 6, 7), - (2, 3, 6, 7), - (1, 3, 5, 7), - (4, 6), - (2, 6), - (0, 2, 4, 6), + (1, 2, 6, 7), + (2, 3, 4, 7), + (5, 6), + (1, 6), + (0, 1, 5, 6), (4, 5), - (1, 5), - (0, 1, 4, 5), - (0, 4), + (0, 5), + (0, 3, 4, 5), + (3, 4), (2, 3), - (1, 3), + (0, 3), (0, 1, 2, 3), - (0, 2), + (1, 2), (0, 1)] sage: it = C.face_iter(dual=False) @@ -416,65 +470,66 @@ cdef class FaceIterator(SageObject): cdef ListOfFaces some_list # make Cython aware of type self.dual = dual - self.face = NULL - self.dimension = C.dimension() - self.current_dimension = self.dimension -1 + self.structure.dual = dual + self.structure.face = NULL + self.structure.dimension = C.dimension() + self.structure.current_dimension = self.structure.dimension -1 self._mem = MemoryAllocator() # We will not yield the empty face. # If there are `n` lines, than there # are no faces below dimension `n`. # The dimension of the level-sets in the face lattice jumps from `n` to `-1`. - self.lowest_dimension = 0 + self.structure.lowest_dimension = 0 if output_dimension is not None: - if not output_dimension in range(0,self.dimension): + if not output_dimension in range(0,self.structure.dimension): raise ValueError("``output_dimension`` must be the dimension of proper faces") if self.dual: # In dual mode, the dimensions are reversed. - self.output_dimension = self.dimension - 1 - output_dimension + self.structure.output_dimension = self.structure.dimension - 1 - output_dimension else: - self.output_dimension = output_dimension - self.lowest_dimension = max(0, self.output_dimension) + self.structure.output_dimension = output_dimension + self.structure.lowest_dimension = max(0, self.structure.output_dimension) else: - self.output_dimension = -2 + self.structure.output_dimension = -2 if dual: self.atoms = C.bitrep_facets() - self.coatoms = C.bitrep_Vrepr() + self.coatoms = C.bitrep_Vrep() else: self.coatoms = C.bitrep_facets() - self.atoms = C.bitrep_Vrepr() - self.face_length = self.coatoms.face_length + self.atoms = C.bitrep_Vrep() + self.structure.face_length = self.coatoms.face_length self._Vrep = C.Vrep() self._facet_names = C.facet_names() self._equalities = C.equalities() - self.atom_repr = self._mem.allocarray(self.coatoms.n_atoms, sizeof(size_t)) - self.coatom_repr = self._mem.allocarray(self.coatoms.n_faces, sizeof(size_t)) + self.structure.atom_rep = self._mem.allocarray(self.coatoms.n_atoms, sizeof(size_t)) + self.structure.coatom_rep = self._mem.allocarray(self.coatoms.n_faces, sizeof(size_t)) - if self.dimension == 0 or self.coatoms.n_faces == 0: + if self.structure.dimension == 0 or self.coatoms.n_faces == 0: # As we will only yield proper faces, # there is nothing to yield in those cases. # We have to discontinue initialization, # as it assumes ``self.dimension > 0`` and ``self.n_faces > 0``. - self.current_dimension = self.dimension + self.structure.current_dimension = self.structure.dimension return # We may assume ``dimension > 0`` and ``n_faces > 0``. # Initialize ``maybe_newfaces``, # the place where the new faces are being stored. self.newfaces_lists = tuple(ListOfFaces(self.coatoms.n_faces, self.coatoms.n_atoms) - for i in range(self.dimension -1)) - self.maybe_newfaces = self._mem.allocarray((self.dimension -1), sizeof(uint64_t **)) - for i in range(self.dimension -1): + for i in range(self.structure.dimension -1)) + self.structure.maybe_newfaces = self._mem.allocarray((self.structure.dimension -1), sizeof(uint64_t **)) + for i in range(self.structure.dimension -1): some_list = self.newfaces_lists[i] - self.maybe_newfaces[i] = some_list.data + self.structure.maybe_newfaces[i] = some_list.data # Initialize ``visited_all``. - self.visited_all = self._mem.allocarray(self.coatoms.n_faces, sizeof(uint64_t *)) - self.n_visited_all = self._mem.allocarray(self.dimension, sizeof(size_t)) - self.n_visited_all[self.dimension -1] = 0 + self.structure.visited_all = self._mem.allocarray(self.coatoms.n_faces, sizeof(uint64_t *)) + self.structure.n_visited_all = self._mem.allocarray(self.structure.dimension, sizeof(size_t)) + self.structure.n_visited_all[self.structure.dimension -1] = 0 if not C.is_bounded(): # Treating the far face as if we had visited all its elements. # Hence we will visit all intersections of facets unless contained in the far face. @@ -485,26 +540,26 @@ cdef class FaceIterator(SageObject): # Hence it is fine to use the first entry already for the far face, # as ``self.visited_all`` holds ``n_facets`` pointers. some_list = C.far_face() - self.visited_all[0] = some_list.data[0] - self.n_visited_all[self.dimension -1] = 1 + self.structure.visited_all[0] = some_list.data[0] + self.structure.n_visited_all[self.structure.dimension -1] = 1 # Initialize ``newfaces``, which will point to the new faces of codimension 1, # which have not been visited yet. - self.newfaces = self._mem.allocarray(self.dimension, sizeof(uint64_t **)) - for i in range(self.dimension - 1): - self.newfaces[i] = self._mem.allocarray(self.coatoms.n_faces, sizeof(uint64_t *)) - self.newfaces[self.dimension - 1] = self.coatoms.data # we start with coatoms + self.structure.newfaces = self._mem.allocarray(self.structure.dimension, sizeof(uint64_t **)) + for i in range(self.structure.dimension - 1): + self.structure.newfaces[i] = self._mem.allocarray(self.coatoms.n_faces, sizeof(uint64_t *)) + self.structure.newfaces[self.structure.dimension - 1] = self.coatoms.data # we start with coatoms # Initialize ``n_newfaces``. - self.n_newfaces = self._mem.allocarray(self.dimension, sizeof(size_t)) - self.n_newfaces[self.dimension - 1] = self.coatoms.n_faces + self.structure.n_newfaces = self._mem.allocarray(self.structure.dimension, sizeof(size_t)) + self.structure.n_newfaces[self.structure.dimension - 1] = self.coatoms.n_faces # Initialize ``first_time``. - self.first_time = self._mem.allocarray(self.dimension, sizeof(bint)) - self.first_time[self.dimension - 1] = True + self.structure.first_time = self._mem.allocarray(self.structure.dimension, sizeof(bint)) + self.structure.first_time[self.structure.dimension - 1] = True - self.yet_to_visit = self.coatoms.n_faces - self._index = 0 + self.structure.yet_to_visit = self.coatoms.n_faces + self.structure._index = 0 def _repr_(self): r""" @@ -518,22 +573,23 @@ cdef class FaceIterator(SageObject): sage: C.face_iter(1) Iterator over the 1-faces of a 3-dimensional combinatorial polyhedron """ - if self.output_dimension != -2: + if self.structure.output_dimension != -2: if self.dual: # ouput_dimension is stored with respect to the dual - intended_dimension = self.dimension - 1 - self.output_dimension + intended_dimension = self.structure.dimension - 1 - self.structure.output_dimension else: - intended_dimension = self.output_dimension + intended_dimension = self.structure.output_dimension output = "Iterator over the {}-faces".format(intended_dimension) else: output = "Iterator over the proper faces" - return output + " of a {}-dimensional combinatorial polyhedron".format(self.dimension) + return output + " of a {}-dimensional combinatorial polyhedron".format(self.structure.dimension) def __next__(self): r""" Return the next face. EXAMPLES:: + sage: P = polytopes.cube() sage: C = CombinatorialPolyhedron(P) sage: it = C.face_iter() @@ -547,7 +603,7 @@ cdef class FaceIterator(SageObject): A 1-dimensional face of a 3-dimensional combinatorial polyhedron] """ cdef CombinatorialFace face = self.next_face() - if unlikely(self.current_dimension == self.dimension): + if unlikely(self.structure.current_dimension == self.structure.dimension): raise StopIteration return face @@ -618,15 +674,15 @@ cdef class FaceIterator(SageObject): """ if unlikely(self.dual): raise ValueError("only possible when not in dual mode") - if unlikely(self.face is NULL): + if unlikely(self.structure.face is NULL): raise ValueError("iterator not set to a face yet") # The current face is added to ``visited_all``. # This will make the iterator skip those faces. # Also, this face will not be added a second time to ``visited_all``, # as there are no new faces. - self.visited_all[self.n_visited_all[self.current_dimension]] = self.face - self.n_visited_all[self.current_dimension] += 1 + self.structure.visited_all[self.structure.n_visited_all[self.structure.current_dimension]] = self.structure.face + self.structure.n_visited_all[self.structure.current_dimension] += 1 def ignore_supfaces(self): r""" @@ -651,15 +707,15 @@ cdef class FaceIterator(SageObject): """ if unlikely(not self.dual): raise ValueError("only possible when in dual mode") - if unlikely(self.face is NULL): + if unlikely(self.structure.face is NULL): raise ValueError("iterator not set to a face yet") # The current face is added to ``visited_all``. # This will make the iterator skip those faces. # Also, this face will not be added a second time to ``visited_all``, # as there are no new faces. - self.visited_all[self.n_visited_all[self.current_dimension]] = self.face - self.n_visited_all[self.current_dimension] += 1 + self.structure.visited_all[self.structure.n_visited_all[self.structure.current_dimension]] = self.structure.face + self.structure.n_visited_all[self.structure.current_dimension] += 1 cdef inline CombinatorialFace next_face(self): r""" @@ -667,7 +723,7 @@ cdef class FaceIterator(SageObject): :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`. """ self.next_dimension() - if unlikely(self.current_dimension == self.dimension): + if unlikely(self.structure.current_dimension == self.structure.dimension): return None return CombinatorialFace(self) @@ -690,11 +746,11 @@ cdef class FaceIterator(SageObject): visiting sub-/supfaces instead of after. One cannot arbitrarily add faces to ``visited_all``, as visited_all has a maximal length. """ - cdef int dim = self.dimension - while (not self.next_face_loop()) and (self.current_dimension < dim): + cdef int dim = self.structure.dimension + while (not self.next_face_loop()) and (self.structure.current_dimension < dim): sig_check() - self._index += 1 - return self.current_dimension + self.structure._index += 1 + return self.structure.current_dimension cdef inline int next_face_loop(self) except -1: r""" @@ -704,53 +760,53 @@ cdef class FaceIterator(SageObject): If ``self.current_dimension == self.dimension``, then the iterator is consumed. """ - if unlikely(self.current_dimension == self.dimension): + if unlikely(self.structure.current_dimension == self.structure.dimension): # The function is not supposed to be called, # just prevent it from crashing. raise StopIteration # Getting ``[faces, n_faces, n_visited_all]`` according to dimension. - cdef uint64_t **faces = self.newfaces[self.current_dimension] - cdef size_t n_faces = self.n_newfaces[self.current_dimension] - cdef size_t n_visited_all = self.n_visited_all[self.current_dimension] + cdef uint64_t **faces = self.structure.newfaces[self.structure.current_dimension] + cdef size_t n_faces = self.structure.n_newfaces[self.structure.current_dimension] + cdef size_t n_visited_all = self.structure.n_visited_all[self.structure.current_dimension] - if (self.output_dimension > -2) and (self.output_dimension != self.current_dimension): + if (self.structure.output_dimension > -2) and (self.structure.output_dimension != self.structure.current_dimension): # If only a specific dimension was requested (i.e. ``self.output_dimension > -2``), # then we will not yield faces in other dimension. - self.yet_to_visit = 0 + self.structure.yet_to_visit = 0 - if self.yet_to_visit: + if self.structure.yet_to_visit: # Set ``face`` to the next face. - self.yet_to_visit -= 1 - self.face = faces[self.yet_to_visit] + self.structure.yet_to_visit -= 1 + self.structure.face = faces[self.structure.yet_to_visit] return 1 - if self.current_dimension <= self.lowest_dimension: + if self.structure.current_dimension <= self.structure.lowest_dimension: # We will not yield the empty face. # We will not yield below requested dimension. - self.current_dimension += 1 + self.structure.current_dimension += 1 return 0 if n_faces <= 1: # There will be no more faces from intersections. - self.current_dimension += 1 + self.structure.current_dimension += 1 return 0 # We will visit the last face now. - self.n_newfaces[self.current_dimension] -= 1 + self.structure.n_newfaces[self.structure.current_dimension] -= 1 n_faces -= 1 - if not self.first_time[self.current_dimension]: + if not self.structure.first_time[self.structure.current_dimension]: # In this case there exists ``faces[n_faces + 1]``, of which we # have visited all faces, but which was not added to # ``visited_all`` yet. - self.visited_all[n_visited_all] = faces[n_faces + 1] - self.n_visited_all[self.current_dimension] += 1 - n_visited_all = self.n_visited_all[self.current_dimension] + self.structure.visited_all[n_visited_all] = faces[n_faces + 1] + self.structure.n_visited_all[self.structure.current_dimension] += 1 + n_visited_all = self.structure.n_visited_all[self.structure.current_dimension] else: # Once we have visited all faces of ``faces[n_faces]``, we want # to add it to ``visited_all``. - self.first_time[self.current_dimension] = False + self.structure.first_time[self.structure.current_dimension] = False # Get the faces of codimension 1 contained in ``faces[n_faces]``, # which we have not yet visited. @@ -758,9 +814,9 @@ cdef class FaceIterator(SageObject): sig_on() newfacescounter = get_next_level( - faces, n_faces + 1, self.maybe_newfaces[self.current_dimension-1], - self.newfaces[self.current_dimension-1], - self.visited_all, n_visited_all, self.face_length) + faces, n_faces + 1, self.structure.maybe_newfaces[self.structure.current_dimension-1], + self.structure.newfaces[self.structure.current_dimension-1], + self.structure.visited_all, n_visited_all, self.structure.face_length) sig_off() if newfacescounter: @@ -768,11 +824,11 @@ cdef class FaceIterator(SageObject): # We will visted them on next call, starting with codimension 1. # Setting the variables correclty for next call of ``next_face_loop``. - self.current_dimension -= 1 - self.first_time[self.current_dimension] = True - self.n_newfaces[self.current_dimension] = newfacescounter - self.n_visited_all[self.current_dimension] = n_visited_all - self.yet_to_visit = newfacescounter + self.structure.current_dimension -= 1 + self.structure.first_time[self.structure.current_dimension] = True + self.structure.n_newfaces[self.structure.current_dimension] = newfacescounter + self.structure.n_visited_all[self.structure.current_dimension] = n_visited_all + self.structure.yet_to_visit = newfacescounter return 0 else: # ``faces[n_faces]`` contains no new faces. @@ -782,7 +838,7 @@ cdef class FaceIterator(SageObject): # this step needs to be done, as ``faces[n_faces]`` might # have been added manually to ``visited_all``. # So this step is required to respect boundaries of ``visited_all``. - self.first_time[self.current_dimension] = True + self.structure.first_time[self.structure.current_dimension] = True return 0 cdef size_t n_atom_rep(self) except -1: @@ -792,31 +848,31 @@ cdef class FaceIterator(SageObject): This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.n_atom_rep` """ - if self.face: - return count_atoms(self.face, self.face_length) + if self.structure.face: + return count_atoms(self.structure.face, self.structure.face_length) # The face was not initialized properly. raise LookupError("``FaceIterator`` does not point to a face") - cdef size_t set_coatom_repr(self) except -1: + cdef size_t set_coatom_rep(self) except -1: r""" - Set ``coatom_repr`` to be the coatom-representation of the current face. + Set ``coatom_rep`` to be the coatom-representation of the current face. Return its length. - This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.set_coatom_repr` + This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.set_coatom_rep` """ cdef size_t n_coatoms = self.coatoms.n_faces cdef uint64_t **coatoms = self.coatoms.data - cdef size_t face_length = self.face_length - return bit_repr_to_coatom_repr(self.face, coatoms, n_coatoms, - face_length, self.coatom_repr) + cdef size_t face_length = self.structure.face_length + return bit_rep_to_coatom_rep(self.structure.face, coatoms, n_coatoms, + face_length, self.structure.coatom_rep) - cdef size_t set_atom_repr(self) except -1: + cdef size_t set_atom_rep(self) except -1: r""" - Set ``atom_repr`` to be the atom-representation of the current face. + Set ``atom_rep`` to be the atom-representation of the current face. Return its length. - This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.set_atom_repr` + This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.set_atom_rep` """ - cdef size_t face_length = self.face_length - return bit_repr_to_Vrepr_list(self.face, self.atom_repr, face_length) + cdef size_t face_length = self.structure.face_length + return bit_rep_to_Vrep_list(self.structure.face, self.structure.atom_rep, face_length) 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 f20416a74f5..f56bfe346ca 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx @@ -1,3 +1,7 @@ +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc +# distutils: language = c++ +# distutils: extra_compile_args = -std=c++11 + r""" List of faces @@ -11,7 +15,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:: @@ -28,35 +32,35 @@ Provide enough space to store `20` faces as incidences to `60` vertices:: Obtain the facets of a polyhedron:: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import incidence_matrix_to_bit_repr_of_facets + ....: import incidence_matrix_to_bit_rep_of_facets sage: P = polytopes.cube() - sage: face_list = incidence_matrix_to_bit_repr_of_facets(P.incidence_matrix()) - sage: face_list = incidence_matrix_to_bit_repr_of_facets(P.incidence_matrix()) + sage: face_list = incidence_matrix_to_bit_rep_of_facets(P.incidence_matrix()) + sage: face_list = incidence_matrix_to_bit_rep_of_facets(P.incidence_matrix()) sage: face_list.compute_dimension() 3 Obtain the Vrepresentation of a polyhedron as facet-incidences:: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import incidence_matrix_to_bit_repr_of_Vrepr + ....: import incidence_matrix_to_bit_rep_of_Vrep sage: P = polytopes.associahedron(['A',3]) - sage: face_list = incidence_matrix_to_bit_repr_of_Vrepr(P.incidence_matrix()) + sage: face_list = incidence_matrix_to_bit_rep_of_Vrep(P.incidence_matrix()) sage: face_list.compute_dimension() 3 Obtain the facets of a polyhedron as :class:`ListOfFaces` from a facet list:: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import facets_tuple_to_bit_repr_of_facets + ....: import facets_tuple_to_bit_rep_of_facets sage: facets = ((0,1,2), (0,1,3), (0,2,3), (1,2,3)) - sage: face_list = facets_tuple_to_bit_repr_of_facets(facets, 4) + sage: face_list = facets_tuple_to_bit_rep_of_facets(facets, 4) Likewise for the Vrepresenatives as facet-incidences:: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import facets_tuple_to_bit_repr_of_Vrepr + ....: import facets_tuple_to_bit_rep_of_Vrep sage: facets = ((0,1,2), (0,1,3), (0,2,3), (1,2,3)) - sage: face_list = facets_tuple_to_bit_repr_of_Vrepr(facets, 4) + sage: face_list = facets_tuple_to_bit_rep_of_Vrep(facets, 4) .. SEEALSO:: @@ -83,7 +87,53 @@ 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 @@ -101,10 +151,10 @@ cdef class ListOfFaces: .. SEEALSO:: - :meth:`incidence_matrix_to_bit_repr_of_facets`, - :meth:`incidence_matrix_to_bit_repr_of_Vrepr`, - :meth:`facets_tuple_to_bit_repr_of_facets`, - :meth:`facets_tuple_to_bit_repr_of_Vrepr`, + :meth:`incidence_matrix_to_bit_rep_of_facets`, + :meth:`incidence_matrix_to_bit_rep_of_Vrep`, + :meth:`facets_tuple_to_bit_rep_of_facets`, + :meth:`facets_tuple_to_bit_rep_of_Vrep`, :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator`, :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.base.CombinatorialPolyhedron`. @@ -171,6 +221,40 @@ cdef class ListOfFaces: self.data[i] = \ self._mem.aligned_malloc(chunksize//8, self.face_length*8) + def __copy__(self): + r""" + Return a copy of self. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ + ....: import ListOfFaces + sage: facets = ListOfFaces(5, 13) + sage: copy(facets).n_atoms + 13 + sage: copy(facets).n_faces + 5 + + TESTS:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import facets_tuple_to_bit_rep_of_facets + sage: bi_pyr = ((0,1,4), (1,2,4), (2,3,4), (3,0,4), + ....: (0,1,5), (1,2,5), (2,3,5), (3,0,5)) + sage: facets = facets_tuple_to_bit_rep_of_facets(bi_pyr, 6) + sage: facets.compute_dimension() + 3 + sage: copy(facets).compute_dimension() + 3 + sage: copy(facets) is facets + False + """ + cdef ListOfFaces copy = ListOfFaces(self.n_faces, self.n_atoms) + cdef size_t i + for i in range(self.n_faces): + memcpy(copy.data[i], self.data[i], self.face_length*8) + return copy + cpdef int compute_dimension(self) except -2: r""" Compute the dimension of a polyhedron by its facets. @@ -179,18 +263,16 @@ cdef class ListOfFaces: EXAMPLES:: - sage: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ - ....: import ListOfFaces sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import facets_tuple_to_bit_repr_of_facets, \ - ....: facets_tuple_to_bit_repr_of_Vrepr + ....: import facets_tuple_to_bit_rep_of_facets, \ + ....: facets_tuple_to_bit_rep_of_Vrep sage: bi_pyr = ((0,1,4), (1,2,4), (2,3,4), (3,0,4), ....: (0,1,5), (1,2,5), (2,3,5), (3,0,5)) - sage: facets = facets_tuple_to_bit_repr_of_facets(bi_pyr, 6) - sage: Vrepr = facets_tuple_to_bit_repr_of_Vrepr(bi_pyr, 6) + sage: facets = facets_tuple_to_bit_rep_of_facets(bi_pyr, 6) + sage: Vrep = facets_tuple_to_bit_rep_of_Vrep(bi_pyr, 6) sage: facets.compute_dimension() 3 - sage: Vrepr.compute_dimension() + sage: Vrep.compute_dimension() 3 ALGORITHM: @@ -216,16 +298,18 @@ cdef class ListOfFaces: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ ....: import ListOfFaces sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import incidence_matrix_to_bit_repr_of_facets, \ - ....: incidence_matrix_to_bit_repr_of_Vrepr + ....: import incidence_matrix_to_bit_rep_of_facets, \ + ....: incidence_matrix_to_bit_rep_of_Vrep sage: bi_pyr = ((0,1,4), (1,2,4), (2,3,4), (3,0,4), ....: (0,1,5), (1,2,5), (2,3,5), (3,0,5)) sage: for _ in range(10): ....: points = tuple(tuple(randint(-1000,1000) for _ in range(10)) ....: for _ in range(randint(3,15))) ....: P = Polyhedron(vertices=points) - ....: facets = incidence_matrix_to_bit_repr_of_facets(P.incidence_matrix()) - ....: vertices = incidence_matrix_to_bit_repr_of_Vrepr(P.incidence_matrix()) + ....: inc = P.incidence_matrix() + ....: mod_inc = inc.delete_columns([i for i,V in enumerate(P.Hrepresentation()) if V.is_equation()]) + ....: facets = incidence_matrix_to_bit_rep_of_facets(mod_inc) + ....: vertices = incidence_matrix_to_bit_rep_of_Vrep(mod_inc) ....: d1 = P.dimension() ....: if d1 == 0: ....: continue @@ -269,7 +353,7 @@ cdef class ListOfFaces: return count_atoms(faces[0], face_length) # ``maybe_newfaces`` are all intersection of ``faces[n_faces -1]`` with previous faces. - # It needs to be allcoated to store those faces. + # It needs to be allocated to store those faces. cdef ListOfFaces maybe_newfaces_mem = ListOfFaces(n_faces, face_length*64) cdef uint64_t **maybe_newfaces = maybe_newfaces_mem.data diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd index 17c84df68e7..9f90d0cac33 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd @@ -12,13 +12,13 @@ cdef class PolyhedronFaceLattice: cdef size_t face_length # stores length of the faces in terms of uint64_t cdef size_t *f_vector # a copy of the f-vector, is reversed if dual cdef size_t *face_counter # how many faces of each dimension have been initialized - cdef size_t *atom_repr # a place where atom-representaion of face will be stored - cdef size_t *coatom_repr # a place where coatom-representaion of face will be stored + cdef size_t *atom_rep # a place where atom-representation of face will be stored + cdef size_t *coatom_rep # a place where coatom-representation of face will be stored # some copies from CombinatorialPolyhedron cdef tuple _Vrep, _facet_names, _equalities - # Atoms and coatoms are the Vrepr/facets of the Polyedron. + # Atoms and coatoms are the Vrep/facets of the Polyedron. # If ``dual == 0``, then coatoms are facets, atoms Vrepresentatives and vice versa. cdef ListOfFaces atoms, coatoms @@ -52,8 +52,8 @@ cdef class PolyhedronFaceLattice: cdef inline int is_equal(self, int dimension, size_t index, uint64_t *face) except -1 cdef CombinatorialFace get_face(self, int dimension, size_t index) - cdef size_t set_coatom_repr(self, int dimension, size_t index) except -1 - cdef size_t set_atom_repr(self, int dimension, size_t index) except -1 + cdef size_t set_coatom_rep(self, int dimension, size_t index) except -1 + cdef size_t set_atom_rep(self, int dimension, size_t index) except -1 cdef void incidence_init(self, int dimension_one, int dimension_two) cdef inline bint next_incidence(self, size_t *one, size_t *two) cdef inline bint next_incidence_loop(self, size_t *one, size_t *two) 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 397ad5f2003..c374962d1e9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx @@ -1,3 +1,7 @@ +# distutils: depends = sage/geometry/polyhedron/combinatorial_polyhedron/bit_vector_operations.cc +# distutils: language = c++ +# distutils: extra_compile_args = -std=c++11 + r""" PolyhedronFaceLattice @@ -8,22 +12,22 @@ the face lattice of a polyhedron. Terminology in this module: -- Vrepr -- ``[vertices, rays, lines]`` of the polyhedron. -- Hrepr -- inequalities and equalities of the polyhedron. +- Vrep -- ``[vertices, rays, lines]`` of the polyhedron. +- Hrep -- inequalities and equalities of the polyhedron. - Facets -- facets of the polyhedron. - Coatoms -- the faces from which all others are constructed in - the face iterator. This will be facets or Vrepr. + the face iterator. This will be facets or Vrep. In non-dual mode, faces are constructed as intersections of the facets. In dual mode, the are constructed theoretically as joins of vertices. - The coatoms are reprsented as incidences with the + The coatoms are repsented as incidences with the atoms they contain. -- Atoms -- facets or Vrepr depending on application of algorithm. - Atoms are reprsented as incidences of coatoms they +- Atoms -- facets or Vrep depending on application of algorithm. + Atoms are repsented as incidences of coatoms they are contained in. -- Vrepresentation -- represents a face by a list of VRepr it contains. -- Hrepresentation -- represents a face by a list of Hrepr it is contained in. +- Vrepresentation -- represents a face by a list of Vrep it contains. +- Hrepresentation -- represents a face by a list of Hrep it is contained in. - bit representation -- represents incidences as ``uint64_t``-array, where each Bit represents one incidences. There might be trailing zeros, to fit alignment-requirements. @@ -60,15 +64,28 @@ AUTHOR: #***************************************************************************** from .conversions \ - import facets_tuple_to_bit_repr_of_facets, \ - facets_tuple_to_bit_repr_of_Vrepr + import facets_tuple_to_bit_rep_of_facets, \ + facets_tuple_to_bit_rep_of_Vrep from sage.rings.integer cimport smallInteger from libc.string cimport memcmp, memcpy, memset -from .conversions cimport Vrepr_list_to_bit_repr, bit_repr_to_Vrepr_list +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_repr_to_coatom_repr + +cdef extern from "bit_vector_operations.cc": + cdef void intersection(uint64_t *A, uint64_t *B, uint64_t *C, + size_t face_length) +# Set ``C = A & B``, i.e. C is the intersection of A and B. +# ``face_length`` is the length of A, B and C 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 @@ -128,12 +145,12 @@ cdef class PolyhedronFaceLattice: self._mem = MemoryAllocator() self.dimension = C.dimension() self.dual = False - if C.bitrep_facets().n_faces > C.bitrep_Vrepr().n_faces: + if C.bitrep_facets().n_faces > C.bitrep_Vrep().n_faces: self.dual = True if not C.is_bounded(): self.dual = False cdef FaceIterator face_iter = C._face_iter(self.dual, -2) - self.face_length = face_iter.face_length + self.face_length = face_iter.structure.face_length self._Vrep = C.Vrep() self._facet_names = C.facet_names() self._equalities = C.equalities() @@ -156,19 +173,19 @@ cdef class PolyhedronFaceLattice: # We will obtain the coatoms from ``CombinatorialPolyhedron``. self.face_counter[self.dimension] = self.f_vector[self.dimension] - # Initialize atoms, coatoms, ``atom_repr`` and ``coatom_repr``. + # Initialize atoms, coatoms, ``atom_rep`` and ``coatom_rep``. if self.dimension == 0: # In case of the 0-dimensional polyhedron, we have to fix atoms and coatoms. # So far this didn't matter, as we only iterated over proper faces. - self.atoms = facets_tuple_to_bit_repr_of_Vrepr(((),), 1) - self.coatoms = facets_tuple_to_bit_repr_of_facets(((),), 1) + self.atoms = facets_tuple_to_bit_rep_of_Vrep(((),), 1) + self.coatoms = facets_tuple_to_bit_rep_of_facets(((),), 1) self.face_length = self.coatoms.face_length else: self.atoms = face_iter.atoms self.coatoms = face_iter.coatoms cdef size_t n_atoms = self.atoms.n_faces - self.atom_repr = self._mem.allocarray(self.coatoms.n_atoms, sizeof(size_t)) - self.coatom_repr = self._mem.allocarray(self.coatoms.n_faces, sizeof(size_t)) + self.atom_rep = self._mem.allocarray(self.coatoms.n_atoms, sizeof(size_t)) + self.coatom_rep = self._mem.allocarray(self.coatoms.n_faces, sizeof(size_t)) # Initialize the data for ``faces``: cdef ListOfFaces coatoms_mem @@ -189,9 +206,9 @@ cdef class PolyhedronFaceLattice: if self.dimension != 0: # Initialize the empty face. # In case ``dimension == 0``, we would overwrite the coatoms. - Vrepr_list_to_bit_repr((), self.faces[0][0], self.face_length) + Vrep_list_to_bit_rep((), self.faces[0][0], self.face_length) # Intialize the full polyhedron - Vrepr_list_to_bit_repr(tuple(j for j in range(n_atoms)), + Vrep_list_to_bit_rep(tuple(j for j in range(n_atoms)), self.faces[self.dimension + 1][0], self.face_length) @@ -203,14 +220,14 @@ cdef class PolyhedronFaceLattice: # Adding all faces, using the iterator. cdef int d - if face_iter.current_dimension != self.dimension: + if face_iter.structure.current_dimension != self.dimension: # If there are proper faces. d = face_iter.next_dimension() while (d == self.dimension - 1): # We already have the coatoms. d = face_iter.next_dimension() while (d < self.dimension): - self._add_face(d, face_iter.face) + self._add_face(d, face_iter.structure.face) d = face_iter.next_dimension() # Sorting the faces, except for coatoms. @@ -336,14 +353,12 @@ cdef class PolyhedronFaceLattice: ....: from sage.geometry.polyhedron.combinatorial_polyhedron.base \ ....: cimport CombinatorialPolyhedron, FaceIterator, PolyhedronFaceLattice ....: - ....: def find_face_from_iterator(it, C1): - ....: cdef FaceIterator face_iter = it - ....: cdef CombinatorialPolyhedron C = C1 + ....: def find_face_from_iterator(FaceIterator it, CombinatorialPolyhedron C): ....: C._record_all_faces() ....: cdef PolyhedronFaceLattice all_faces = C._all_faces ....: if not (all_faces.dual == it.dual): ....: raise ValueError("iterator and allfaces not in same mode") - ....: return all_faces.find_face(face_iter.current_dimension, face_iter.face) + ....: return all_faces.find_face(it.structure.current_dimension, it.structure.face) ....: ''') sage: P = polytopes.permutahedron(4) sage: C = CombinatorialPolyhedron(P) @@ -422,15 +437,13 @@ cdef class PolyhedronFaceLattice: ....: from sage.geometry.polyhedron.combinatorial_polyhedron.base \ ....: cimport CombinatorialPolyhedron, FaceIterator, PolyhedronFaceLattice ....: - ....: def face_via_all_faces_from_iterator(it, C1): - ....: cdef FaceIterator face_iter = it - ....: cdef CombinatorialPolyhedron C = C1 - ....: cdef int dimension = face_iter.current_dimension + ....: def face_via_all_faces_from_iterator(FaceIterator it, CombinatorialPolyhedron C): + ....: cdef int dimension = it.structure.current_dimension ....: C._record_all_faces() ....: cdef PolyhedronFaceLattice all_faces = C._all_faces ....: if not (all_faces.dual == it.dual): ....: raise ValueError("iterator and allfaces not in same mode") - ....: index = all_faces.find_face(dimension, face_iter.face) + ....: index = all_faces.find_face(dimension, it.structure.face) ....: return all_faces.get_face(dimension, index) ....: ''') sage: P = polytopes.permutahedron(4) @@ -460,17 +473,17 @@ cdef class PolyhedronFaceLattice: """ cdef size_t length if self.dual: - # if dual, the Vrepresention corresponds to the coatom-representation + # if dual, the Vrepresentation corresponds to the coatom-representation dimension = self.dimension - 1 - dimension # if dual, the dimensions are reversed return CombinatorialFace(self, dimension=dimension, index=index) - cdef size_t set_coatom_repr(self, int dimension, size_t index) except -1: + cdef size_t set_coatom_rep(self, int dimension, size_t index) except -1: r""" - Set ``atom_repr`` to be the atom-representation of the face + Set ``atom_rep`` to be the atom-representation of the face of dimension ``dimension`` and index ``index``. Return its length. - This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.set_coatom_repr` + This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.set_coatom_rep` """ if unlikely(dimension < -1 or dimension > self.dimension): raise ValueError("no face of dimension %s"%dimension) @@ -483,16 +496,16 @@ cdef class PolyhedronFaceLattice: cdef uint64_t **coatoms = self.faces[self.dimension] cdef size_t face_length = self.face_length cdef uint64_t *face = self.faces[dimension+1][index] - return bit_repr_to_coatom_repr(face, coatoms, n_coatoms, - face_length, self.coatom_repr) + return bit_rep_to_coatom_rep(face, coatoms, n_coatoms, + face_length, self.coatom_rep) - cdef size_t set_atom_repr(self, int dimension, size_t index) except -1: + cdef size_t set_atom_rep(self, int dimension, size_t index) except -1: r""" - Set ``atom_repr`` to be the atom-representation of the face + Set ``atom_rep`` to be the atom-representation of the face of dimension ``dimension`` and index ``index``. Return its length. - This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.set_atom_repr` + This is a shortcut of :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace.set_atom_rep` """ if unlikely(dimension < -1 or dimension > self.dimension): raise ValueError("no face of dimension %s"%dimension) @@ -501,7 +514,7 @@ cdef class PolyhedronFaceLattice: cdef size_t face_length = self.face_length cdef uint64_t *face = self.faces[dimension+1][index] - return bit_repr_to_Vrepr_list(face, self.atom_repr, face_length) + return bit_rep_to_Vrep_list(face, self.atom_rep, face_length) cdef void incidence_init(self, int dimension_one, int dimension_two): r""" @@ -567,7 +580,7 @@ cdef class PolyhedronFaceLattice: ``two[0]`` will represent the index of a face in ``dimension_two`` according to their order in :class:`PolyhedronFaceLattice`. - Use :meth:`Vrepr` and :meth:`Hrepr` to interpret the output. + Use :meth:`Vrep` and :meth:`Hrep` to interpret the output. ALGORITHM: diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 1a84f4fb97d..821cd4cfc63 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -341,9 +341,9 @@ def __richcmp__(self, other, op): sage: square = polytopes.hypercube(2) sage: f = square.faces(1) sage: matrix(4,4, lambda i,j: ZZ(f[i] <= f[j])) - [1 1 0 0] - [0 1 0 0] [1 1 1 0] + [0 1 0 0] + [0 1 1 0] [1 1 1 1] sage: matrix(4,4, lambda i,j: ZZ(f[i] == f[j])) == 1 True @@ -379,16 +379,16 @@ def ambient_Hrepresentation(self, index=None): sage: square = polytopes.hypercube(2) sage: for face in square.face_lattice(): ....: print(face.ambient_Hrepresentation()) - (An inequality (1, 0) x + 1 >= 0, An inequality (0, 1) x + 1 >= 0, - An inequality (-1, 0) x + 1 >= 0, An inequality (0, -1) x + 1 >= 0) - (An inequality (1, 0) x + 1 >= 0, An inequality (0, 1) x + 1 >= 0) - (An inequality (1, 0) x + 1 >= 0, An inequality (0, -1) x + 1 >= 0) - (An inequality (0, 1) x + 1 >= 0, An inequality (-1, 0) x + 1 >= 0) + (An inequality (-1, 0) x + 1 >= 0, An inequality (0, -1) x + 1 >= 0, + An inequality (1, 0) x + 1 >= 0, An inequality (0, 1) x + 1 >= 0) + (An inequality (-1, 0) x + 1 >= 0, An inequality (0, 1) x + 1 >= 0) (An inequality (-1, 0) x + 1 >= 0, An inequality (0, -1) x + 1 >= 0) - (An inequality (1, 0) x + 1 >= 0,) - (An inequality (0, 1) x + 1 >= 0,) + (An inequality (0, -1) x + 1 >= 0, An inequality (1, 0) x + 1 >= 0) + (An inequality (1, 0) x + 1 >= 0, An inequality (0, 1) x + 1 >= 0) (An inequality (-1, 0) x + 1 >= 0,) (An inequality (0, -1) x + 1 >= 0,) + (An inequality (1, 0) x + 1 >= 0,) + (An inequality (0, 1) x + 1 >= 0,) () """ if index is None: @@ -421,16 +421,16 @@ def ambient_Vrepresentation(self, index=None): sage: for fl in square.face_lattice(): ....: print(fl.ambient_Vrepresentation()) () - (A vertex at (-1, -1),) - (A vertex at (-1, 1),) (A vertex at (1, -1),) (A vertex at (1, 1),) - (A vertex at (-1, -1), A vertex at (-1, 1)) - (A vertex at (-1, -1), A vertex at (1, -1)) + (A vertex at (-1, 1),) + (A vertex at (-1, -1),) (A vertex at (1, -1), A vertex at (1, 1)) - (A vertex at (-1, 1), A vertex at (1, 1)) - (A vertex at (-1, -1), A vertex at (-1, 1), - A vertex at (1, -1), A vertex at (1, 1)) + (A vertex at (1, 1), A vertex at (-1, 1)) + (A vertex at (-1, 1), A vertex at (-1, -1)) + (A vertex at (1, -1), A vertex at (-1, -1)) + (A vertex at (1, -1), A vertex at (1, 1), + A vertex at (-1, 1), A vertex at (-1, -1)) """ if index is None: return self._ambient_Vrepresentation @@ -535,12 +535,12 @@ def ambient_V_indices(self): sage: P = polytopes.cube() sage: F = P.faces(2) sage: [f.ambient_V_indices() for f in F] - [(0, 1, 4, 5), - (0, 2, 4, 6), - (0, 1, 2, 3), + [(0, 3, 4, 5), + (0, 1, 5, 6), (4, 5, 6, 7), - (2, 3, 6, 7), - (1, 3, 5, 7)] + (2, 3, 4, 7), + (1, 2, 6, 7), + (0, 1, 2, 3)] """ return self._ambient_Vrepresentation_indices @@ -677,6 +677,86 @@ def as_polyhedron(self): Vrep = (self.vertices(), self.rays(), self.lines()) return P.__class__(parent, Vrep, None) + @cached_method + def normal_cone(self, direction='outer'): + """ + Return the pointed polyhedral cone consisting of normal vectors to + hyperplanes supporting ``self``. + + INPUT: + + - ``direction`` -- string (default: ``'outer'``), the direction in + which to consider the normals. The other allowed option is + ``'inner'``. + + OUTPUT: + + A polyhedron. + + EXAMPLES:: + + sage: p = Polyhedron(vertices = [[1,2],[2,1],[-2,2],[-2,-2],[2,-2]]) + sage: for v in p.face_generator(0): + ....: vect = v.vertices()[0].vector() + ....: nc = v.normal_cone().rays_list() + ....: print("{} has outer normal cone spanned by {}".format(vect,nc)) + ....: + (2, 1) has outer normal cone spanned by [[1, 0], [1, 1]] + (1, 2) has outer normal cone spanned by [[0, 1], [1, 1]] + (2, -2) has outer normal cone spanned by [[0, -1], [1, 0]] + (-2, -2) has outer normal cone spanned by [[-1, 0], [0, -1]] + (-2, 2) has outer normal cone spanned by [[-1, 0], [0, 1]] + + sage: for v in p.face_generator(0): + ....: vect = v.vertices()[0].vector() + ....: nc = v.normal_cone(direction='inner').rays_list() + ....: print("{} has inner normal cone spanned by {}".format(vect,nc)) + ....: + (2, 1) has inner normal cone spanned by [[-1, -1], [-1, 0]] + (1, 2) has inner normal cone spanned by [[-1, -1], [0, -1]] + (2, -2) has inner normal cone spanned by [[-1, 0], [0, 1]] + (-2, -2) has inner normal cone spanned by [[0, 1], [1, 0]] + (-2, 2) has inner normal cone spanned by [[0, -1], [1, 0]] + + The function works for polytopes that are not full-dimensional:: + + sage: p = polytopes.permutahedron(3) + sage: f1 = p.faces(0)[0] + sage: f2 = p.faces(1)[0] + sage: f3 = p.faces(2)[0] + sage: f1.normal_cone() + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line + sage: f2.normal_cone() + A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line + sage: f3.normal_cone() + A 1-dimensional polyhedron in ZZ^3 defined as the convex hull of 1 vertex and 1 line + + Normal cones are only defined for non-empty faces:: + + sage: f0 = p.faces(-1)[0] + sage: f0.normal_cone() + Traceback (most recent call last): + ... + ValueError: the empty face does not have a normal cone + """ + if self.dim() == -1: + raise ValueError("the empty face does not have a normal cone") + elif direction not in ['outer','inner']: + raise ValueError("the direction should be either 'outer' or 'inner'") + rays = [] + lines = [] + for facet in self.ambient_Hrepresentation(): + if facet.is_equation(): + lines += [facet.A()] + elif direction == 'outer': + rays += [-facet.A()] + else: # 'inner' + rays += [facet.A()] + parent = self.polyhedron().parent() + origin = self.polyhedron().ambient_space().zero() + return parent.element_class(parent, [[origin], rays, lines], None) + + def combinatorial_face_to_polyhedral_face(polyhedron, combinatorial_face): r""" Convert a combinatorial face to a face of a polyhedron. diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index 839822c3f76..86578d36539 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -86,6 +86,7 @@ from sage.combinat.permutation import Permutations from sage.groups.perm_gps.permgroup_named import AlternatingGroup from .constructor import Polyhedron +from .parent import Polyhedra from sage.graphs.digraph import DiGraph from sage.combinat.root_system.associahedron import Associahedron @@ -192,6 +193,308 @@ def project_points(*points, **kwds): return [m * v for v in vecs] +def gale_transform_to_polytope(vectors, base_ring=None, backend=None): + r""" + Return the polytope associated to the list of vectors forming a Gale transform. + + This function is the inverse of + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.gale_transform` + up to projective transformation. + + INPUT: + + - ``vectors`` -- the vectors of the Gale transform + + - ``base_ring`` -- string (default: `None`); + the base ring to be used for the construction + + - ``backend`` -- string (default: `None`); + the backend to use to create the polytope + + .. NOTE:: + + The order of the input vectors will not be preserved. + + If the center of the (input) vectors is the origin, + the function is much faster and might give a nicer representation + of the polytope. + + If this is not the case, the vectors will be scaled + (each by a positive scalar) accordingly to obtain the polytope. + + .. SEEALSO:: + + :func`~sage.geometry.polyhedron.library.gale_transform_to_primal`. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.library import gale_transform_to_polytope + sage: points = polytopes.octahedron().gale_transform() + sage: points + ((0, -1), (-1, 0), (1, 1), (1, 1), (-1, 0), (0, -1)) + sage: P = gale_transform_to_polytope(points); P + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 6 vertices + sage: P.vertices() + (A vertex at (-1, 0, 0), + A vertex at (0, -1, 0), + A vertex at (0, 0, -1), + A vertex at (0, 0, 1), + A vertex at (0, 1, 0), + A vertex at (1, 0, 0)) + + One can specify the base ring:: + + sage: gale_transform_to_polytope( + ....: [(1,1), (-1,-1), (1,0), + ....: (-1,0), (1,-1), (-2,1)]).vertices() + (A vertex at (-25, 0, 0), + A vertex at (-15, 50, -60), + A vertex at (0, -25, 0), + A vertex at (0, 0, -25), + A vertex at (16, -35, 54), + A vertex at (24, 10, 31)) + sage: gale_transform_to_polytope( + ....: [(1,1), (-1,-1), (1,0), + ....: (-1,0), (1,-1), (-2,1)], + ....: base_ring=RDF).vertices() + (A vertex at (-0.64, 1.4, -2.16), + A vertex at (-0.96, -0.4, -1.24), + A vertex at (0.6, -2.0, 2.4), + A vertex at (1.0, 0.0, 0.0), + A vertex at (0.0, 1.0, 0.0), + A vertex at (0.0, 0.0, 1.0)) + + One can also specify the backend:: + + sage: gale_transform_to_polytope( + ....: [(1,1), (-1,-1), (1,0), + ....: (-1,0), (1,-1), (-2,1)], + ....: backend='field').backend() + 'field' + sage: gale_transform_to_polytope( + ....: [(1,1), (-1,-1), (1,0), + ....: (-1,0), (1,-1), (-2,1)], + ....: backend='cdd', base_ring=RDF).backend() + 'cdd' + + A gale transform corresponds to a polytope if and only if + every oriented (linear) hyperplane + has at least two vectors on each side. + See Theorem 6.19 of [Zie2007]_. + If this is not the case, one of two errors is raised. + + If there is such a hyperplane with no vector on one side, + the vectors are not totally cyclic:: + + sage: gale_transform_to_polytope([(0,1), (1,1), (1,0), (-1,0)]) + Traceback (most recent call last): + ... + ValueError: input vectors not totally cyclic + + If every hyperplane has at least one vector on each side, then the gale + transform corresponds to a point configuration. + It corresponds to a polytope if and only if this point configuration is + convex and if and only if every hyperplane contains at least two vectors of + the gale transform on each side. + + If this is not the case, an error is raised:: + + sage: gale_transform_to_polytope([(0,1), (1,1), (1,0), (-1,-1)]) + Traceback (most recent call last): + ... + ValueError: the gale transform does not correspond to a polytope + """ + vertices = gale_transform_to_primal(vectors, base_ring, backend) + P = Polyhedron(vertices=vertices, base_ring=base_ring, backend=backend) + + if not P.n_vertices() == len(vertices): + # If the input vectors are not totally cyclic, ``gale_transform_to_primal`` + # raises an error. + # As no error was raised so far, the gale transform corresponds to + # to a point configuration. + # It corresponds to a polytope if and only if + # ``vertices`` are in convex position. + raise ValueError("the gale transform does not correspond to a polytope") + + return P + +def gale_transform_to_primal(vectors, base_ring=None, backend=None): + r""" + Return a point configuration dual to a totally cyclic vector configuration. + + This is the dehomogenized vector configuration dual to the input. + The dual vector configuration is acyclic and can therefore + be dehomogenized as the input is totally cyclic. + + INPUT: + + - ``vectors`` -- the ordered vectors of the Gale transform + + - ``base_ring`` -- string (default: `None`); + the base ring to be used for the construction + + - ``backend`` -- string (default: `None`); + the backend to be use to construct a polyhedral, + used interally in case the center is not the origin, + see :func:`~sage.geometry.polyhedron.constructor.Polyhedron` + + OUTPUT: An ordered point configuration as list of vectors. + + .. NOTE:: + + If the center of the (input) vectors is the origin, + the function is much faster and might give a nicer representation + of the point configuration. + + If this is not the case, the vectors will be scaled + (each by a positive scalar) accordingly. + + ALGORITHM: + + Step 1: If the center of the (input) vectors is not the origin, + we do an appropriate transformation to make it so. + + Step 2: We add a row of ones on top of ``Matrix(vectors)``. + The right kernel of this larger matrix is the dual configuration space, + and a basis of this space provides the dual point configuration. + + More concretely, the dual vector configuration (inhomogeneous) + is obtained by taking a basis of the right kernel of ``Matrix(vectors)``. + If the center of the (input) vectors is the origin, + there exists a basis of the right kernel of the form + ``[[1], [V]]``, where ``[1]`` represents a row of ones. + Then, ``V`` is a dehomogenization and thus the dual point configuration. + + To extend ``[1]`` to a basis of ``Matrix(vectors)``, we add + a row of ones to ``Matrix(vectors)`` and calculate a basis of the + right kernel of the obtained matrix. + + REFERENCES: + + For more information, see Section 6.4 of [Zie2007]_ + or Definition 2.5.1 and Definition 4.1.35 of [DLRS2010]_. + + .. SEEALSO:: + + :func`~sage.geometry.polyhedron.library.gale_transform_to_polytope`. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.library import gale_transform_to_primal + sage: points = ((0, -1), (-1, 0), (1, 1), (1, 1), (-1, 0), (0, -1)) + sage: gale_transform_to_primal(points) + [(0, 0, 1), (0, 1, 0), (1, 0, 0), (-1, 0, 0), (0, -1, 0), (0, 0, -1)] + + One can specify the base ring:: + + sage: gale_transform_to_primal( + ....: [(1,1), (-1,-1), (1,0), + ....: (-1,0), (1,-1), (-2,1)]) + [(16, -35, 54), + (24, 10, 31), + (-15, 50, -60), + (-25, 0, 0), + (0, -25, 0), + (0, 0, -25)] + sage: gale_transform_to_primal( + ....: [(1,1),(-1,-1),(1,0),(-1,0),(1,-1),(-2,1)], base_ring=RDF) + [(-0.6400000000000001, 1.4, -2.1600000000000006), + (-0.9600000000000002, -0.39999999999999997, -1.2400000000000002), + (0.6000000000000001, -2.0, 2.4000000000000004), + (1.0, 0.0, 0.0), + (0.0, 1.0, 0.0), + (0.0, 0.0, 1.0)] + + One can also specify the backend to be used internally:: + + sage: gale_transform_to_primal( + ....: [(1,1), (-1,-1), (1,0), + ....: (-1,0), (1,-1), (-2,1)], backend='field') + [(48, -71, 88), + (84, -28, 99), + (-77, 154, -132), + (-55, 0, 0), + (0, -55, 0), + (0, 0, -55)] + sage: gale_transform_to_primal( # optional - pynormaliz + ....: [(1,1), (-1,-1), (1,0), + ....: (-1,0), (1,-1), (-2,1)], backend='normaliz') + [(16, -35, 54), + (24, 10, 31), + (-15, 50, -60), + (-25, 0, 0), + (0, -25, 0), + (0, 0, -25)] + + The input vectors should be totally cyclic:: + + sage: gale_transform_to_primal([(0,1), (1,0), (1,1), (-1,0)]) + Traceback (most recent call last): + ... + ValueError: input vectors not totally cyclic + + sage: gale_transform_to_primal( + ....: [(1,1,0), (-1,-1,0), (1,0,0), + ....: (-1,0,0), (1,-1,0), (-2,1,0)], backend='field') + Traceback (most recent call last): + ... + ValueError: input vectors not totally cyclic + """ + from sage.modules.free_module_element import vector + from sage.matrix.all import Matrix + if base_ring: + vectors = tuple(vector(base_ring, x) for x in vectors) + else: + vectors = tuple(vector(x) for x in vectors) + + if not sum(vectors).is_zero(): + # The center of the input vectors shall be the origin. + # If this is not the case, we scale them accordingly. + # This has the adventage that right kernel of ``vectors`` can be + # presented in the form ``[[1], [V]]``, where ``V`` are the points + # in the dual point configuration. + # (Dehomogenization is straightforward.) + + # Scaling of the vectors is equivalent to finding a hyperplane that intersects + # all vectors of the dual point configuration. But if the input is already provided + # such that the vectors add up to zero, the coordinates might be nicer. + # (And this is faster.) + + if base_ring: + ker = Matrix(base_ring, vectors).left_kernel() + else: + ker = Matrix(vectors).left_kernel() + solutions = Polyhedron(lines=tuple(y for y in ker.basis_matrix()), base_ring=base_ring, backend=backend) + + from sage.matrix.special import identity_matrix + pos_orthant = Polyhedron(rays=identity_matrix(len(vectors)), base_ring=base_ring, backend=backend) + pos_solutions = solutions.intersection(pos_orthant) + if base_ring is ZZ: + pos_solutions = pos_solutions.change_ring(ZZ) + + # Any integral point in ``pos_solutions`` will correspond to scaling-factors + # that make ``sum(vectors)`` zero. + x = pos_solutions.representative_point() + if not all(y > 0 for y in x): + raise ValueError("input vectors not totally cyclic") + vectors = tuple(vec*x[i] for i,vec in enumerate(vectors)) + + # The right kernel of ``vectors`` has a basis of the form ``[[1], [V]]``, + # where ``V`` is the dehomogenized dual point configuration. + # If we append a row of ones to ``vectors``, ``V`` is just the right kernel. + if base_ring: + m = Matrix(base_ring, vectors).transpose().stack(Matrix(base_ring, [[1]*len(vectors)])) + else: + m = Matrix(vectors).transpose().stack(Matrix([[1]*len(vectors)])) + + if m.rank() != len(vectors[0]) + 1: + # The given vectors do not span the ambient space, + # then there exists a nonnegative value vector. + raise ValueError("input vectors not totally cyclic") + + return m.right_kernel_matrix(basis='computed').columns() + + class Polytopes(): """ A class of constructors for commonly used, famous, or interesting @@ -246,7 +549,9 @@ 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 + sage: TestSuite(octagon).run() # long time + sage: TestSuite(polytopes.regular_polygon(5, exact=False)).run() """ n = ZZ(n) if n <= 2: @@ -311,6 +616,7 @@ def Birkhoff_polytope(self, n, backend=None): sage: b4norm = polytopes.Birkhoff_polytope(4,backend='normaliz') # optional - pynormaliz sage: TestSuite(b4norm).run() # optional - pynormaliz + sage: TestSuite(polytopes.Birkhoff_polytope(3)).run() """ from itertools import permutations verts = [] @@ -385,6 +691,7 @@ def simplex(self, dim=3, project=False, base_ring=None, backend=None): sage: s6norm = polytopes.simplex(6,backend='normaliz') # optional - pynormaliz sage: TestSuite(s6norm).run() # optional - pynormaliz + sage: TestSuite(polytopes.simplex(5)).run() """ verts = list((ZZ**(dim + 1)).basis()) if project: @@ -451,6 +758,9 @@ def icosahedron(self, exact=True, base_ring=None, backend=None): (1, 12, 30, 20, 1) sage: ico.volume() # optional - pynormaliz 5/12*sqrt5 + 5/4 + sage: TestSuite(ico).run() # optional - pynormaliz + sage: ico = polytopes.icosahedron(exact=False) + sage: TestSuite(ico).run() """ if base_ring is None and exact: @@ -516,6 +826,7 @@ def dodecahedron(self, exact=True, base_ring=None, backend=None): sage: d12 = polytopes.dodecahedron(backend='normaliz') # optional - pynormaliz sage: d12.f_vector() # optional - pynormaliz (1, 20, 30, 12, 1) + sage: TestSuite(d12).run() # optional - pynormaliz """ return self.icosahedron(exact=exact, base_ring=base_ring, backend=backend).polar() @@ -570,6 +881,7 @@ def small_rhombicuboctahedron(self, exact=True, base_ring=None, backend=None): (1, 24, 48, 26, 1) sage: sr.volume() # optional - pynormaliz 80/3*sqrt2 + 32 + sage: TestSuite(sr).run() # optional - pynormaliz """ if base_ring is None and exact: from sage.rings.number_field.number_field import QuadraticField @@ -786,6 +1098,7 @@ def truncated_cube(self, exact=True, base_ring=None, backend=None): sage: co = polytopes.truncated_cube(backend='normaliz') # optional - pynormaliz sage: co.f_vector() # optional - pynormaliz (1, 24, 36, 14, 1) + sage: TestSuite(co).run() # optional - pynormaliz """ if base_ring is None and exact: @@ -1010,7 +1323,7 @@ def snub_cube(self, exact=False, base_ring=None, backend=None, verbose=False): A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 24 vertices sage: sc_inexact.f_vector() (1, 24, 60, 38, 1) - sage: sc_exact = polytopes.snub_cube(exact=True) # long time - 30secs + sage: sc_exact = polytopes.snub_cube(exact=True) # long time sage: sc_exact.f_vector() # long time (1, 24, 60, 38, 1) sage: sorted(sc_exact.vertices()) # long time @@ -1038,7 +1351,7 @@ def snub_cube(self, exact=False, base_ring=None, backend=None, verbose=False): A vertex at (1, -z^2, -z), A vertex at (1, z^2, z), A vertex at (1, z, -z^2)] - sage: sc_exact.is_combinatorially_isomorphic(sc_inexact) #long time + sage: sc_exact.is_combinatorially_isomorphic(sc_inexact) # long time True TESTS:: @@ -1163,14 +1476,16 @@ def icosidodecahedron(self, exact=True, backend=None): TESTS:: - sage: polytopes.icosidodecahedron(exact=False) + sage: id = polytopes.icosidodecahedron(exact=False); id A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 30 vertices + sage: TestSuite(id).run(skip="_test_is_combinatorially_isomorphic") sage: id = polytopes.icosidodecahedron(backend='normaliz') # optional - pynormaliz sage: id.f_vector() # optional - pynormaliz (1, 30, 60, 32, 1) sage: id.base_ring() # optional - pynormaliz Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790? + sage: TestSuite(id).run() # optional - pynormaliz """ from sage.rings.number_field.number_field import QuadraticField from itertools import product @@ -1243,7 +1558,7 @@ def icosidodecahedron_V2(self, exact=True, base_ring=None, backend=None): (1, 30, 60, 32, 1) sage: id.base_ring() # optional - pynormaliz Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790? - + sage: TestSuite(id).run() # optional - pynormaliz """ if base_ring is None and exact: from sage.rings.number_field.number_field import QuadraticField @@ -1927,7 +2242,7 @@ def six_hundred_cell(self, exact=False, backend=None): TESTS:: sage: p600 = polytopes.six_hundred_cell(exact=True, backend='normaliz') # optional - pynormaliz - sage: len(list(p600.bounded_edges())) # optional - pynormaliz + sage: len(list(p600.bounded_edges())) # optional - pynormaliz, long time 720 """ if exact: @@ -2066,7 +2381,7 @@ def Gosset_3_21(self, backend=None): TESTS:: sage: G321 = polytopes.Gosset_3_21(backend='normaliz') # optional - pynormaliz - sage: TestSuite(G321).run() # optional - pynormaliz + sage: TestSuite(G321).run() # optional - pynormaliz, long time """ from itertools import combinations verts = [] @@ -2147,12 +2462,14 @@ def hypersimplex(self, dim, k, project=False, backend=None): (1, 6, 12, 8, 1) sage: h_4_2.ehrhart_polynomial() # optional - latte_int 2/3*t^3 + 2*t^2 + 7/3*t + 1 + sage: TestSuite(h_4_2).run() sage: h_7_3 = polytopes.hypersimplex(7, 3, project=True) sage: h_7_3 A 6-dimensional polyhedron in RDF^6 defined as the convex hull of 35 vertices sage: h_7_3.f_vector() (1, 35, 210, 350, 245, 84, 14, 1) + sage: TestSuite(h_7_3).run() """ verts = Permutations([0] * (dim - k) + [1] * k).list() if project: @@ -2294,8 +2611,8 @@ def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regula A vertex at (0.500000000000000?, 0.866025403784439?)) sage: perm_a2_reg.is_inscribed() True - sage: perm_a3_reg = polytopes.generalized_permutahedron(['A',3],regular=True) - sage: perm_a3_reg.is_inscribed() + sage: perm_a3_reg = polytopes.generalized_permutahedron(['A',3],regular=True) # long time + sage: perm_a3_reg.is_inscribed() # long time True The same is possible with vertices in ``RDF``:: @@ -2320,7 +2637,7 @@ def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regula It works also with types with non-rational coordinates:: - sage: perm_b3 = polytopes.generalized_permutahedron(['B',3]); perm_b3 + sage: perm_b3 = polytopes.generalized_permutahedron(['B',3]); perm_b3 # long time A 3-dimensional polyhedron in (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^3 defined as the convex hull of 48 vertices sage: perm_b3_reg = polytopes.generalized_permutahedron(['B',3],regular=True); perm_b3_reg # not tested - long time (12sec on 64 bits). @@ -2338,8 +2655,8 @@ def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regula sage: perm_h3 = polytopes.generalized_permutahedron(['H',3],backend='normaliz') # optional - pynormaliz sage: perm_h3 # optional - pynormaliz A 3-dimensional polyhedron in (Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790?)^3 defined as the convex hull of 120 vertices - sage: perm_f4 = polytopes.generalized_permutahedron(['F',4],backend='normaliz') # optional - pynormaliz - sage: perm_f4 # optional - pynormaliz + sage: perm_f4 = polytopes.generalized_permutahedron(['F',4],backend='normaliz') # optional - pynormaliz, long time + sage: perm_f4 # optional - pynormaliz, long time A 4-dimensional polyhedron in (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^4 defined as the convex hull of 1152 vertices .. SEEALSO:: @@ -2661,7 +2978,7 @@ def one_hundred_twenty_cell(self, exact=True, backend=None, construction='coxete The ``'normaliz'`` is faster:: - sage: polytopes.one_hundred_twenty_cell(backend='normaliz') # optional - pynormaliz + sage: P = polytopes.one_hundred_twenty_cell(backend='normaliz'); P # optional - pynormaliz A 4-dimensional polyhedron in (Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as the convex hull of 600 vertices @@ -2670,6 +2987,10 @@ def one_hundred_twenty_cell(self, exact=True, backend=None, construction='coxete sage: polytopes.one_hundred_twenty_cell(backend='normaliz',construction='as_permutahedron') # not tested - long time A 4-dimensional polyhedron in AA^4 defined as the convex hull of 600 vertices + + TESTS:: + + sage: TestSuite(P).run() # optional - pynormaliz, long time """ if construction == 'coxeter': if not exact: @@ -2730,7 +3051,7 @@ def hypercube(self, dim, intervals=None, backend=None): - ``intervals`` -- (default = None). It takes the following possible inputs: - - If ``None`` (the default), it returns the the `\pm 1`-cube of + - If ``None`` (the default), it returns the `\pm 1`-cube of dimension ``dim``. - ``'zero_one'`` -- (string). Return the `0/1`-cube. @@ -2759,7 +3080,7 @@ def hypercube(self, dim, intervals=None, backend=None): sage: z_cube = polytopes.hypercube(4,intervals = 'zero_one') sage: z_cube.vertices()[0] - A vertex at (0, 0, 0, 0) + A vertex at (1, 0, 1, 1) sage: z_cube.is_simple() True sage: z_cube.base_ring() @@ -2784,6 +3105,19 @@ def hypercube(self, dim, intervals=None, backend=None): sage: fc = polytopes.hypercube(4,backend='normaliz') # optional - pynormaliz sage: TestSuite(fc).run() # optional - pynormaliz + :: + + sage: ls = [randint(-100,100) for _ in range(4)] + sage: intervals = [[x, x+randint(1,50)] for x in ls] + sage: P = polytopes.hypercube(4, intervals, backend='field') + sage: TestSuite(P).run() + + Check that :trac:`29904` is fixed:: + + sage: intervals = [[-2,2]] + sage: P = polytopes.hypercube(1, intervals, 'field') + sage: TestSuite(P).run() + If the dimension ``dim`` is not equal to the length of intervals, an error is raised:: @@ -2792,6 +3126,13 @@ def hypercube(self, dim, intervals=None, backend=None): ... ValueError: the dimension of the hypercube must match the number of intervals + The intervals must be pairs `(a, b)` with `a < b`:: + + sage: w_cube = polytopes.hypercube(3, intervals = [[0,1],[3,2],[0,3]]) + Traceback (most recent call last): + ... + ValueError: each interval must be a pair `(a, b)` with `a < b` + If a string besides 'zero_one' is passed to ``intervals``, return an error:: @@ -2799,19 +3140,79 @@ def hypercube(self, dim, intervals=None, backend=None): Traceback (most recent call last): ... ValueError: the only allowed string is 'zero_one' + + Check that we set up the hypercube correctly:: + + sage: ls = [randint(-100,100) for _ in range(4)] + sage: intervals = [[x, x+randint(1,50)] for x in ls] + sage: P = polytopes.hypercube(4, intervals, backend='field') + sage: P1 = polytopes.hypercube(4, intervals, backend='ppl') + sage: assert P == P1 + + Check that coercion for input invervals is handled correctly:: + + sage: P = polytopes.hypercube(2, [[1/2, 2], [0, 1]]) + sage: P = polytopes.hypercube(2, [[1/2, 2], [0, 1.0]]) + sage: P = polytopes.hypercube(2, [[1/2, 2], [0, AA(2).sqrt()]]) + sage: P = polytopes.hypercube(2, [[1/2, 2], [0, 1.0]], backend='ppl') + Traceback (most recent call last): + ... + ValueError: specified backend ppl cannot handle the intervals """ + parent = Polyhedra(ZZ, dim, backend=backend) + convert = False + + # If the intervals are (a_1,b_1), ..., (a_dim, b_dim), + # then the inequalites correspond to + # b_1,b_2,...,b_dim, a_1,a_2,...,a_dim + # in that order. + if intervals is None: - cp = list(itertools.product([-1,1], repeat=dim)) + cp = itertools.product((-1,1), repeat=dim) + + # An inequality -x_i + 1 >= 0 for i < dim + # resp. x_{dim-i} + 1 >= 0 for i >= dim + ieq_b = lambda i: 1 + elif isinstance(intervals, str): if intervals == 'zero_one': - cp = list(itertools.product([0,1], repeat=dim)) + cp = itertools.product((0,1), repeat=dim) + + # An inequality -x_i + 1 >= 0 for i < dim + # resp. x_{dim-i} + 0 >= 0 for i >= dim + ieq_b = lambda i: 1 if i < dim else 0 else: raise ValueError("the only allowed string is 'zero_one'") elif len(intervals) == dim: - cp = list(itertools.product(*intervals)) + if not all(a < b for a,b in intervals): + raise ValueError("each interval must be a pair `(a, b)` with `a < b`") + parent = parent.base_extend(sum(a + b for a,b in intervals)) + if parent.base_ring() not in (ZZ, QQ): + convert = True + if backend and parent.backend() is not backend: + # If the parent changed backends, but a backend was specified, + # the specified backend cannot handle the intervals. + raise ValueError("specified backend {} cannot handle the intervals".format(backend)) + + cp = itertools.product(*intervals) + + # An inequality -x_i + b_i >= 0 for i < dim + # resp. x_{dim-i} - a_i >= 0 for i >= dim + ieq_b = lambda i: intervals[i][1] if i < dim \ + else -intervals[i-dim][0] else: raise ValueError("the dimension of the hypercube must match the number of intervals") - return Polyhedron(vertices=cp, backend=backend) + + # An inequality -x_i + ieq_b(i) >= 0 for i < dim + # resp. x_{dim-i} + ieq_b(i-dim) >= 0 for i >= dim + ieq_A = lambda i, pos: -1 if i == pos \ + else 1 if i == pos + dim \ + else 0 + ieqs = (tuple(ieq_b(i) if pos == 0 else ieq_A(i, pos-1) + for pos in range(dim+1)) + for i in range(2*dim)) + + return parent([cp, [], []], [ieqs, []], convert=convert, Vrep_minimal=True, Hrep_minimal=True, pref_rep='Hrep') def cube(self, intervals=None, backend=None): r""" @@ -2862,14 +3263,14 @@ def cube(self, intervals=None, backend=None): sage: cc = polytopes.cube(intervals ='zero_one') sage: cc.vertices_list() - [[0, 0, 0], - [0, 0, 1], - [0, 1, 0], - [0, 1, 1], - [1, 0, 0], - [1, 0, 1], - [1, 1, 0], - [1, 1, 1]] + [[1, 0, 0], + [1, 1, 0], + [1, 1, 1], + [1, 0, 1], + [0, 0, 1], + [0, 0, 0], + [0, 1, 0], + [0, 1, 1]] """ return self.hypercube(3, backend=backend, intervals=intervals) @@ -2900,10 +3301,24 @@ def cross_polytope(self, dim, backend=None): sage: cp = polytopes.cross_polytope(4,backend='normaliz') # optional - pynormaliz sage: TestSuite(cp).run() # optional - pynormaliz + + :: + + sage: P = polytopes.cross_polytope(6, backend='field') + sage: TestSuite(P).run() + + Check that double description is set up correctly:: + + sage: P = polytopes.cross_polytope(6, backend='ppl') + sage: Q = polytopes.cross_polytope(6, backend='ppl') + sage: P == Q + True """ - verts = list((ZZ**dim).basis()) - verts.extend([-v for v in verts]) - return Polyhedron(vertices=verts, backend=backend) + verts = tuple((ZZ**dim).basis()) + verts += tuple(-v for v in verts) + ieqs = ((1,) + x for x in itertools.product((-1,1), repeat=dim)) + parent = Polyhedra(ZZ, dim, backend=backend) + return parent([verts, [], []], [ieqs, []], Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrep') def parallelotope(self, generators, backend=None): r""" @@ -2927,10 +3342,14 @@ def parallelotope(self, generators, backend=None): sage: K = QuadraticField(2, 'sqrt2') sage: sqrt2 = K.gen() - sage: polytopes.parallelotope([ (1,sqrt2), (1,-1) ]) + sage: P = polytopes.parallelotope([ (1,sqrt2), (1,-1) ]); P A 2-dimensional polyhedron in (Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.414213562373095?)^2 defined as the convex hull of 4 vertices + + TESTS:: + + sage: TestSuite(P).run() """ from sage.modules.free_module_element import vector from sage.structure.sequence import Sequence diff --git a/src/sage/geometry/polyhedron/modules/__init__.py b/src/sage/geometry/polyhedron/modules/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/geometry/polyhedron/modules/formal_polyhedra_module.py b/src/sage/geometry/polyhedron/modules/formal_polyhedra_module.py new file mode 100644 index 00000000000..e3b55989563 --- /dev/null +++ b/src/sage/geometry/polyhedron/modules/formal_polyhedra_module.py @@ -0,0 +1,148 @@ +r""" +Formal modules generated by polyhedra +""" +from sage.combinat.free_module import CombinatorialFreeModule +from sage.categories.graded_modules_with_basis import GradedModulesWithBasis + + +class FormalPolyhedraModule(CombinatorialFreeModule): + r""" + Class for formal modules generated by polyhedra. + + It is formal because it is free -- it does not know + about linear relations of polyhedra. + + A formal polyhedral module is graded by dimension. + + INPUT: + + - ``base_ring`` -- base ring of the module; unrelated to the + base ring of the polyhedra + + - ``dimension`` -- the ambient dimension of the polyhedra + + - ``basis`` -- the basis + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.modules.formal_polyhedra_module import FormalPolyhedraModule + sage: def closed_interval(a,b): return Polyhedron(vertices=[[a], [b]]) + + A three-dimensional vector space of polyhedra:: + + sage: I01 = closed_interval(0, 1); I01.rename("conv([0], [1])") + sage: I11 = closed_interval(1, 1); I11.rename("{[1]}") + sage: I12 = closed_interval(1, 2); I12.rename("conv([1], [2])") + sage: basis = [I01, I11, I12] + sage: M = FormalPolyhedraModule(QQ, 1, basis=basis); M + Free module generated by {conv([0], [1]), {[1]}, conv([1], [2])} over Rational Field + sage: M.get_order() + [conv([0], [1]), {[1]}, conv([1], [2])] + + A one-dimensional subspace; bases of subspaces just use the indexing + set `0, \dots, d-1`, where `d` is the dimension:: + + sage: M_lower = M.submodule([M(I11)]); M_lower + Free module generated by {0} over Rational Field + sage: M_lower.print_options(prefix='S') + sage: M_lower.is_submodule(M) + True + sage: x = M(I01) - 2*M(I11) + M(I12) + sage: M_lower.reduce(x) + [conv([0], [1])] + [conv([1], [2])] + sage: M_lower.retract.domain() is M + True + sage: y = M_lower.retract(M(I11)); y + S[0] + sage: M_lower.lift(y) + [{[1]}] + + Quotient space; bases of quotient space are families indexed by + elements of the ambient space:: + + sage: M_mod_lower = M.quotient_module(M_lower); M_mod_lower + Free module generated by {conv([0], [1]), conv([1], [2])} over Rational Field + sage: M_mod_lower.print_options(prefix='Q') + sage: M_mod_lower.retract(x) + Q[conv([0], [1])] + Q[conv([1], [2])] + sage: M_mod_lower.retract(M(I01) - 2*M(I11) + M(I12)) == M_mod_lower.retract(M(I01) + M(I12)) + True + + """ + + @staticmethod + def __classcall__(cls, base_ring, dimension, basis, category=None): + r""" + Normalize the arguments for caching. + + TESTS:: + + sage: from sage.geometry.polyhedron.modules.formal_polyhedra_module import FormalPolyhedraModule + sage: FormalPolyhedraModule(QQ, 1, ()) is FormalPolyhedraModule(QQ, 1, []) + True + """ + if isinstance(basis, list): + basis = tuple(basis) + if category is None: + category = GradedModulesWithBasis(base_ring) + return super(FormalPolyhedraModule, cls).__classcall__(cls, + base_ring=base_ring, + dimension=dimension, + basis=basis, + category=category) + + def __init__(self, base_ring, dimension, basis, category): + """ + Construct a free module generated by the polyhedra in ``basis``. + + TESTS:: + + sage: from sage.geometry.polyhedron.modules.formal_polyhedra_module import FormalPolyhedraModule + sage: def closed_interval(a,b): return Polyhedron(vertices=[[a], [b]]) + sage: I01 = closed_interval(0, 1); I01.rename("conv([0], [1])") + sage: I11 = closed_interval(1, 1); I11.rename("{[1]}") + sage: I12 = closed_interval(1, 2); I12.rename("conv([1], [2])") + sage: I02 = closed_interval(0, 2); I02.rename("conv([0], [2])") + sage: M = FormalPolyhedraModule(QQ, 1, basis=[I01, I11, I12, I02]) + sage: TestSuite(M).run() + """ + super(FormalPolyhedraModule, self).__init__(base_ring, basis, prefix="", category=category) + + def degree_on_basis(self, m): + r""" + The degree of an element of the basis is defined as the dimension of the polyhedron. + + INPUT: + + - ``m`` -- an element of the basis (a polyhedron) + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.modules.formal_polyhedra_module import FormalPolyhedraModule + sage: def closed_interval(a,b): return Polyhedron(vertices=[[a], [b]]) + sage: I01 = closed_interval(0, 1); I01.rename("conv([0], [1])") + sage: I11 = closed_interval(1, 1); I11.rename("{[1]}") + sage: I12 = closed_interval(1, 2); I12.rename("conv([1], [2])") + sage: I02 = closed_interval(0, 2); I02.rename("conv([0], [2])") + sage: M = FormalPolyhedraModule(QQ, 1, basis=[I01, I11, I12, I02]) + + We can extract homogeneous components:: + + sage: O = M(I01) + M(I11) + M(I12) + sage: O.homogeneous_component(0) + [{[1]}] + sage: O.homogeneous_component(1) + [conv([0], [1])] + [conv([1], [2])] + + We note that modulo the linear relations of polyhedra, this would only be a filtration, + not a grading, as the following example shows:: + + sage: X = M(I01) + M(I12) - M(I02) + sage: X.degree() + 1 + + sage: Y = M(I11) + sage: Y.degree() + 0 + """ + return m.dimension() diff --git a/src/sage/geometry/polyhedron/palp_database.py b/src/sage/geometry/polyhedron/palp_database.py index 9f91adc4f17..4e4d18532a1 100644 --- a/src/sage/geometry/polyhedron/palp_database.py +++ b/src/sage/geometry/polyhedron/palp_database.py @@ -33,8 +33,6 @@ from subprocess import Popen, PIPE -import six - from sage.structure.sage_object import SageObject from sage.rings.all import ZZ @@ -136,16 +134,8 @@ def _palp_Popen(self): """ - if six.PY2: - encoding_kwargs = {} - else: - encoding_kwargs = { - 'encoding': 'utf-8', - 'errors': 'surrogateescape' - } - return Popen(["class.x", "-b2a", "-di", self._data_basename], - stdout=PIPE, **encoding_kwargs) + stdout=PIPE, encoding='utf-8', errors='surrogateescape') def _read_vertices(self, stdout, rows, cols): r""" @@ -470,15 +460,8 @@ def _palp_Popen(self): sage: polygons._palp_Popen() # optional - polytopes_db_4d """ - if six.PY2: - encoding_kwargs = {} - else: - encoding_kwargs = { - 'encoding': 'utf-8', - 'errors': 'surrogateescape' - } return Popen(['class-4d.x', '-He', 'H{}:{}L100000000'.format(self._h21, self._h11), '-di', self._data_basename], stdout=PIPE, - **encoding_kwargs) + encoding='utf-8', errors='surrogateescape') diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index 63c98450b9d..e8de9191ddd 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -13,16 +13,19 @@ from sage.structure.parent import Parent from sage.structure.element import get_coercion_model from sage.structure.unique_representation import UniqueRepresentation -from sage.modules.free_module import is_FreeModule +from sage.modules.free_module import FreeModule, is_FreeModule from sage.misc.cachefunc import cached_method, cached_function from sage.rings.all import ZZ, QQ, RDF, CommutativeRing from sage.categories.fields import Fields +from sage.categories.rings import Rings +from sage.categories.modules import Modules from sage.geometry.polyhedron.base import is_Polyhedron from .representation import Inequality, Equation, Vertex, Ray, Line -def Polyhedra(base_ring, ambient_dim, backend=None): +def Polyhedra(ambient_space_or_base_ring=None, ambient_dim=None, backend=None, *, + ambient_space=None, base_ring=None): r""" Construct a suitable parent class for polyhedra @@ -33,6 +36,8 @@ def Polyhedra(base_ring, ambient_dim, backend=None): - ``ambient_dim`` -- integer. The ambient space dimension. + - ``ambient_space`` -- A free module. + - ``backend`` -- string. The name of the backend for computations. There are several backends implemented: @@ -72,6 +77,20 @@ def Polyhedra(base_ring, ambient_dim, backend=None): sage: Polyhedra(ZZ, 3, backend='cdd') Polyhedra in QQ^3 + Using a more general form of the constructor:: + + sage: V = VectorSpace(QQ, 3) + sage: Polyhedra(V) is Polyhedra(QQ, 3) + True + sage: Polyhedra(V, backend='field') is Polyhedra(QQ, 3, 'field') + True + sage: Polyhedra(backend='field', ambient_space=V) is Polyhedra(QQ, 3, 'field') + True + + sage: M = FreeModule(ZZ, 2) + sage: Polyhedra(M, backend='ppl') is Polyhedra(ZZ, 2, 'ppl') + True + TESTS:: sage: Polyhedra(RR, 3, backend='field') @@ -88,6 +107,27 @@ def Polyhedra(base_ring, ambient_dim, backend=None): ValueError: invalid base ring: Number Field in I with defining polynomial x^2 + 1 with I = 1*I cannot be coerced to a real field """ + if ambient_space_or_base_ring is not None: + if ambient_space_or_base_ring in Rings(): + base_ring = ambient_space_or_base_ring + else: + ambient_space = ambient_space_or_base_ring + if ambient_space is not None: + if ambient_space not in Modules: + # There is no category of free modules, unfortunately + # (see https://trac.sagemath.org/ticket/30164)... + raise ValueError('ambient_space must be a free module') + if base_ring is None: + base_ring = ambient_space.base_ring() + if ambient_dim is None: + try: + ambient_dim = ambient_space.rank() + except AttributeError: + # ... so we test whether it is free using the existence of + # a rank method + raise ValueError('ambient_space must be a free module') + if ambient_space is not FreeModule(base_ring, ambient_dim): + raise NotImplementedError('ambient_space must be a standard free module') if backend is None: if base_ring is ZZ or base_ring is QQ: backend = 'ppl' @@ -173,17 +213,50 @@ def __init__(self, base_ring, ambient_dim, backend): sage: from sage.geometry.polyhedron.parent import Polyhedra sage: P = Polyhedra(QQ, 3) sage: TestSuite(P).run(skip='_test_pickling') + sage: P = Polyhedra(QQ, 0) + sage: TestSuite(P).run(skip='_test_pickling') """ self._backend = backend self._ambient_dim = ambient_dim from sage.categories.polyhedra import PolyhedralSets - Parent.__init__(self, base=base_ring, category=PolyhedralSets(base_ring)) + from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets + category = PolyhedralSets(base_ring) + if ambient_dim == 0: + category = category & FiniteEnumeratedSets() + else: + category = category.Infinite() + + Parent.__init__(self, base=base_ring, category=category) self._Inequality_pool = [] self._Equation_pool = [] self._Vertex_pool = [] self._Ray_pool = [] self._Line_pool = [] + def list(self): + """ + Return the two polyhedra in ambient dimension 0, raise an error otherwise + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.parent import Polyhedra + sage: P = Polyhedra(QQ, 3) + sage: P.cardinality() + +Infinity + + sage: P = Polyhedra(AA, 0) + sage: P.category() + Category of finite enumerated polyhedral sets over Algebraic Real Field + sage: P.list() + [The empty polyhedron in AA^0, + A 0-dimensional polyhedron in AA^0 defined as the convex hull of 1 vertex] + sage: P.cardinality() + 2 + """ + if self.ambient_dim(): + raise NotImplementedError + return [self.empty(), self.universe()] + def recycle(self, polyhedron): """ Recycle the H/V-representation objects of a polyhedron. diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index a1235f0861a..6b167bfdf6c 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -152,7 +152,7 @@ class ProjectionFuncStereographic(): sage: cube = polytopes.hypercube(3).vertices() sage: proj = ProjectionFuncStereographic([1.2, 3.4, 5.6]) sage: ppoints = [proj(vector(x)) for x in cube] - sage: ppoints[0] + sage: ppoints[5] (-0.0486511..., 0.0859565...) """ def __init__(self, projection_point): @@ -500,7 +500,7 @@ def coord_index_of(self, v): sage: p = polytopes.hypercube(3) sage: proj = p.projection() sage: proj.coord_index_of(vector((1,1,1))) - 7 + 2 """ try: return self.coords.index(v) @@ -518,7 +518,7 @@ def coord_indices_of(self, v_list): sage: p = polytopes.hypercube(3) sage: proj = p.projection() sage: proj.coord_indices_of([vector((1,1,1)),vector((1,-1,1))]) - [7, 5] + [2, 3] """ return [self.coord_index_of(v) for v in v_list] @@ -928,7 +928,7 @@ def render_wireframe_3d(self, **kwds): sage: cube_proj = cube.projection() sage: wire = cube_proj.render_wireframe_3d() sage: print(wire.tachyon().split('\n')[77]) # for testing - FCylinder base -1.0 1.0 -1.0 apex -1.0 -1.0 -1.0 rad 0.005 texture... + FCylinder base 1.0 1.0 -1.0 apex 1.0 1.0 1.0 rad 0.005 texture... """ wireframe = [] for l in self.lines: @@ -1509,15 +1509,15 @@ def _tikz_3d_in_3d(self, view, angle, scale, edge_color, sage: print('\n'.join(ImageAsso.splitlines()[29:41])) %% Drawing edges in the back %% - \draw[edge,back] (-0.50000, -0.50000, -0.50000) -- (-1.00000, 0.00000, 0.00000); - \draw[edge,back] (-0.50000, -0.50000, -0.50000) -- (0.00000, -1.00000, 0.00000); - \draw[edge,back] (-0.50000, -0.50000, -0.50000) -- (0.00000, 0.00000, -1.00000); + \draw[edge,back] (0.00000, 1.00000, -1.00000) -- (0.00000, 0.00000, -1.00000); + \draw[edge,back] (1.00000, -1.00000, 0.00000) -- (0.00000, -1.00000, 0.00000); + \draw[edge,back] (1.00000, 0.00000, -1.00000) -- (0.00000, 0.00000, -1.00000); + \draw[edge,back] (0.00000, 0.00000, -1.00000) -- (-0.50000, -0.50000, -0.50000); + \draw[edge,back] (-1.00000, 1.00000, 0.00000) -- (-1.00000, 0.00000, 0.00000); \draw[edge,back] (-1.00000, 0.00000, 0.00000) -- (-1.00000, 0.00000, 1.00000); - \draw[edge,back] (-1.00000, 0.00000, 0.00000) -- (-1.00000, 1.00000, 0.00000); - \draw[edge,back] (0.00000, -1.00000, 0.00000) -- (0.00000, -1.00000, 1.00000); - \draw[edge,back] (0.00000, -1.00000, 0.00000) -- (1.00000, -1.00000, 0.00000); - \draw[edge,back] (0.00000, 0.00000, -1.00000) -- (0.00000, 1.00000, -1.00000); - \draw[edge,back] (0.00000, 0.00000, -1.00000) -- (1.00000, 0.00000, -1.00000); + \draw[edge,back] (-1.00000, 0.00000, 0.00000) -- (-0.50000, -0.50000, -0.50000); + \draw[edge,back] (0.00000, -1.00000, 1.00000) -- (0.00000, -1.00000, 0.00000); + \draw[edge,back] (0.00000, -1.00000, 0.00000) -- (-0.50000, -0.50000, -0.50000); %% """ view_vector = vector(RDF, view) diff --git a/src/sage/geometry/polyhedron/ppl_lattice_polytope.py b/src/sage/geometry/polyhedron/ppl_lattice_polytope.py index 4281de7bf0f..57be0ec8010 100644 --- a/src/sage/geometry/polyhedron/ppl_lattice_polytope.py +++ b/src/sage/geometry/polyhedron/ppl_lattice_polytope.py @@ -56,7 +56,7 @@ - Volker Braun: initial version, 2012 """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Volker Braun # # This program is free software: you can redistribute it and/or modify @@ -64,10 +64,7 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # https://www.gnu.org/licenses/ -#***************************************************************************** - -from __future__ import absolute_import, print_function - +# **************************************************************************** import copy from sage.rings.integer import GCD_list, Integer from sage.rings.integer_ring import ZZ @@ -76,7 +73,7 @@ from sage.matrix.constructor import matrix from ppl import ( C_Polyhedron, Linear_Expression, Variable, - point, ray, line, Generator, Generator_System, + point, line, Generator, Generator_System, Poly_Con_Relation ) @@ -218,21 +215,21 @@ def __repr__(self): The empty lattice polytope in ZZ^0 """ desc = '' - if self.n_vertices()==0: + if self.n_vertices() == 0: desc += 'The empty lattice polytope' else: desc += 'A ' + repr(self.affine_dimension()) + '-dimensional lattice polytope' desc += ' in ZZ^' + repr(self.space_dimension()) - if self.n_vertices()>0: + if self.n_vertices() > 0: desc += ' with ' desc += repr(self.n_vertices()) - if self.n_vertices()==1: desc += ' vertex' - else: desc += ' vertices' + if self.n_vertices() == 1: + desc += ' vertex' + else: + desc += ' vertices' return desc - - def is_bounded(self): """ Return whether the lattice polytope is compact. @@ -1092,7 +1089,6 @@ def lattice_automorphism_group(self, points=None, point_labels=None): basis_inverse = basis.inverse() from sage.groups.perm_gps.permgroup import PermutationGroup - from sage.groups.perm_gps.permgroup_element import PermutationGroupElement lattice_gens = [] G = self.restricted_automorphism_group( vertex_labels=tuple(range(len(vertices)))) diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index 8d29c4fac62..d68682c6f09 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -182,7 +182,7 @@ def vector(self, base_ring=None): sage: C = polytopes.cube() sage: C.vertices()[0].vector()[0] = 3 sage: C.vertices()[0] - A vertex at (-1, -1, -1) + A vertex at (1, -1, -1) """ if (base_ring is None) or (base_ring is self._base_ring): return copy(self._vector) @@ -429,7 +429,7 @@ def A(self): sage: C = polytopes.cube() sage: C.inequalities()[0].A()[2] = 5 sage: C.inequalities()[0] - An inequality (0, 0, -1) x + 1 >= 0 + An inequality (-1, 0, 0) x + 1 >= 0 """ return copy(self._A) @@ -751,7 +751,7 @@ def contains(self, Vobj): [True, True, True, True, True, True] sage: p2 = 3*polytopes.hypercube(3) sage: [i1.contains(q) for q in p2.vertex_generator()] - [True, False, False, False, True, True, True, False] + [True, True, False, True, False, True, False, False] """ try: if Vobj.is_vector(): # assume we were passed a point @@ -778,7 +778,7 @@ def interior_contains(self, Vobj): [False, True, True, False, False, True] sage: p2 = 3*polytopes.hypercube(3) sage: [i1.interior_contains(q) for q in p2.vertex_generator()] - [True, False, False, False, True, True, True, False] + [True, True, False, True, False, True, False, False] If you pass a vector, it is assumed to be the coordinate vector of a point:: @@ -1105,12 +1105,12 @@ def is_incident(self, Hobj): sage: p = polytopes.hypercube(3) sage: h1 = next(p.inequality_generator()) sage: h1 - An inequality (0, 0, -1) x + 1 >= 0 + An inequality (-1, 0, 0) x + 1 >= 0 sage: v1 = next(p.vertex_generator()) sage: v1 - A vertex at (-1, -1, -1) + A vertex at (1, -1, -1) sage: v1.is_incident(h1) - False + True """ return self.polyhedron().incidence_matrix()[self.index(), Hobj.index()] == 1 @@ -1124,7 +1124,7 @@ def __mul__(self, Hobj): sage: h1 = next(p.inequality_generator()) sage: v1 = next(p.vertex_generator()) sage: v1.__mul__(h1) - 2 + 0 """ return self.evaluated_on(Hobj) @@ -1252,11 +1252,11 @@ def evaluated_on(self, Hobj): sage: v = next(p.vertex_generator()) sage: h = next(p.inequality_generator()) sage: v - A vertex at (-1, -1, -1) + A vertex at (1, -1, -1) sage: h - An inequality (0, 0, -1) x + 1 >= 0 + An inequality (-1, 0, 0) x + 1 >= 0 sage: v.evaluated_on(h) - 2 + 0 """ return Hobj.A() * self.vector() + Hobj.b() diff --git a/src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py b/src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py index 8eac6e0d722..5bd80fb5d81 100644 --- a/src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py +++ b/src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py @@ -2,8 +2,9 @@ Differential Geometry of Parametrized Surfaces AUTHORS: - - Mikhail Malakhaltsev (2010-09-25): initial version - - Joris Vankerschaver (2010-10-25): implementation, doctests + +- Mikhail Malakhaltsev (2010-09-25): initial version +- Joris Vankerschaver (2010-10-25): implementation, doctests """ # **************************************************************************** @@ -653,7 +654,6 @@ def first_fundamental_form_coefficients(self): """ coefficients = {} for index in product((1, 2), repeat=2): - sorted_index = sorted(index) coefficients[index] = \ self._compute_first_fundamental_form_coefficient(index) return coefficients @@ -1590,10 +1590,6 @@ def geodesics_numerical(self, p0, v0, tinterval): sage: [round4(p) for p in ext_points] [[1.000, 0.0000, 0.0000], [-0.2049, 0.6921, 0.6921], [-0.9160, -0.2836, -0.2836], [0.5803, -0.5759, -0.5759], [0.6782, 0.5196, 0.5196], [-0.8582, 0.3629, 0.3629]] """ - - u1 = self.variables[1] - u2 = self.variables[2] - solver = self._create_geodesic_ode_system() t_interval, n = tinterval[0:2], tinterval[2] @@ -1710,10 +1706,6 @@ def parallel_translation_numerical(self,curve,t,v0,tinterval): [[1.000, 1.000], [0.9876, 1.025], [0.9499, 1.102], [0.8853, 1.238], [0.7920, 1.448], [0.6687, 1.762]] """ - - u1 = self.variables[1] - u2 = self.variables[2] - solver = self._create_pt_ode_system(tuple(curve), t) t_interval, n = tinterval[0:2], tinterval[2] diff --git a/src/sage/geometry/riemannian_manifolds/surface3d_generators.py b/src/sage/geometry/riemannian_manifolds/surface3d_generators.py index 8bfbba189cb..078af43b338 100644 --- a/src/sage/geometry/riemannian_manifolds/surface3d_generators.py +++ b/src/sage/geometry/riemannian_manifolds/surface3d_generators.py @@ -114,7 +114,7 @@ def Dini(a=1, b=1, name="Dini's surface"): - ``name`` -- string. Name of the surface. - For more information, see :wikipedia:`Dini's_surface`. + For more information, see :wikipedia:`Dini%27s_surface`. EXAMPLES:: diff --git a/src/sage/geometry/toric_plotter.py b/src/sage/geometry/toric_plotter.py index 5ba73413734..ffe59cc81db 100644 --- a/src/sage/geometry/toric_plotter.py +++ b/src/sage/geometry/toric_plotter.py @@ -46,7 +46,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six import iteritems from copy import copy from math import pi @@ -203,12 +202,12 @@ def __init__(self, all_options, dimension, generators=None): extra_options = dict() self.extra_options = extra_options toric_options = options() - for option, value in iteritems(all_options): + for option, value in all_options.items(): if option in toric_options: sd[option] = value else: extra_options[option] = value - for option, value in iteritems(toric_options): + for option, value in toric_options.items(): if option not in sd: sd[option] = value if dimension not in [1, 2, 3]: @@ -607,7 +606,7 @@ def plot_walls(self, walls): sage: tp = ToricPlotter(dict(), 2, quadrant.rays()) sage: tp.plot_walls([quadrant]) Graphics object consisting of 2 graphics primitives - + Let's also check that the truncating polyhedron is functioning correctly:: diff --git a/src/sage/geometry/triangulation/base.pyx b/src/sage/geometry/triangulation/base.pyx index b575e5af4d1..5e062327e23 100644 --- a/src/sage/geometry/triangulation/base.pyx +++ b/src/sage/geometry/triangulation/base.pyx @@ -1,3 +1,7 @@ +# distutils: sources = sage/geometry/triangulation/functions.cc sage/geometry/triangulation/data.cc sage/geometry/triangulation/triangulations.cc +# distutils: depends = sage/geometry/triangulation/functions.h sage/geometry/triangulation/data.h sage/geometry/triangulation/triangulations.h +# distutils: language = c++ + r""" Base classes for triangulations diff --git a/src/sage/geometry/triangulation/element.py b/src/sage/geometry/triangulation/element.py index a80d9111e4d..fe7a6560946 100644 --- a/src/sage/geometry/triangulation/element.py +++ b/src/sage/geometry/triangulation/element.py @@ -39,8 +39,6 @@ # https://www.gnu.org/licenses/ #***************************************************************************** -from six import iteritems - from sage.structure.richcmp import richcmp from sage.structure.element import Element from sage.rings.all import QQ, ZZ @@ -606,31 +604,31 @@ def _boundary_simplex_dictionary(self): sage: triangulation = polytopes.hypercube(2).triangulate(engine='internal') sage: triangulation._boundary_simplex_dictionary() {(0, 1): ((0, 1, 3),), - (0, 2): ((0, 2, 3),), - (0, 3): ((0, 1, 3), (0, 2, 3)), - (1, 3): ((0, 1, 3),), - (2, 3): ((0, 2, 3),)} + (0, 3): ((0, 1, 3),), + (1, 2): ((1, 2, 3),), + (1, 3): ((0, 1, 3), (1, 2, 3)), + (2, 3): ((1, 2, 3),)} sage: triangulation = polytopes.cube().triangulate(engine='internal') sage: triangulation._boundary_simplex_dictionary() {(0, 1, 2): ((0, 1, 2, 7),), - (0, 1, 4): ((0, 1, 4, 7),), - (0, 1, 7): ((0, 1, 2, 7), (0, 1, 4, 7)), - (0, 2, 4): ((0, 2, 4, 7),), - (0, 2, 7): ((0, 1, 2, 7), (0, 2, 4, 7)), - (0, 4, 7): ((0, 1, 4, 7), (0, 2, 4, 7)), - (1, 2, 3): ((1, 2, 3, 7),), - (1, 2, 7): ((0, 1, 2, 7), (1, 2, 3, 7)), - (1, 3, 7): ((1, 2, 3, 7),), - (1, 4, 5): ((1, 4, 5, 7),), - (1, 4, 7): ((0, 1, 4, 7), (1, 4, 5, 7)), - (1, 5, 7): ((1, 4, 5, 7),), - (2, 3, 7): ((1, 2, 3, 7),), - (2, 4, 6): ((2, 4, 6, 7),), - (2, 4, 7): ((0, 2, 4, 7), (2, 4, 6, 7)), - (2, 6, 7): ((2, 4, 6, 7),), - (4, 5, 7): ((1, 4, 5, 7),), - (4, 6, 7): ((2, 4, 6, 7),)} + (0, 1, 5): ((0, 1, 5, 7),), + (0, 1, 7): ((0, 1, 2, 7), (0, 1, 5, 7)), + (0, 2, 3): ((0, 2, 3, 7),), + (0, 2, 7): ((0, 1, 2, 7), (0, 2, 3, 7)), + (0, 3, 4): ((0, 3, 4, 7),), + (0, 3, 7): ((0, 2, 3, 7), (0, 3, 4, 7)), + (0, 4, 5): ((0, 4, 5, 7),), + (0, 4, 7): ((0, 3, 4, 7), (0, 4, 5, 7)), + (0, 5, 7): ((0, 1, 5, 7), (0, 4, 5, 7)), + (1, 2, 7): ((0, 1, 2, 7),), + (1, 5, 6): ((1, 5, 6, 7),), + (1, 5, 7): ((0, 1, 5, 7), (1, 5, 6, 7)), + (1, 6, 7): ((1, 5, 6, 7),), + (2, 3, 7): ((0, 2, 3, 7),), + (3, 4, 7): ((0, 3, 4, 7),), + (4, 5, 7): ((0, 4, 5, 7),), + (5, 6, 7): ((1, 5, 6, 7),)} """ result = dict() for simplex in self: @@ -655,25 +653,25 @@ def boundary(self): sage: triangulation = polytopes.cube().triangulate(engine='internal') sage: triangulation - (<0,1,2,7>, <0,1,4,7>, <0,2,4,7>, <1,2,3,7>, <1,4,5,7>, <2,4,6,7>) + (<0,1,2,7>, <0,1,5,7>, <0,2,3,7>, <0,3,4,7>, <0,4,5,7>, <1,5,6,7>) sage: triangulation.boundary() frozenset({(0, 1, 2), - (0, 1, 4), - (0, 2, 4), - (1, 2, 3), - (1, 3, 7), - (1, 4, 5), - (1, 5, 7), + (0, 1, 5), + (0, 2, 3), + (0, 3, 4), + (0, 4, 5), + (1, 2, 7), + (1, 5, 6), + (1, 6, 7), (2, 3, 7), - (2, 4, 6), - (2, 6, 7), + (3, 4, 7), (4, 5, 7), - (4, 6, 7)}) + (5, 6, 7)}) sage: triangulation.interior_facets() - frozenset({(0, 1, 7), (0, 2, 7), (0, 4, 7), (1, 2, 7), (1, 4, 7), (2, 4, 7)}) + frozenset({(0, 1, 7), (0, 2, 7), (0, 3, 7), (0, 4, 7), (0, 5, 7), (1, 5, 7)}) """ return frozenset(facet for facet, bounded_simplices - in iteritems(self._boundary_simplex_dictionary()) + in self._boundary_simplex_dictionary().items() if len(bounded_simplices) == 1) @cached_method @@ -691,25 +689,25 @@ def interior_facets(self): sage: triangulation = polytopes.cube().triangulate(engine='internal') sage: triangulation - (<0,1,2,7>, <0,1,4,7>, <0,2,4,7>, <1,2,3,7>, <1,4,5,7>, <2,4,6,7>) + (<0,1,2,7>, <0,1,5,7>, <0,2,3,7>, <0,3,4,7>, <0,4,5,7>, <1,5,6,7>) sage: triangulation.boundary() frozenset({(0, 1, 2), - (0, 1, 4), - (0, 2, 4), - (1, 2, 3), - (1, 3, 7), - (1, 4, 5), - (1, 5, 7), + (0, 1, 5), + (0, 2, 3), + (0, 3, 4), + (0, 4, 5), + (1, 2, 7), + (1, 5, 6), + (1, 6, 7), (2, 3, 7), - (2, 4, 6), - (2, 6, 7), + (3, 4, 7), (4, 5, 7), - (4, 6, 7)}) + (5, 6, 7)}) sage: triangulation.interior_facets() - frozenset({(0, 1, 7), (0, 2, 7), (0, 4, 7), (1, 2, 7), (1, 4, 7), (2, 4, 7)}) + frozenset({(0, 1, 7), (0, 2, 7), (0, 3, 7), (0, 4, 7), (0, 5, 7), (1, 5, 7)}) """ return frozenset(facet for facet, bounded_simplices - in iteritems(self._boundary_simplex_dictionary()) + in self._boundary_simplex_dictionary().items() if len(bounded_simplices) == 2) @cached_method @@ -739,21 +737,21 @@ def normal_cone(self): sage: triangulation = polytopes.hypercube(2).triangulate(engine='internal') sage: triangulation - (<0,1,3>, <0,2,3>) + (<0,1,3>, <1,2,3>) sage: N = triangulation.normal_cone(); N 4-d cone in 4-d lattice sage: N.rays() - (-1, 0, 0, 0), - ( 1, 0, 1, 0), - (-1, 0, -1, 0), - ( 1, 0, 0, -1), - (-1, 0, 0, 1), - ( 1, 1, 0, 0), - (-1, -1, 0, 0) + ( 0, 0, 0, -1), + ( 0, 0, 1, 1), + ( 0, 0, -1, -1), + ( 1, 0, 0, 1), + (-1, 0, 0, -1), + ( 0, 1, 0, -1), + ( 0, -1, 0, 1) in Ambient free module of rank 4 over the principal ideal domain Integer Ring sage: N.dual().rays() - (-1, 1, 1, -1) + (1, -1, 1, -1) in Ambient free module of rank 4 over the principal ideal domain Integer Ring diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index 892df3f2247..bb392630193 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -1869,7 +1869,9 @@ def contained_simplex(self, large=True, initial_point=None, point_order=None): (P(-1, -1), P(1, 1), P(0, 1)) sage: pc.contained_simplex(point_order = [pc[1],pc[3],pc[4],pc[2],pc[0]]) (P(0, 1), P(1, 1), P(-1, -1)) - sage: # lower-dimensional example: + + Lower-dimensional example:: + sage: pc.contained_simplex(point_order = [pc[0],pc[3],pc[4]]) (P(0, 0), P(1, 1)) @@ -1969,7 +1971,9 @@ def placing_triangulation(self, point_order=None): (<1,2,3>, <1,2,4>) sage: p0.pushing_triangulation(point_order=[0,1,2,3,4]) (<0,1,3>, <0,1,4>, <0,2,3>, <0,2,4>) - sage: # the same triangulation with renumbered points 0->4, 1->0, etc.: + + The same triangulation with renumbered points 0->4, 1->0, etc:: + sage: p1 = PointConfiguration([(+1,0),(-1,0),(0,+1),(0,-1),(0,0)]) sage: p1.pushing_triangulation(point_order=[4,0,1,2,3]) (<0,2,4>, <0,3,4>, <1,2,4>, <1,3,4>) diff --git a/src/sage/graphs/all.py b/src/sage/graphs/all.py index 114a10dd187..74613060b0a 100644 --- a/src/sage/graphs/all.py +++ b/src/sage/graphs/all.py @@ -14,9 +14,6 @@ import sage.graphs.partial_cube from . import graph_list as graphs_list lazy_import("sage.graphs", "graph_coloring") -lazy_import("sage.graphs.cliquer", ['all_max_clique', 'max_clique', - 'clique_number'], - deprecation=26200) from .graph_database import graph_db_info lazy_import("sage.graphs.graph_editor", "graph_editor") @@ -35,15 +32,15 @@ sage.graphs.cliquer are deprecated from the global namespace (:trac:`26200`):: sage: all_max_clique(Graph()) - doctest:...: DeprecationWarning: Importing all_max_clique from here is deprecated. If you need to use it, please import it directly from sage.graphs.cliquer - See https://trac.sagemath.org/26200 for details. - [[]] + Traceback (most recent call last): + ... + NameError: name 'all_max_clique' is not defined sage: max_clique(Graph()) - doctest:...: DeprecationWarning: Importing max_clique from here is deprecated. If you need to use it, please import it directly from sage.graphs.cliquer - See https://trac.sagemath.org/26200 for details. - [] + Traceback (most recent call last): + ... + NameError: name 'max_clique' is not defined sage: clique_number(Graph()) - doctest:...: DeprecationWarning: Importing clique_number from here is deprecated. If you need to use it, please import it directly from sage.graphs.cliquer - See https://trac.sagemath.org/26200 for details. - 0 + Traceback (most recent call last): + ... + NameError: name 'clique_number' is not defined """ diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 6ba0a44c9af..42d3bd1b364 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -212,9 +212,7 @@ cpdef edge_connectivity(g): sage: from sage.graphs.base.boost_graph import edge_connectivity sage: g = graphs.GridGraph([2,2]) - sage: edge_connectivity(g) # py2 - (2, [((0, 1), (1, 1)), ((0, 1), (0, 0))]) - sage: edge_connectivity(g) # py3 + sage: edge_connectivity(g) (2, [((0, 0), (0, 1)), ((0, 0), (1, 0))]) """ from sage.graphs.graph import Graph @@ -541,13 +539,9 @@ cpdef bandwidth_heuristics(g, algorithm='cuthill_mckee'): sage: from sage.graphs.base.boost_graph import bandwidth_heuristics sage: bandwidth_heuristics(graphs.PathGraph(10)) (1, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - sage: bandwidth_heuristics(graphs.GridGraph([3,3])) # py2 - (3, [(2, 2), (2, 1), (1, 2), (2, 0), (1, 1), (0, 2), (1, 0), (0, 1), (0, 0)]) - sage: bandwidth_heuristics(graphs.GridGraph([3,3]), algorithm='king') # py2 - (3, [(2, 2), (2, 1), (1, 2), (2, 0), (1, 1), (0, 2), (1, 0), (0, 1), (0, 0)]) - sage: bandwidth_heuristics(graphs.GridGraph([3,3])) # py3 + sage: bandwidth_heuristics(graphs.GridGraph([3,3])) (3, [(0, 0), (1, 0), (0, 1), (2, 0), (1, 1), (0, 2), (2, 1), (1, 2), (2, 2)]) - sage: bandwidth_heuristics(graphs.GridGraph([3,3]), algorithm='king') # py3 + sage: bandwidth_heuristics(graphs.GridGraph([3,3]), algorithm='king') (3, [(0, 0), (1, 0), (0, 1), (2, 0), (1, 1), (0, 2), (2, 1), (1, 2), (2, 2)]) TESTS: @@ -720,9 +714,9 @@ cpdef min_spanning_tree(g, result = g_boost.prim_min_spanning_tree() sig_off() - cdef size_t n = g.num_verts() + cdef v_index n = g.num_verts() - if result.size() != 2 * (n - 1): + if result.size() != 2 * (n - 1): return [] else: edges = [(int_to_vertex[ result[2*i]], int_to_vertex[ result[2*i+1]]) for i in range(n-1)] @@ -852,7 +846,8 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): - ``weight_function`` -- function (default: ``None``); a function that associates a weight to each edge. If ``None`` (default), the weights of - ``g`` are used, if available, otherwise all edges have weight 1. + ``g`` are used, if ``g.weighted()==True``, otherwise all edges have + weight 1. - ``algorithm`` -- string (default: ``None``); one of the following algorithms: @@ -951,7 +946,7 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): if float(weight_function(e)) < 0: algorithm = 'Bellman-Ford' break - else: + elif g.weighted(): for _,_,w in g.edge_iterator(): if float(w) < 0: algorithm = 'Bellman-Ford' @@ -977,19 +972,22 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): raise ValueError("the graph contains a negative cycle") elif algorithm in ['Dijkstra', 'Dijkstra_Boost']: - if g.is_directed(): - boost_weighted_graph_from_sage_graph(&g_boost_dir, g, v_to_int, weight_function) - vi = v_to_int[start] - sig_on() - result = g_boost_dir.dijkstra_shortest_paths(vi) - sig_off() - else: - boost_weighted_graph_from_sage_graph(&g_boost_und, g, v_to_int, weight_function) - vi = v_to_int[start] - sig_on() - result = g_boost_und.dijkstra_shortest_paths(vi) - sig_off() - if not result.distances.size(): + try: + if g.is_directed(): + boost_weighted_graph_from_sage_graph(&g_boost_dir, g, v_to_int, weight_function) + vi = v_to_int[start] + sig_on() + result = g_boost_dir.dijkstra_shortest_paths(vi) + sig_off() + else: + boost_weighted_graph_from_sage_graph(&g_boost_und, g, v_to_int, weight_function) + vi = v_to_int[start] + sig_on() + result = g_boost_und.dijkstra_shortest_paths(vi) + sig_off() + if not result.distances.size(): + raise RuntimeError("Dijkstra algorithm does not work with negative weights, use Bellman-Ford instead") + except RuntimeError: raise RuntimeError("Dijkstra algorithm does not work with negative weights, use Bellman-Ford instead") else: @@ -1093,7 +1091,8 @@ cpdef johnson_shortest_paths(g, weight_function=None, distances=True, predecesso - ``weight_function`` -- function (default: ``None``); a function that associates a weight to each edge. If ``None`` (default), the weights of - ``g`` are used, if available, otherwise all edges have weight 1. + ``g`` are used, if ``g.weighted()==True``, otherwise all edges have + weight 1. - ``distances`` -- boolean (default: ``True``); whether to return the dictionary of shortest distances @@ -1248,7 +1247,8 @@ cpdef floyd_warshall_shortest_paths(g, weight_function=None, distances=True, pre - ``weight_function`` -- function (default: ``None``); a function that associates a weight to each edge. If ``None`` (default), the weights of - ``g`` are used, if available, otherwise all edges have weight 1. + ``g`` are used, if ``g.weighted()==True``, otherwise all edges have + weight 1. - ``distances`` -- boolean (default: ``True``); whether to return the dictionary of shortest distances @@ -1400,7 +1400,8 @@ cpdef johnson_closeness_centrality(g, weight_function=None): - ``weight_function`` -- function (default: ``None``); a function that associates a weight to each edge. If ``None`` (default), the weights of - ``g`` are used, if available, otherwise all edges have weight 1. + ``g`` are used, if ``g.weighted()==True``, otherwise all edges have + weight 1. OUTPUT: @@ -1507,7 +1508,8 @@ cpdef min_cycle_basis(g_sage, weight_function=None, by_weight=False): - ``weight_function`` -- function (default: ``None``); a function that takes as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` and - ``by_weight`` is ``True``, we use the edge label ``l`` as a weight. + ``by_weight`` is ``True``, the weights of ``g_sage`` are used, if + ``g_sage.weighted()==True``, otherwise all edges have weight 1. - ``by_weight`` -- boolean (default: ``False``); if ``True``, the edges in the graph are weighted, otherwise all edges have weight 1 @@ -1603,3 +1605,998 @@ cpdef min_cycle_basis(g_sage, weight_function=None, by_weight=False): if len(orth_set[j] & new_cycle) % 2: orth_set[j] = orth_set[j] ^ base return cycle_basis + + +cpdef eccentricity_DHV(g, vertex_list=None, weight_function=None, check_weight=True): + r""" + Return the vector of eccentricities using the algorithm of [Dragan2018]_. + + The array returned is of length `n`, and by default its `i`-th component is + the eccentricity of the `i`-th vertex in ``g.vertices()``, + if ``vertex_list is None``, otherwise ``ecc[i]`` is the eccentricity of + vertex ``vertex_list[i]``. + + The algorithm proposed in [Dragan2018]_ is based on the observation that for + all nodes `v,w\in V`, we have `\max(ecc[v]-d(v,w), d(v,w))\leq ecc[w] \leq + ecc[v] + d(v,w)`. Also the algorithm iteratively improves upper and lower + bounds on the eccentricity of each vertex until no further improvements can + be done. + + INPUT: + + - ``g`` -- the input Sage graph. + + - ``vertex_list`` -- list (default: ``None``); a list of `n` vertices + specifying a mapping from `(0, \ldots, n-1)` to vertex labels in `g`. When + set, ``ecc[i]`` is the eccentricity of vertex ``vertex_list[i]``. When + ``vertex_list`` is ``None``, ``ecc[i]`` is the eccentricity of vertex + ``g.vertices()[i]``. + + - ``weight_function`` -- function (default: ``None``); a function that + associates a weight to each edge. If ``None`` (default), the weights of + ``g`` are used, if ``g.weighted()==True``, otherwise all edges have + weight 1. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + EXAMPLES:: + + sage: from sage.graphs.base.boost_graph import eccentricity_DHV + sage: G = graphs.BullGraph() + sage: eccentricity_DHV(G) + [2.0, 2.0, 2.0, 3.0, 3.0] + + TESTS: + + sage: G = Graph(2) + sage: eccentricity_DHV(G) + [+Infinity, +Infinity] + sage: G = graphs.RandomGNP(20, 0.7) + sage: eccentricity_DHV(G) == G.eccentricity() + True + sage: G = Graph([(0,1,-1)], weighted=True) + sage: eccentricity_DHV(G) + Traceback (most recent call last): + ... + ValueError: graph contains negative edge weights, use Johnson_Boost instead + """ + if g.is_directed(): + raise TypeError("the 'DHV' algorithm only works on undirected graphs") + + cdef v_index n = g.order() + if not n: + return [] + if n == 1: + return [0] + + if weight_function and check_weight: + g._check_weight_function(weight_function) + + if weight_function is not None: + for e in g.edge_iterator(): + if float(weight_function(e)) < 0: + raise ValueError("graph contains negative edge weights, use Johnson_Boost instead") + elif g.weighted(): + for _,_,w in g.edge_iterator(): + if w and float(w) < 0: + raise ValueError("graph contains negative edge weights, use Johnson_Boost instead") + + if vertex_list is None: + vertex_list = g.vertices() + elif not len(vertex_list) == n or not set(vertex_list) == set(g): + raise ValueError("parameter vertex_list is incorrect for this graph") + + # These variables are automatically deleted when the function terminates. + cdef dict v_to_int = {vv: vi for vi, vv in enumerate(vertex_list)} + cdef BoostVecWeightedGraph g_boost + boost_weighted_graph_from_sage_graph(&g_boost, g, v_to_int, weight_function) + + import sys + cdef v_index u, antipode, v + cdef double ecc_u, ecc_antipode, tmp + cdef v_index i, idx + + cdef list active = list(range(n)) + cdef vector[double] ecc_lower_bound + cdef vector[double] ecc_upper_bound + cdef vector[double] distances + + ecc_lower_bound.assign(n, 0) + ecc_upper_bound.assign(n, sys.float_info.max) + + # Algorithm + while active: + # Select vertex with minimum eccentricity in active and update + # eccentricity upper bounds. + # For this, we select u with minimum eccentricity lower bound in active + # if ecc_u == ecc_lb[u], we are done. Otherwise, we update eccentricity + # lower bounds and repeat + + tmp = sys.float_info.max + for i, v in enumerate(active): + if ecc_lower_bound[v] < tmp: + tmp = ecc_lower_bound[v] + idx = i + active[idx], active[-1] = active[-1], active[idx] + u = active.pop() + + # compute distances from u + sig_on() + distances = g_boost.dijkstra_shortest_paths(u).distances + sig_off() + + # Compute eccentricity of u + ecc_u = 0 + for v in range(n): + if ecc_u < distances[v]: + ecc_u = distances[v] + antipode = v + ecc_upper_bound[u] = ecc_u + + if ecc_u == sys.float_info.max: # Disconnected graph + break + + if ecc_u == ecc_lower_bound[u]: + # We found the good vertex. + # Update eccentricity upper bounds and remove from active those + # vertices for which gap is closed + i = 0 + while i < len(active): + v = active[i] + ecc_upper_bound[v] = min(ecc_upper_bound[v], distances[v] + ecc_u) + if ecc_upper_bound[v] == ecc_lower_bound[v]: + active[i] = active[-1] + active.pop() + else: + i += 1 + + else: + # u was not a good choice. + # We use its antipode to update eccentricity lower bounds. + # Observe that this antipode might have already been seen. + for i, v in enumerate(active): + if v == antipode: + active[i] = active[-1] + active.pop() + break + + # Compute distances from antipode + sig_on() + distances = g_boost.dijkstra_shortest_paths(antipode).distances + sig_off() + + # Compute eccentricity of antipode + ecc_antipode = 0 + for v in range(n): + ecc_antipode = max(ecc_antipode, distances[v]) + ecc_upper_bound[antipode] = ecc_antipode + + # Update eccentricity lower bounds and remove from active those + # vertices for which the gap is closed + i = 0 + while i < len(active): + v = active[i] + ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) + if ecc_upper_bound[v] == ecc_lower_bound[v]: + active[i] = active[-1] + active.pop() + else: + i += 1 + + from sage.rings.infinity import Infinity + cdef list eccentricity = [] + for i in range(n): + if ecc_upper_bound[i] != sys.float_info.max: + eccentricity.append(ecc_upper_bound[i]) + else: + eccentricity.append(+Infinity) + + return eccentricity + +cpdef radius_DHV(g, weight_function=None, check_weight=True): + r""" + Return the radius of weighted graph `g`. + + This method computes the radius of undirected graph using the algorithm + given in [Dragan2018]_. + + This method returns Infinity if graph is not connected. + + INPUT: + + - ``g`` -- the input Sage graph + + - ``weight_function`` -- function (default: ``None``); a function that + associates a weight to each edge. If ``None`` (default), the weights of + ``g`` are used, if ``g.weighted()==True``, otherwise all edges have + weight 1. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge. + + EXAMPLES:: + + sage: from sage.graphs.base.boost_graph import radius_DHV + sage: G = Graph([(0,1,1), (1,2,1), (0,2,3)]) + sage: radius_DHV(G) + 1.0 + sage: G = graphs.PathGraph(7) + sage: radius_DHV(G) == G.radius(algorithm='Dijkstra_Boost') + True + + TESTS:: + + sage: G = Graph() + sage: radius_DHV(G) + 0 + sage: G = Graph(1) + sage: radius_DHV(G) + 0 + sage: G = Graph(2) + sage: radius_DHV(G) + +Infinity + sage: G = Graph([(0, 1, 2)],weighted=True) + sage: radius_DHV(G) + 2.0 + sage: G = DiGraph(1) + sage: radius_DHV(G) + Traceback (most recent call last): + ... + TypeError: this method works for undirected graphs only + + """ + if g.is_directed(): + raise TypeError("this method works for undirected graphs only") + + cdef int n = g.order() + if n <= 1: + return 0 + + if weight_function and check_weight: + g._check_weight_function(weight_function) + + if weight_function is not None: + for e in g.edge_iterator(): + if float(weight_function(e)) < 0: + raise ValueError("graphs contains negative weights, use Johnson_Boost instead") + elif g.weighted(): + for _,_,w in g.edge_iterator(): + if w and float(w) < 0: + raise ValueError("graphs contains negative weights, use Johnson_Boost instead") + + # These variables are automatically deleted when the function terminates. + cdef dict v_to_int = {vv: vi for vi, vv in enumerate(g)} + cdef BoostVecWeightedGraph g_boost + boost_weighted_graph_from_sage_graph(&g_boost, g, v_to_int, weight_function) + + import sys + cdef v_index source = 0 + cdef v_index antipode + cdef v_index v + cdef double ecc_source + cdef double UB = sys.float_info.max + cdef double LB = 0 + # For storing distances of all nodes from source + cdef vector[double] distances + # For storing lower bound on eccentricity of nodes + cdef vector[double] ecc_lower_bound + + # Initializing + for i in range(n): + ecc_lower_bound.push_back(0) + + # Algorithm + while LB < UB: + # 1) pick vertex with minimum eccentricity lower bound + # and compute its eccentricity + sig_on() + distances = g_boost.dijkstra_shortest_paths(source).distances + sig_off() + + # Determine the eccentricity of source and its antipode, that is a + # vertex at largest distance from source + ecc_source = 0 + for v in range(n): + if ecc_source < distances[v]: + ecc_source = distances[v] + antipode = v + + if ecc_source == sys.float_info.max: # Disconnected graph + break + + UB = min(UB, ecc_source) # minimum among exact computed eccentricities + if ecc_source == ecc_lower_bound[source]: + # we have found minimum eccentricity vertex and hence the radius + break + + # 2) Compute distances from antipode + sig_on() + distances = g_boost.dijkstra_shortest_paths(antipode).distances + sig_off() + + # 3) Use distances from antipode to improve eccentricity lower bounds. + # We also determine the next source + LB = sys.float_info.max + for v in range(n): + ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) + if LB > ecc_lower_bound[v]: + LB = ecc_lower_bound[v] + source = v # vertex with minimum eccentricity lower bound + + if UB == sys.float_info.max: + from sage.rings.infinity import Infinity + return +Infinity + + return UB + +cpdef diameter_DHV(g, weight_function=None, check_weight=True): + r""" + Return the diameter of weighted graph `g`. + + This method computes the diameter of undirected graph using the + algorithm proposed in [Dragan2018]_. + + This method returns Infinity if graph is not connected. + + INPUT: + + - ``g`` -- the input Sage graph + + - ``weight_function`` -- function (default: ``None``); a function that + associates a weight to each edge. If ``None`` (default), the weights of + ``g`` are used, if ``g.weighted()==True``, otherwise all edges have + weight 1. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + EXAMPLES:: + + sage: from sage.graphs.base.boost_graph import diameter_DHV + sage: G = graphs.ButterflyGraph() + sage: diameter_DHV(G) + 2.0 + + TESTS:: + + sage: G = graphs.RandomBarabasiAlbert(17,6) + sage: diameter_DHV(G) == G.diameter(algorithm = 'Dijkstra_Boost') + True + sage: G = Graph([(0,1,-1)], weighted=True) + sage: diameter_DHV(G) + Traceback (most recent call last): + ... + ValueError: graph contains negative edge weights, use Johnson_Boost instead + """ + if g.is_directed(): + raise TypeError("this method works for undirected graphs only") + + cdef int n = g.order() + if n <= 1: + return 0 + + if weight_function and check_weight: + g._check_weight_function(weight_function) + + if weight_function is not None: + for e in g.edges(sort=False): + if float(weight_function(e)) < 0: + raise ValueError("graph contains negative edge weights, use Johnson_Boost instead") + elif g.weighted(): + for _,_,w in g.edges(sort=False): + if w and float(w) < 0: + raise ValueError("graph contains negative edge weights, use Johnson_Boost instead") + + # These variables are automatically deleted when the function terminates. + cdef dict v_to_int = {vv: vi for vi, vv in enumerate(g)} + cdef BoostVecWeightedGraph g_boost + boost_weighted_graph_from_sage_graph(&g_boost, g, v_to_int, weight_function) + + import sys + cdef v_index u, x, antipode + cdef double ecc_u, ecc_x, ecc_antipode + cdef double LB = 0 + cdef double UB = sys.float_info.max + cdef v_index v + cdef double tmp + cdef size_t i, idx + + cdef list active = list(range(n)) + cdef vector[double] ecc_lower_bound, ecc_upper_bound, distances + + for i in range(n): + ecc_lower_bound.push_back(0) + ecc_upper_bound.push_back(sys.float_info.max) + + # Algorithm + while LB < UB and active: + # 1. Select vertex u with maximum eccentricity upper bound + tmp = 0 + for i, v in enumerate(active): + if ecc_upper_bound[v] > tmp: + tmp = ecc_upper_bound[v] + idx = i + active[idx], active[-1] = active[-1], active[idx] + u = active.pop() + + # Compute the distances from u + sig_on() + distances = g_boost.dijkstra_shortest_paths(u).distances + sig_off() + + # compute the eccentricity of u and update eccentricity lower bounds + ecc_u = 0 + for v in range(n): + ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) + ecc_u = max(ecc_u, distances[v]) + + LB = max(LB, ecc_u) + + if LB == sys.float_info.max: # Disconnected graph + break + + # 2. Select x such that dist(u, x) + ecc[x] == ecc[u]. + # Since we don't know ecc[x], we select x with minimum eccentricity + # lower bound. If ecc[x] == ecc_lb[x], we are done. Otherwise, we + # update eccentricity lower bounds and repeat + while active: + # Select v with minimum eccentricity lower bound + tmp = sys.float_info.max + for i, v in enumerate(active): + if ecc_lower_bound[v] < tmp: + tmp = ecc_lower_bound[v] + idx = i + active[idx], active[-1] = active[-1], active[idx] + x = active.pop() + + # compute the distances from x + sig_on() + distances = g_boost.dijkstra_shortest_paths(x).distances + sig_off() + + # compute the eccentricity of x and its antipode + ecc_x = 0 + for v in range(n): + if distances[v] > ecc_x: + ecc_x = distances[v] + antipode = v + LB = max(LB,ecc_x) + + if ecc_x == ecc_lower_bound[x]: + # We found the good vertex x + # We update eccentricity upper bounds and break + UB = ecc_x + for v in active: + ecc_upper_bound[v] = min(ecc_upper_bound[v], distances[v] + ecc_x) + UB = max(UB, ecc_upper_bound[v]) + break + else: + # x was not a good choice + # We use its antipode to update eccentricity lower bounds. + # Observe that this antipode might have already been seen. + for i, v in enumerate(active): + if v == antipode: + active[i] = active[-1] + active.pop() + break + + # compute the distances from antipode + sig_on() + distances = g_boost.dijkstra_shortest_paths(antipode).distances + sig_off() + + # compute the eccentricity of antipode and update + # eccentricity lower bounds + ecc_antipode = 0 + for v in range(n): + ecc_antipode = max(ecc_antipode, distances[v]) + ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) + LB = max(LB, ecc_antipode) + + if LB == sys.float_info.max: + from sage.rings.infinity import Infinity + return +Infinity + + return LB + +cdef tuple diameter_lower_bound_2Dsweep(BoostVecWeightedDiGraphU g_boost, + BoostVecWeightedDiGraphU rev_g_boost, + v_index source, + str algorithm): + r""" + Return a lower bound on the diameter of `G`. + + This method implements the weighted version of the algorithm proposed + in [Broder2000]_ to compute a lower bound on the diameter of the + weighted digraph `G`. + + If the digraph is not strongly connected, the returned value is infinity. + + Firstly, this method computes forward distances from `source` and selects a + vertex `vf` at maximum forward distance from `source` (i.e. an + antipode). Then, it computes backward eccentricity of `vf`. Observe that the + backward eccentricity of `vf` is at least the forward eccentricity of + `source`. + + Secondly, this method computes backward distances from `source` and selects + a vertex `vb` at maximum backward distance from `source`. Then, it computes + the forward eccentricity of `vb`, which is at least the backward + eccentricity of `source`. + + The lower bound on the diameter is the maximum among the backward + eccentricity of `vf` and forward eccentricity of `vb`. + + The method returns `(LB, s, m, d)`, where `LB` is best found lower bound on + diameter, `s` is a vertex whose forward / backward eccentricity is `LB`, `d` + is a vertex at a distance `LB` from / to `s` , and `m` is a vertex at + distance `LB/2` from / to both `s` and `d`. + + INPUT: + + - ``g_boost`` -- a boost weighted digraph. + + - ``rev_g_boost`` -- a copy of ``g_boost`` with edges reversed. + + - ``source`` -- starting node for forward and backward distance computation. + + - ``algorithm`` -- string; algorithm for computing single source shortest + distances. If ``g_boost`` contains negative edge weights then it will be + ``Bellman-Ford``, otherwise it will be ``Dijkstra_Boost``. + + TESTS:: + + sage: from sage.graphs.base.boost_graph import diameter + sage: G = DiGraph() + sage: diameter(DiGraph(), algorithm='2Dsweep') + 0 + sage: diameter(DiGraph(1), algorithm='2Dsweep') + 0 + sage: diameter(DiGraph(2), algorithm='2Dsweep') + +Infinity + """ + import sys + + cdef int n = g_boost.num_verts() + + if n <= 1: + return (0, 0, 0, 0) + + cdef v_index source_1, source_2, m, s, d, antipode_1, antipode_2, v + cdef double LB_1, LB_2, LB, LB_m + cdef result_distances result_1, result_2 + source_1 = source_2 = source + + # Algorithm + + # 1) Compute forward distances from source_1. + # Get forward eccentricity and antipode (vertex at maximum forward distance) + if algorithm == 'Bellman-Ford': + sig_on() + result_1 = g_boost.bellman_ford_shortest_paths(source_1) + sig_off() + else: + sig_on() + result_1 = g_boost.dijkstra_shortest_paths(source_1) + sig_off() + if not result_1.distances.size(): + raise ValueError("the graph contains a negative cycle") + + LB_1 = -sys.float_info.max + for v in range(n): + if result_1.distances[v] > LB_1: + LB_1 = result_1.distances[v] + antipode_1 = v + + if LB_1 == sys.float_info.max: + return (LB_1, 0, 0, 0) + + + # 2) Compute backward distances from antipode_1. + source_1, antipode_1 = antipode_1, source_1 + if algorithm == 'Bellman-Ford': + sig_on() + result_1 = rev_g_boost.bellman_ford_shortest_paths(source_1) + sig_off() + else: + sig_on() + result_1 = rev_g_boost.dijkstra_shortest_paths(source_1) + sig_off() + if not result_1.distances.size(): + raise ValueError("the graph contains a negative cycle") + + for v in range(n): + if result_1.distances[v] > LB_1: + LB_1 = result_1.distances[v] + antipode_1 = v + + if LB_1 == sys.float_info.max: + return (LB_1, 0, 0, 0) + + + # 3) Compute backward distances from source_2. + # Get backward eccentricity and antipode. + if algorithm == 'Bellman-Ford': + sig_on() + result_2 = rev_g_boost.bellman_ford_shortest_paths(source_2) + sig_off() + else: + sig_on() + result_2 = rev_g_boost.dijkstra_shortest_paths(source_2) + sig_off() + if not result_2.distances.size(): + raise ValueError("the graph contains a negative cycle") + + LB_2 = -sys.float_info.max + for v in range(n): + if result_2.distances[v] > LB_2: + LB_2 = result_2.distances[v] + antipode_2 = v + + if LB_2 == sys.float_info.max: + return (LB_2, 0, 0, 0) + + # 4) Compute forward distances from antipode_2. + source_2, antipode_2 = antipode_2, source_2 + if algorithm == 'Bellman-Ford': + sig_on() + result_2 = g_boost.bellman_ford_shortest_paths(source_2) + sig_off() + else: + sig_on() + result_2 = g_boost.dijkstra_shortest_paths(source_2) + sig_off() + if not result_2.distances.size(): + raise ValueError("the graph contains a negative cycle") + + for v in range(n): + if result_2.distances[v] > LB_2: + LB_2 = result_2.distances[v] + antipode_2 = v + + if LB_2 == sys.float_info.max: + return (LB_2, 0, 0, 0) + + + # 5) Select the best found lower bound as LB with corresponding source s and + # antipode d. Then find a vertex m at a distance LB/2 from/to both s and d. + if LB_1 < LB_2: + LB = LB_2 + s = source_2 + d = antipode_2 + LB_m = LB_2 / 2 + m = d + while result_2.distances[m] > LB_m: + m = result_2.predecessors[m] + else: + LB = LB_1 + s = source_1 + d = antipode_1 + LB_m = LB_1 / 2 + m = d + while result_1.distances[m] > LB_m: + m = result_1.predecessors[m] + + return (LB, s, m, d) + +cdef double diameter_DiFUB(BoostVecWeightedDiGraphU g_boost, + BoostVecWeightedDiGraphU rev_g_boost, + v_index source, + str algorithm) except? -1: + r""" + Return the diameter of a weighted directed graph. + + The ``DiFUB`` (Directed iterative Fringe Upper Bound) algorithm calculates + the exact value of the diameter of an weighted directed graph [CGLM2012]_. + + This algorithm starts from a vertex found through a 2Dsweep call (a directed + version of the 2sweep method). The worst case time complexity of the DiFUB + algorithm is `O(nm)`, but it can be very fast in practice. See the code's + documentation and [CGLM2012]_ for more details. + + If the digraph is not strongly connected, the returned value is infinity. + + INPUT: + + - ``g_boost`` -- a boost weighted digraph. + + - ``rev_g_boost`` -- a copy of ``g_boost`` with edges reversed. + + - ``source`` -- starting node for forward and backward distance computation. + + - ``algorithm`` -- string; algorithm for computing single source shortest + distances. If ``g_boost`` contains negative edge weights then it will be + ``Bellman-Ford``, otherwise it will be ``Dijkstra_Boost``. + + TESTS:: + + sage: from sage.graphs.base.boost_graph import diameter + sage: G = DiGraph() + sage: diameter(DiGraph(), algorithm='DiFUB') + 0 + sage: diameter(DiGraph(1), algorithm='DiFUB') + 0 + sage: diameter(DiGraph(2), algorithm='DiFUB') + +Infinity + """ + cdef v_index n = g_boost.num_verts() + if n <= 1: + return 0 + + import sys + # These variables are automatically deleted when the function terminates. + cdef double LB, LB_1, LB_2, UB + cdef v_index s, m, d, v, tmp + cdef v_index i + cdef vector[double] distances + cdef vector[pair[double, v_index]] order_1, order_2 + + # We select a vertex with low eccentricity using 2Dsweep + LB, s, m, d = diameter_lower_bound_2Dsweep(g_boost, rev_g_boost, + source, algorithm) + + # If the lower bound is a very large number, it means that the digraph is + # not strongly connected and so the diameter is infinite. + if LB == sys.float_info.max: + return LB + + # Compute Forward distances from `m`. + if algorithm == 'Bellman-Ford': + sig_on() + distances = g_boost.bellman_ford_shortest_paths(m).distances + sig_off() + else: + sig_on() + distances = g_boost.dijkstra_shortest_paths(m).distances + sig_off() + if not distances.size(): + raise ValueError("the graph contains a negative cycle") + + # Obtain Forward eccentricity of `m` and store pair of + # forward distances, vertex in order_1 + LB_1 = sys.float_info.min + for v in range(n): + LB_1 = max(LB_1, distances[v]) + order_1.push_back(pair[double,v_index](distances[v], v)) + # Compute Backward distances from `m`. + if algorithm == 'Bellman-Ford': + sig_on() + distances = rev_g_boost.bellman_ford_shortest_paths(m).distances + sig_off() + else: + sig_on() + distances = rev_g_boost.dijkstra_shortest_paths(m).distances + sig_off() + if not distances.size(): + raise ValueError("the graph contains a negative cycle") + + # Obtain Backward eccentricity of `m` and store pair of + # backward distances, vertex in order_2. + LB_2 = sys.float_info.min + for v in range(n): + LB_2 = max(LB_2, distances[v]) + order_2.push_back(pair[double,v_index](distances[v], v)) + + # Now sort order_1 / order_2 in decreasing order of forward / backward + # distances respectively. + # Now order_1 and order_2 will contain order of vertices in which + # further distance computations will be done. + sorted(order_1, reverse=True) + sorted(order_2, reverse=True) + + LB = max(LB, LB_1, LB_2) + if LB == sys.float_info.max: + return LB + + # The algorithm: + # + # The diameter of the digraph is equal to the maximum forward or backward + # eccentricity of a vertex. Let `\[db_1, db_2,..., db_i\]` represents the + # different backward distances from `m` containing at least one vertex at + # that distance. Similarly, let `\[df_1, df_2,..., df_i\]` represents the + # different forward distances from `m` containing at least one vertex at + # that distance. + # + # The algorithm is based on the following two observations: + # + # 1). All the nodes `x` at a backward distance greater than `\[db_i\]` from + # `m` having forward eccentricity greater than `\[2db_{i-1}\]` have a + # corresponding node `y` whose backward eccentricity is greater than or + # equal to the forward eccentricity of `x`, at a forward distance greater + # than `\[db_i\]` from `m`. + # + # 2). All the nodes `x` at a forward distance greater than `\[df_i\]` from + # `m` having backward eccentricity greater than `\[2df_{i-1}\]` have a + # corresponding node `y` whose forward eccentricity is greater than or equal + # to the backward eccentricity of `x`, at a backward distance greater than + # `\[df_i\]` from `m`. + # + # Therefore, we calculate backward / forward eccentricity of all nodes at + # forward / backward distance `\[df_i / db_i\]` from `m` respectively. And + # their maximum is `LB`. If `LB` is greater than `2(next maximum forward / + # backward distance)` then we are done, else we proceed further. + + i = 0 + UB = max(2 * order_1[i].first, 2 * order_2[i].first) + + while LB < UB: + v = order_1[i].second + if algorithm == 'Bellman-Ford': + sig_on() + distances = rev_g_boost.bellman_ford_shortest_paths(v).distances + sig_off() + else: + sig_on() + distances = rev_g_boost.dijkstra_shortest_paths(v).distances + sig_off() + if not distances.size(): + raise ValueError("the graph contains a negative cycle") + + LB_1 = sys.float_info.min + for tmp in range(n): + LB_1 = max(LB_1, distances[tmp]) + + v = order_2[i].second + if algorithm == 'Bellman-Ford': + sig_on() + distances = g_boost.bellman_ford_shortest_paths(v).distances + sig_off() + else: + sig_on() + distances = g_boost.dijkstra_shortest_paths(v).distances + sig_off() + if not distances.size(): + raise ValueError("the graph contains a negative cycle") + + LB_2 = sys.float_info.min + for tmp in range(n): + LB_2 = max(LB_2, distances[tmp]) + + # Update the lower bound + LB = max(LB, LB_1, LB_2) + i += 1 + + if LB == sys.float_info.max or i == n: + break + + # next maximum forward / backward distance + UB = max( 2 * order_1[i].first, 2 * order_2[i].first) + + # Finally return the computed diameter + return LB + +cpdef diameter(G, algorithm=None, source=None, + weight_function=None, check_weight=True): + r""" + Return the diameter of `G`. + + This method returns Infinity if the digraph is not strongly connected. It + can also quickly return a lower bound on the diameter using the ``2Dsweep`` + scheme. + + INPUT: + + - ``G`` -- the input sage digraph. + + - ``algorithm`` -- string (default: ``None``); specifies the algorithm to + use among: + + - ``'2Dsweep'`` -- Computes lower bound on the diameter of an weighted + directed graph using the weighted version of the algorithm proposed in + [Broder2000]_. See the code's documentation for more details. + + - ``'DiFUB'`` -- Computes the diameter of an weighted directed graph + using the weighted version of the algorithm proposed in [CGLM2012]_. + See the code's documentation for more details. + + - ``source`` -- (default: ``None``) vertex from which to start the + computation. If ``source==None``, an arbitrary vertex of the graph is + chosen. Raise an error if the initial vertex is not in `G`. + + - ``weight_function`` -- function (default: ``None``); a function that + associates a weight to each edge. If ``None`` (default), the weights of + ``G`` are used, if ``G.weighted()==True``, otherwise all edges have + weight 1. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge. + + EXAMPLES:: + + sage: from sage.graphs.base.boost_graph import diameter + sage: G = DiGraph([(0, 1, 2), (1, 0, -1)]) + sage: diameter(G, algorithm='DiFUB') + 1.0 + sage: diameter(G, algorithm='DiFUB', weight_function=lambda e:e[2]) + 2.0 + sage: G = DiGraph([(0, 1, -1), (1, 0, 2)]) + sage: diameter(G, algorithm='DiFUB', weight_function=lambda e:e[2]) + 2.0 + + TESTS: + + Diameter of weakly connected digraph is Infinity:: + + sage: G = DiGraph(2) + sage: diameter(G, algorithm='DiFUB') + +Infinity + sage: diameter(G, algorithm='2Dsweep') + +Infinity + + DiGraph containing negative cycle:: + + sage: G = DiGraph([(0,1,-2), (1,0,1)]) + sage: diameter(G, algorithm='2Dsweep', weight_function=lambda e:e[2]) + Traceback (most recent call last): + ... + ValueError: the graph contains a negative cycle + sage: diameter(G, algorithm='DiFUB', weight_function=lambda e:e[2]) + Traceback (most recent call last): + ... + ValueError: the graph contains a negative cycle + """ + import sys + + if not G.is_directed(): + raise TypeError("this method works only for digraphs") + + cdef int n = G.order() + + if n <= 1: + return 0 + + if weight_function and check_weight: + G._check_weight_function(weight_function) + + # Algorithm for single source shortest distance computations. + cdef str algo = 'Dijkstra_Boost' + + # If digraph contains negative edge weight then + # algo is set to `Bellman-Ford` + if weight_function is not None: + for e in G.edges(sort=False): + if float(weight_function(e)) < 0: + algo = 'Bellman-Ford' + break + elif G.weighted(): + for _,_,w in G.edges(sort=False): + if w and float(w) < 0: + algo = 'Bellman-Ford' + break + + if algorithm is None: # default algorithm for diameter computation + algorithm = 'DiFUB' + + if not algorithm in ['2Dsweep', 'DiFUB']: + raise ValueError("unknown algorithm for computing the diameter of directed graph") + + if source is None: + source = next(G.vertex_iterator()) + elif not G.has_vertex(source): + raise ValueError("the specified source is not a vertex of the input Graph") + + # These variables are automatically deleted when the function terminates. + cdef dict v_to_int = {vv: vi for vi, vv in enumerate(G)} + + # boost copy of G + cdef BoostVecWeightedDiGraphU g_boost + # boost copy of G with edges reversed + cdef BoostVecWeightedDiGraphU rev_g_boost + + # Initializing + boost_weighted_graph_from_sage_graph(&g_boost, G, v_to_int, weight_function) + boost_weighted_graph_from_sage_graph(&rev_g_boost, G, v_to_int, weight_function, reverse=True) + + cdef v_index isource = 0 if source is None else v_to_int[source] + cdef double LB + + if algorithm == '2Dsweep': + LB = diameter_lower_bound_2Dsweep(g_boost, rev_g_boost, isource, algo)[0] + else: + LB = diameter_DiFUB(g_boost, rev_g_boost, isource, algo) + + if LB == sys.float_info.max: + from sage.rings.infinity import Infinity + return +Infinity + else: + return LB diff --git a/src/sage/graphs/base/c_graph.pyx b/src/sage/graphs/base/c_graph.pyx index d9eb17d98e9..138153779b2 100644 --- a/src/sage/graphs/base/c_graph.pyx +++ b/src/sage/graphs/base/c_graph.pyx @@ -1,3 +1,4 @@ +# distutils: language = c++ r""" Fast compiled graphs @@ -2438,7 +2439,8 @@ cdef class CGraphBackend(GenericGraphBackend): - ``weight_function`` -- function (default: ``None``); a function that inputs an edge ``(u, v, l)`` and outputs its weight. If ``None``, we - use the edge label ``l`` as a weight. + use the edge label ``l`` as a weight, if ``l`` is not ``None``, else + ``1`` as a weight. - ``distance_flag`` -- boolean (default: ``False``); when set to ``True``, the shortest path distance from ``x`` to ``y`` is returned @@ -2662,7 +2664,8 @@ cdef class CGraphBackend(GenericGraphBackend): - ``weight_function`` -- function (default: ``None``); a function that inputs an edge ``(u, v, l)`` and outputs its weight. If ``None``, we - use the edge label ``l`` as a weight. + use the edge label ``l`` as a weight, if ``l`` is not ``None``, else + ``1`` as a weight. - ``distance_flag`` -- boolean (default: ``False``); when set to ``True``, the shortest path distance from ``x`` to ``y`` is returned @@ -2759,7 +2762,8 @@ cdef class CGraphBackend(GenericGraphBackend): cdef int meeting_vertex = -1 if weight_function is None: - weight_function = lambda e:e[2] + def weight_function(e): + return 1 if e[2] is None else e[2] # As long as the current side (x or y) is not totally explored ... while not pq.empty(): @@ -3033,9 +3037,7 @@ cdef class CGraphBackend(GenericGraphBackend): ....: "Wurzburg": ["Frankfurt","Erfurt","Nurnberg"], ....: "Nurnberg": ["Wurzburg","Stuttgart","Munchen"], ....: "Stuttgart": ["Nurnberg"], "Erfurt": ["Wurzburg"]}) - sage: list(G.depth_first_search("Stuttgart")) # py2 - ['Stuttgart', 'Nurnberg', 'Wurzburg', 'Frankfurt', 'Kassel', 'Munchen', 'Augsburg', 'Karlsruhe', 'Mannheim', 'Erfurt'] - sage: list(G.depth_first_search("Stuttgart")) # py3 + sage: list(G.depth_first_search("Stuttgart")) ['Stuttgart', 'Nurnberg', ...] """ return Search_iterator(self, diff --git a/src/sage/graphs/base/static_sparse_graph.pyx b/src/sage/graphs/base/static_sparse_graph.pyx index 79a32ea523f..0cb3de02cbb 100644 --- a/src/sage/graphs/base/static_sparse_graph.pyx +++ b/src/sage/graphs/base/static_sparse_graph.pyx @@ -1,4 +1,5 @@ # cython: binding=True +# distutils: language = c++ r""" Static Sparse Graphs @@ -682,7 +683,7 @@ def tarjan_strongly_connected_components(G): the lowlink of `v`, that whole subtree is a new SCC. For more information, see the - :wikipedia:`Tarjan's_strongly_connected_components_algorithm`. + :wikipedia:`Tarjan%27s_strongly_connected_components_algorithm`. EXAMPLES:: diff --git a/src/sage/graphs/bipartite_graph.py b/src/sage/graphs/bipartite_graph.py index d136cd4f35b..b63ca4ad02d 100644 --- a/src/sage/graphs/bipartite_graph.py +++ b/src/sage/graphs/bipartite_graph.py @@ -38,9 +38,6 @@ # **************************************************************************** from __future__ import print_function, absolute_import -from six import iteritems -from six.moves import range - from collections import defaultdict from .generic_graph import GenericGraph @@ -464,7 +461,7 @@ def _upgrade_from_graph(self): if not ans: raise ValueError("input graph is not bipartite") cols = defaultdict(set) - for k, v in iteritems(certif): + for k, v in certif.items(): cols[v].add(k) self.left = cols[1] self.right = cols[0] @@ -1548,7 +1545,6 @@ def matching(self, value_only=False, algorithm=None, sage: G = BipartiteGraph(graphs.CubeGraph(3)) sage: for e in G.edges(): ....: G.set_edge_label(e[0], e[1], int(e[0]) + int(e[1])) - ....: sage: G.allow_multiple_edges(True) sage: G.matching(use_edge_labels=True, value_only=True) 444 @@ -1557,14 +1553,14 @@ def matching(self, value_only=False, algorithm=None, sage: B = BipartiteGraph() sage: algorithms = ["Hopcroft-Karp", "Eppstein", "Edmonds", "LP"] - sage: all(B.matching(algorithm=algo) == [] for algo in algorithms) + sage: not any(B.matching(algorithm=algo) for algo in algorithms) True sage: all(B.matching(algorithm=algo, value_only=True) == 0 for algo in algorithms) True sage: B.add_vertex(1, left=True) sage: B.add_vertex(2, left=True) sage: B.add_vertex(3, right=True) - sage: all(B.matching(algorithm=algo) == [] for algo in algorithms) + sage: not any(B.matching(algorithm=algo) for algo in algorithms) True sage: all(B.matching(algorithm=algo, value_only=True) == 0 for algo in algorithms) True @@ -1592,7 +1588,7 @@ def matching(self, value_only=False, algorithm=None, m = networkx.bipartite.hopcroft_karp_matching(h) else: m = networkx.bipartite.eppstein_matching(h) - d.extend((u, v, g.edge_label(u,v)) for u,v in iteritems(m) if v2int[u] < v2int[v]) + d.extend((u, v, g.edge_label(u,v)) for u,v in m.items() if v2int[u] < v2int[v]) if value_only: return Integer(len(d)) @@ -1635,7 +1631,7 @@ def vertex_cover(self, algorithm="Konig", value_only=False, among: - ``"Konig"`` will compute a minimum vertex cover using Konig's - algorithm (:wikipedia:`Kőnig's_theorem_(graph_theory)`) + algorithm (:wikipedia:`Kőnig%27s_theorem_(graph_theory)`) - ``"Cliquer"`` will compute a minimum vertex cover using the Cliquer package diff --git a/src/sage/graphs/bliss.pyx b/src/sage/graphs/bliss.pyx index 7bfa4018643..0448766d1f0 100644 --- a/src/sage/graphs/bliss.pyx +++ b/src/sage/graphs/bliss.pyx @@ -1,3 +1,7 @@ +# distutils: language = c++ +# distutils: libraries = bliss +# sage_setup: distribution = sage-bliss + r""" Interface with bliss: graph (iso/auto)morphism diff --git a/src/sage/graphs/cliquer.pyx b/src/sage/graphs/cliquer.pyx index 3c9e878d21a..5ded77152be 100644 --- a/src/sage/graphs/cliquer.pyx +++ b/src/sage/graphs/cliquer.pyx @@ -39,6 +39,7 @@ cdef extern from "sage/graphs/cliquer/cl.c": cdef int sage_clique_max(graph_t *g, int ** list_of_vertices) cdef int sage_all_clique_max(graph_t *g, int ** list_of_vertices) cdef int sage_clique_number(graph_t *g) + cdef int sage_find_all_clique(graph_t *g,int ** list_of_vertices, int min_size, int max_size) def max_clique(graph): @@ -162,6 +163,127 @@ def all_max_clique(graph): return sorted(b) +def all_cliques(graph, min_size=0, max_size=0): + r""" + Iterator over the cliques in ``graph``. + + A clique is an induced complete subgraph. This method is an iterator over + all the cliques with size in between ``min_size`` and ``max_size``. By + default, this method returns only maximum cliques. Each yielded clique is + represented by a list of vertices. + + .. NOTE:: + + Currently only implemented for undirected graphs. Use + :meth:`~sage.graphs.digraph.DiGraph.to_undirected` to convert a digraph + to an undirected graph. + + INPUT: + + - ``min_size`` -- integer (default: 0); minimum size of reported cliques. + When set to 0 (default), this method searches for maximum cliques. In such + case, parameter ``max_size`` must also be set to 0. + + - ``max_size`` -- integer (default: 0); maximum size of reported cliques. + When set to 0 (default), the maximum size of the cliques is unbounded. + When ``min_size`` is set to 0, this parameter must be set to 0. + + ALGORITHM: + + This function is based on Cliquer [NO2003]_. + + EXAMPLES:: + + sage: G = graphs.CompleteGraph(5) + sage: list(sage.graphs.cliquer.all_cliques(G)) + [[0, 1, 2, 3, 4]] + sage: list(sage.graphs.cliquer.all_cliques(G, 2, 3)) + [[3, 4], + [2, 3], + [2, 3, 4], + [2, 4], + [1, 2], + [1, 2, 3], + [1, 2, 4], + [1, 3], + [1, 3, 4], + [1, 4], + [0, 1], + [0, 1, 2], + [0, 1, 3], + [0, 1, 4], + [0, 2], + [0, 2, 3], + [0, 2, 4], + [0, 3], + [0, 3, 4], + [0, 4]] + sage: G.delete_edge([1,3]) + sage: list(sage.graphs.cliquer.all_cliques(G)) + [[0, 2, 3, 4], [0, 1, 2, 4]] + + TESTS:: + + sage: G = graphs.CompleteGraph(3) + sage: list(sage.graphs.cliquer.all_cliques(G, 2, 3)) + [[1, 2], [0, 1], [0, 1, 2], [0, 2]] + + sage: G = graphs.EmptyGraph() + sage: list(sage.graphs.cliquer.all_cliques(G, 2, 3)) + [] + + sage: G = Graph([(0, 1), (0, 1)], multiedges=True) + sage: list(sage.graphs.cliquer.all_cliques(G, 2, 2)) + [[0, 1]] + + sage: list(sage.graphs.cliquer.all_cliques(G, 0, 2)) + Traceback (most recent call last): + ... + ValueError: max_size > 0 is incompatible with min_size == 0 + + .. TODO:: + + Use the re-entrant functionality of Cliquer [NO2003]_ to avoid storing + all cliques. + """ + if not min_size and max_size > 0: + raise ValueError("max_size > 0 is incompatible with min_size == 0") + if not graph: + return + + cdef int i + cdef list int_to_vertex = list(graph) + cdef dict vertex_to_int = {v: i for i, v in enumerate(int_to_vertex)} + + cdef graph_t* g = graph_new(graph.order()) + for u,v in graph.edge_iterator(labels=None): + GRAPH_ADD_EDGE(g, vertex_to_int[u], vertex_to_int[v]) + + cdef int* list_of_vertices + cdef int size = 0 + cdef list c + try: + try: + sig_on() + size = sage_find_all_clique(g, &list_of_vertices, min_size, max_size) + sig_off() + finally: + graph_free(g) + c = [] + for i in range(size): + if list_of_vertices[i] != -1: + c.append(int_to_vertex[list_of_vertices[i]]) + else: + yield c + c = [] + finally: + if list_of_vertices: + # We free ``list_of_vertices``, + # but only if previous computations weren't interrupted before + # allocating memory for ``list_of_vertices``. + sig_free(list_of_vertices) + + #computes the clique number of a graph def clique_number(graph): diff --git a/src/sage/graphs/cliquer/cl.c b/src/sage/graphs/cliquer/cl.c index be37c3cbaa7..88d35fc168b 100644 --- a/src/sage/graphs/cliquer/cl.c +++ b/src/sage/graphs/cliquer/cl.c @@ -101,6 +101,39 @@ int sage_all_clique_max(graph_t *g,int **list){ return (1+size)*sage_clique_count; } +int sage_find_all_clique(graph_t *g,int **list, int min_size, int max_size){ + sage_reset_global_variables(); + quiet++; + maximal = FALSE; + int i, j, l; + + clique_options *opts = sage_init_clique_opt(); + clique_unweighted_find_all(g, min_size, max_size, maximal, opts); + free(opts); + + int size = 0; + // All the cliques in the output set will be delimited with -1 value. + // The size of the output with delimiters has to be computed now. + for (i = 0; i < sage_clique_count; i++) { + size += set_size(sage_clique_list[i]) + 1; + } + *list = malloc(sizeof(int) * size); + l = 0; + + for (j = 0; j < sage_clique_count; j++) { + for (i = 0; i < SET_MAX_SIZE(sage_clique_list[j]); i++) { + if (SET_CONTAINS(sage_clique_list[j],i)) { + *((*list) + l) = i; + l++; + } + } + set_free(sage_clique_list[j]); + *((*list) +l ) = -1; + l++; + } + return size; +} + int sage_clique_number(graph_t *g){ sage_reset_global_variables(); maximal=TRUE; diff --git a/src/sage/graphs/comparability.pyx b/src/sage/graphs/comparability.pyx index fc19042f23c..1ac9fc93469 100644 --- a/src/sage/graphs/comparability.pyx +++ b/src/sage/graphs/comparability.pyx @@ -242,9 +242,7 @@ def greedy_is_comparability(g, no_certificate = False, equivalence_class = False sage: g = graphs.PetersenGraph() sage: is_comparability(g) False - sage: is_comparability(g, no_certificate=True) # py2 - (False, [0, 4, 9, 6, 1, 0]) - sage: is_comparability(g, no_certificate=True) # py3 + sage: is_comparability(g, no_certificate=True) (False, [2, 1, 0, 4, 3, 2]) But the Bull graph is:: @@ -339,16 +337,12 @@ def greedy_is_comparability_with_certificate(g, certificate = False): The 5-cycle or the Petersen Graph are not transitively orientable:: sage: from sage.graphs.comparability import greedy_is_comparability_with_certificate as is_comparability - sage: is_comparability(graphs.CycleGraph(5), certificate=True) # py2 - (False, [1, 2, 3, 4, 0, 1]) - sage: is_comparability(graphs.CycleGraph(5), certificate=True) # py3 + sage: is_comparability(graphs.CycleGraph(5), certificate=True) (False, [2, 1, 0, 4, 3, 2]) sage: g = graphs.PetersenGraph() sage: is_comparability(g) False - sage: is_comparability(g, certificate=True) # py2 - (False, [0, 4, 9, 6, 1, 0]) - sage: is_comparability(g, certificate=True) # py3 + sage: is_comparability(g, certificate=True) (False, [2, 1, 0, 4, 3, 2]) But the Bull graph is:: @@ -752,7 +746,7 @@ def is_transitive(g, certificate=False): sage: cert = D.is_transitive(certificate=True) sage: D.has_edge(*cert) False - sage: D.shortest_path(*cert) != [] + sage: bool(D.shortest_path(*cert)) True sage: digraphs.RandomDirectedGNP(20,.2).transitive_closure().is_transitive() True diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index 378491294e6..38e1a3a6bd4 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -1804,6 +1804,13 @@ def strong_articulation_points(G): Traceback (most recent call last): ... TypeError: the input must be a Sage DiGraph + + Ticket :trac:`29958` is fixed:: + + sage: D = DiGraph('SA?GA??_??a???@?@OH_?@?I??b??G?AgGGCO??AC????a?????A@????AOCOQ?d??I?') + sage: SAP = strong_articulation_points(D) + sage: set(SAP) == {1, 2, 4, 17, 18} + True """ from sage.graphs.digraph import DiGraph if not isinstance(G, DiGraph): @@ -1822,10 +1829,7 @@ def strong_articulation_points(G): SAP = [] for g in L: n = g.order() - if n <= 1: - continue - if n == 2: - SAP.extend(g.vertex_iterator()) + if n <= 2: continue # 1. Choose arbitrarily a vertex r, and test whether r is a strong @@ -2805,19 +2809,19 @@ cdef class TriconnectivitySPQR: sage: T = tric.get_spqr_tree() sage: G.is_isomorphic(spqr_tree_to_graph(T)) True - sage: tric.print_triconnected_components() # py2 - Polygon: [(6, 7, None), (5, 6, None), (7, 5, 'newVEdge0')] - Bond: [(7, 5, 'newVEdge0'), (5, 7, 'newVEdge1'), (5, 7, None)] - Polygon: [(5, 7, 'newVEdge1'), (4, 7, None), (5, 4, 'newVEdge2')] - Bond: [(4, 5, None), (5, 4, 'newVEdge2'), (5, 4, 'newVEdge3')] - Polygon: [(5, 8, None), (5, 4, 'newVEdge3'), (1, 8, 'newVEdge8'), (1, 4, 'newVEdge9')] - Triconnected: [(8, 9, None), (9, 12, None), (9, 11, None), (8, 11, None), (10, 11, None), (9, 10, None), (10, 12, None), (8, 12, 'newVEdge5')] - Bond: [(8, 12, 'newVEdge5'), (12, 8, 'newVEdge6'), (8, 12, None)] - Polygon: [(1, 12, None), (12, 8, 'newVEdge6'), (1, 8, 'newVEdge7')] - Bond: [(1, 8, None), (1, 8, 'newVEdge7'), (1, 8, 'newVEdge8')] - Bond: [(1, 4, None), (1, 4, 'newVEdge9'), (1, 4, 'newVEdge10')] - Polygon: [(1, 4, 'newVEdge10'), (3, 4, None), (1, 3, 'newVEdge11')] - Triconnected: [(2, 3, None), (2, 13, None), (1, 2, None), (1, 3, 'newVEdge11'), (1, 13, None), (3, 13, None)] + sage: tric.print_triconnected_components() + Triconnected: [(8, 9, None), (9, 12, None), (9, 11, None), (8, 11, None), (10, 11, None), (9, 10, None), (10, 12, None), (8, 12, 'newVEdge0')] + Bond: [(8, 12, None), (8, 12, 'newVEdge0'), (8, 12, 'newVEdge1')] + Polygon: [(6, 7, None), (5, 6, None), (7, 5, 'newVEdge2')] + Bond: [(7, 5, 'newVEdge2'), (5, 7, 'newVEdge3'), (5, 7, None)] + Polygon: [(5, 7, 'newVEdge3'), (4, 7, None), (5, 4, 'newVEdge4')] + Bond: [(5, 4, 'newVEdge4'), (4, 5, 'newVEdge5'), (4, 5, None)] + Polygon: [(4, 5, 'newVEdge5'), (5, 8, None), (1, 4, 'newVEdge9'), (1, 8, 'newVEdge10')] + Triconnected: [(1, 2, None), (2, 13, None), (1, 13, None), (3, 13, None), (2, 3, None), (1, 3, 'newVEdge7')] + Polygon: [(1, 3, 'newVEdge7'), (3, 4, None), (1, 4, 'newVEdge8')] + Bond: [(1, 4, None), (1, 4, 'newVEdge8'), (1, 4, 'newVEdge9')] + Bond: [(1, 8, None), (1, 8, 'newVEdge10'), (1, 8, 'newVEdge11')] + Polygon: [(8, 12, 'newVEdge1'), (1, 8, 'newVEdge11'), (1, 12, None)] An example from [Gut2001]_:: @@ -4022,20 +4026,7 @@ cdef class TriconnectivitySPQR: sage: T = tric.get_spqr_tree() sage: G.is_isomorphic(spqr_tree_to_graph(T)) True - sage: tric.print_triconnected_components() # py2 - Polygon: [(6, 7, None), (5, 6, None), (7, 5, 'newVEdge0')] - Bond: [(7, 5, 'newVEdge0'), (5, 7, 'newVEdge1'), (5, 7, None)] - Polygon: [(5, 7, 'newVEdge1'), (4, 7, None), (5, 4, 'newVEdge2')] - Bond: [(4, 5, None), (5, 4, 'newVEdge2'), (5, 4, 'newVEdge3')] - Polygon: [(5, 8, None), (5, 4, 'newVEdge3'), (1, 8, 'newVEdge8'), (1, 4, 'newVEdge9')] - Triconnected: [(8, 9, None), (9, 12, None), (9, 11, None), (8, 11, None), (10, 11, None), (9, 10, None), (10, 12, None), (8, 12, 'newVEdge5')] - Bond: [(8, 12, 'newVEdge5'), (12, 8, 'newVEdge6'), (8, 12, None)] - Polygon: [(1, 12, None), (12, 8, 'newVEdge6'), (1, 8, 'newVEdge7')] - Bond: [(1, 8, None), (1, 8, 'newVEdge7'), (1, 8, 'newVEdge8')] - Bond: [(1, 4, None), (1, 4, 'newVEdge9'), (1, 4, 'newVEdge10')] - Polygon: [(1, 4, 'newVEdge10'), (3, 4, None), (1, 3, 'newVEdge11')] - Triconnected: [(2, 3, None), (2, 13, None), (1, 2, None), (1, 3, 'newVEdge11'), (1, 13, None), (3, 13, None)] - sage: tric.print_triconnected_components() # py3 + sage: tric.print_triconnected_components() Triconnected: [(8, 9, None), (9, 12, None), (9, 11, None), (8, 11, None), (10, 11, None), (9, 10, None), (10, 12, None), (8, 12, 'newVEdge0')] Bond: [(8, 12, None), (8, 12, 'newVEdge0'), (8, 12, 'newVEdge1')] Polygon: [(6, 7, None), (5, 6, None), (7, 5, 'newVEdge2')] @@ -4067,7 +4058,7 @@ cdef class TriconnectivitySPQR: sage: from sage.graphs.connectivity import TriconnectivitySPQR sage: G = Graph(2) sage: for i in range(3): - ....: G.add_path([0, G.add_vertex(), G.add_vertex(), 1]) + ....: G.add_path([0, G.add_vertex(), G.add_vertex(), 1]) sage: tric = TriconnectivitySPQR(G) sage: tric.get_triconnected_components() [('Polygon', [(4, 5, None), (0, 4, None), (1, 5, None), (1, 0, 'newVEdge1')]), diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index d60fb237008..b65b9aa31fd 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -38,6 +38,19 @@ :meth:`~DiGraph.is_directed` | Since digraph is directed, returns True. :meth:`~DiGraph.dig6_string` | Return the ``dig6`` representation of the digraph as an ASCII string. +**Distances:** + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: | + + :meth:`~DiGraph.eccentricity` | Return the eccentricity of vertex (or vertices) ``v``. + :meth:`~DiGraph.radius` | Return the radius of the DiGraph. + :meth:`~DiGraph.diameter` | Return the diameter of the DiGraph. + :meth:`~DiGraph.center` | Return the set of vertices in the center of the DiGraph. + :meth:`~DiGraph.periphery` | Return the set of vertices in the periphery of the DiGraph. + **Paths and cycles:** .. csv-table:: @@ -109,6 +122,8 @@ :meth:`~DiGraph.flow_polytope` | Compute the flow polytope of a digraph :meth:`~DiGraph.degree_polynomial` | Return the generating polynomial of degrees of vertices in ``self``. + :meth:`~DiGraph.out_branchings` | Return an iterator over the out branchings rooted at given vertex in ``self``. + :meth:`~DiGraph.in_branchings` | Return an iterator over the in branchings rooted at given vertex in ``self``. Methods ------- @@ -162,6 +177,7 @@ from copy import copy from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ +from itertools import product import sage.graphs.generic_graph_pyx as generic_graph_pyx from sage.graphs.generic_graph import GenericGraph from sage.graphs.dot2tex_utils import have_dot2tex @@ -790,7 +806,8 @@ def __init__(self, data=None, pos=None, loops=None, format=None, from_dict_of_lists(self, data, loops=loops, multiedges=multiedges, weighted=weighted) elif format == 'NX': - # adjust for empty dicts instead of None in NetworkX default edge labels + # adjust for empty dicts instead of None in NetworkX default edge + # labels if convert_empty_dict_labels_to_None is None: convert_empty_dict_labels_to_None = (format == 'NX') @@ -1954,8 +1971,8 @@ def reverse_edge(self, u, v=None, label=None, inplace=True, multiedges=None): tempG.delete_edge(u, v, label) tempG.add_edge(v, u, label) - # If user does not want to force digraph to allow parallel edges, we - # delete edge u to v and overwrite v,u with the label of u,v + # If user does not want to force digraph to allow parallel edges, + # we delete edge u to v and overwrite v,u with the label of u,v elif multiedges is False: tempG.delete_edge(u,v,label) tempG.set_edge_label(v,u,label) @@ -2077,6 +2094,575 @@ def reverse_edges(self, edges, inplace=True, multiedges=None): if not inplace: return tempG + ### Distances + + def eccentricity(self, v=None, by_weight=False, algorithm=None, + weight_function=None, check_weight=True, dist_dict=None, + with_labels=False): + """ + Return the eccentricity of vertex (or vertices) ``v``. + + The eccentricity of a vertex is the maximum distance to any other + vertex. + + For more information and examples on how to use input variables, see + :meth:`~GenericGraph.shortest_path_all_pairs`, + :meth:`~GenericGraph.shortest_path_lengths` and + :meth:`~GenericGraph.shortest_paths` + + INPUT: + + - ``v`` - either a single vertex or a list of vertices. If it is not + specified, then it is taken to be all vertices. + + - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge + weights are taken into account; if False, all edges have weight 1 + + - ``algorithm`` -- string (default: ``None``); one of the following + algorithms: + + - ``'BFS'`` - the computation is done through a BFS centered on each + vertex successively. Works only if ``by_weight==False``. + + - ``'Floyd-Warshall-Cython'`` - a Cython implementation of the + Floyd-Warshall algorithm. Works only if ``by_weight==False`` and + ``v is None`` or ``v`` should contain all vertices of ``self``. + + - ``'Floyd-Warshall-Python'`` - a Python implementation of the + Floyd-Warshall algorithm. Works also with weighted graphs, even with + negative weights (but no negative cycle is allowed). However, ``v`` + must be ``None`` or ``v`` should contain all vertices of ``self``. + + - ``'Dijkstra_NetworkX'`` - the Dijkstra algorithm, implemented in + NetworkX. It works with weighted graphs, but no negative weight is + allowed. + + - ``'Dijkstra_Boost'`` - the Dijkstra algorithm, implemented in Boost + (works only with positive weights). + + - ``'Johnson_Boost'`` - the Johnson algorithm, implemented in + Boost (works also with negative weights, if there is no negative + cycle). Works only if ``v is None`` or ``v`` should contain all + vertices of ``self``. + + - ``'From_Dictionary'`` - uses the (already computed) distances, that + are provided by input variable ``dist_dict``. + + - ``None`` (default): Sage chooses the best algorithm: + ``'From_Dictionary'`` if ``dist_dict`` is not None, ``'BFS'`` for + unweighted graphs, ``'Dijkstra_Boost'`` if all weights are + positive, ``'Johnson_Boost'`` otherwise. + + - ``weight_function`` -- function (default: ``None``); a function that + takes as input an edge ``(u, v, l)`` and outputs its weight. If not + ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` + and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l`` + is not ``None``, else ``1`` as a weight. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + - ``dist_dict`` -- a dictionary (default: ``None``); a dict of dicts of + distances (used only if ``algorithm=='From_Dictionary'``) + + - ``with_labels`` -- boolean (default: ``False``); whether to return a + list or a dictionary keyed by vertices. + + EXAMPLES:: + + sage: G = graphs.KrackhardtKiteGraph().to_directed() + sage: G.eccentricity() + [4, 4, 4, 4, 4, 3, 3, 2, 3, 4] + sage: G.vertices() + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + sage: G.eccentricity(7) + 2 + sage: G.eccentricity([7,8,9]) + [2, 3, 4] + sage: G.eccentricity([7,8,9], with_labels=True) == {8: 3, 9: 4, 7: 2} + True + sage: G = DiGraph(3) + sage: G.eccentricity(with_labels=True) + {0: +Infinity, 1: +Infinity, 2: +Infinity} + sage: G = DiGraph({0:[]}) + sage: G.eccentricity(with_labels=True) + {0: 0} + sage: G = DiGraph([(0,1,2), (1,2,3), (2,0,2)]) + sage: G.eccentricity(algorithm = 'BFS') + [2, 2, 2] + sage: G.eccentricity(algorithm = 'Floyd-Warshall-Cython') + [2, 2, 2] + sage: G.eccentricity(by_weight = True, algorithm = 'Dijkstra_NetworkX') + [5, 5, 4] + sage: G.eccentricity(by_weight = True, algorithm = 'Dijkstra_Boost') + [5, 5, 4] + sage: G.eccentricity(by_weight = True, algorithm = 'Johnson_Boost') + [5, 5, 4] + sage: G.eccentricity(by_weight = True, algorithm = 'Floyd-Warshall-Python') + [5, 5, 4] + sage: G.eccentricity(dist_dict = G.shortest_path_all_pairs(by_weight = True)[0]) + [5, 5, 4] + + TESTS: + + A non-implemented algorithm:: + + sage: G.eccentricity(algorithm = 'boh') + Traceback (most recent call last): + ... + ValueError: unknown algorithm "boh" + + An algorithm that does not work with edge weights:: + + sage: G.eccentricity(by_weight = True, algorithm = 'BFS') + Traceback (most recent call last): + ... + ValueError: algorithm 'BFS' does not work with weights + sage: G.eccentricity(by_weight = True, algorithm = 'Floyd-Warshall-Cython') + Traceback (most recent call last): + ... + ValueError: algorithm 'Floyd-Warshall-Cython' does not work with weights + + An algorithm that computes the all-pair-shortest-paths when not all + vertices are needed:: + + sage: G.eccentricity(0, algorithm = 'Floyd-Warshall-Cython') + Traceback (most recent call last): + ... + ValueError: algorithm 'Floyd-Warshall-Cython' works only if all eccentricities are needed + sage: G.eccentricity(0, algorithm = 'Floyd-Warshall-Python') + Traceback (most recent call last): + ... + ValueError: algorithm 'Floyd-Warshall-Python' works only if all eccentricities are needed + sage: G.eccentricity(0, algorithm = 'Johnson_Boost') + Traceback (most recent call last): + ... + ValueError: algorithm 'Johnson_Boost' works only if all eccentricities are needed + """ + if weight_function is not None: + by_weight = True + elif by_weight: + def weight_function(e): + return 1 if e[2] is None else e[2] + + if algorithm is None: + if dist_dict is not None: + algorithm = 'From_Dictionary' + elif not by_weight: + algorithm = 'BFS' + else: + for e in self.edge_iterator(): + try: + if float(weight_function(e)) < 0: + algorithm = 'Johnson_Boost' + break + except (ValueError, TypeError): + raise ValueError("the weight function cannot find the" + " weight of " + str(e)) + if algorithm is None: + algorithm = 'Dijkstra_Boost' + + if v is not None and not isinstance(v, list): + v = [v] + + if v is None or all(u in v for u in self): + if v is None: + v = list(self) + + # If we want to use BFS, we use the Cython routine + if algorithm == 'BFS': + if by_weight: + raise ValueError("algorithm 'BFS' does not work with weights") + from sage.graphs.distances_all_pairs import eccentricity + algo = 'standard' + if with_labels: + return dict(zip(v, eccentricity(self, algorithm=algo, vertex_list=v))) + else: + return eccentricity(self, algorithm=algo) + + if algorithm in ['Floyd-Warshall-Python', 'Floyd-Warshall-Cython', 'Johnson_Boost']: + dist_dict = self.shortest_path_all_pairs(by_weight, algorithm, + weight_function, + check_weight)[0] + algorithm = 'From_Dictionary' + + elif algorithm in ['Floyd-Warshall-Python', 'Floyd-Warshall-Cython', 'Johnson_Boost']: + raise ValueError("algorithm '" + algorithm + "' works only if all" + + " eccentricities are needed") + + ecc = {} + + from sage.rings.infinity import Infinity + + for u in v: + if algorithm == 'From_Dictionary': + length = dist_dict[u] + else: + # If algorithm is wrong, the error is raised by the + # shortest_path_lengths function + length = self.shortest_path_lengths(u, by_weight=by_weight, + algorithm=algorithm, + weight_function=weight_function, + check_weight=check_weight) + + if len(length) != self.num_verts(): + ecc[u] = Infinity + else: + ecc[u] = max(length.values()) + + if with_labels: + return ecc + else: + if len(ecc) == 1: + # return single value + v, = ecc.values() + return v + return [ecc[u] for u in v] + + def radius(self, by_weight=False, algorithm=None, weight_function=None, + check_weight=True): + r""" + Return the radius of the DiGraph. + + The radius is defined to be the minimum eccentricity of any vertex, + where the eccentricity is the maximum distance to any other + vertex. For more information and examples on how to use input variables, + see :meth:`~GenericGraph.shortest_paths` and + :meth:`~DiGraph.eccentricity` + + INPUT: + + - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge + weights are taken into account; if False, all edges have weight 1 + + - ``algorithm`` -- string (default: ``None``); see method + :meth:`eccentricity` for the list of available algorithms + + - ``weight_function`` -- function (default: ``None``); a function that + takes as input an edge ``(u, v, l)`` and outputs its weight. If not + ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` + and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l`` + is not ``None``, else ``1`` as a weight. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + EXAMPLES: + + The more symmetric a DiGraph is, the smaller (diameter - radius) is:: + + sage: G = graphs.BarbellGraph(9, 3).to_directed() + sage: G.radius() + 3 + sage: G.diameter() + 6 + + :: + + sage: G = digraphs.Circuit(9) + sage: G.radius() + 8 + sage: G.diameter() + 8 + + TESTS:: + + sage: G = DiGraph() + sage: G.radius() + Traceback (most recent call last): + ... + ValueError: radius is not defined for the empty DiGraph + """ + if not self.order(): + raise ValueError("radius is not defined for the empty DiGraph") + + if weight_function is not None: + by_weight = True + + if by_weight and not weight_function: + def weight_function(e): + return 1 if e[2] is None else e[2] + + return min(self.eccentricity(v=None, by_weight=by_weight, + weight_function=weight_function, + check_weight=check_weight, + algorithm=algorithm)) + + def diameter(self, by_weight=False, algorithm=None, weight_function=None, + check_weight=True): + r""" + Return the diameter of the DiGraph. + + The diameter is defined to be the maximum distance between two vertices. + It is infinite if the DiGraph is not strongly connected. + + For more information and examples on how to use input variables, see + :meth:`~GenericGraph.shortest_paths` and + :meth:`~DiGraph.eccentricity` + + INPUT: + + - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge + weights are taken into account; if False, all edges have weight 1 + + - ``algorithm`` -- string (default: ``None``); one of the following + algorithms: + + - ``'BFS'``: the computation is done through a BFS centered on each + vertex successively. Works only if ``by_weight==False``. It computes + all the eccentricities and return the maximum value. + + - ``'Floyd-Warshall-Cython'``: a Cython implementation of the + Floyd-Warshall algorithm. Works only if ``by_weight==False``. It + computes all the eccentricities and return the maximum value. + + - ``'Floyd-Warshall-Python'``: a Python implementation of the + Floyd-Warshall algorithm. Works also with weighted graphs, even with + negative weights (but no negative cycle is allowed). It computes all + the eccentricities and return the maximum value. + + - ``'Dijkstra_NetworkX'``: the Dijkstra algorithm, implemented in + NetworkX. It works with weighted graphs, but no negative weight is + allowed. It computes all the eccentricities and return the maximum + value. + + - ``'DiFUB'``, ``'2Dsweep'``: these algorithms are + implemented in :func:`sage.graphs.distances_all_pairs.diameter` and + :func:`sage.graphs.base.boost_graph.diameter`. ``'2Dsweep'`` returns + lower bound on the diameter, while ``'DiFUB'`` returns the exact + computed diameter. They also work with negative weight, if there is + no negative cycle. See the functions documentation for more + information. + + - ``'standard'`` : the standard algorithm is implemented in + :func:`sage.graphs.distances_all_pairs.diameter`. It works only + if ``by_weight==False``. See the function documentation for more + information. It computes all the eccentricities and return the + maximum value. + + - ``'Dijkstra_Boost'``: the Dijkstra algorithm, implemented in Boost + (works only with positive weights). It computes all the + eccentricities and return the maximum value. + + - ``'Johnson_Boost'``: the Johnson algorithm, implemented in + Boost (works also with negative weights, if there is no negative + cycle). It computes all the eccentricities and return the maximum + value. + + - ``None`` (default): Sage chooses the best algorithm: ``'DiFUB'``. + + - ``weight_function`` -- function (default: ``None``); a function that + takes as input an edge ``(u, v, l)`` and outputs its weight. If not + ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` + and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l`` + is not ``None``, else ``1`` as weight. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + EXAMPLES:: + + sage: G = digraphs.DeBruijn(5,4) + sage: G.diameter() + 4 + sage: G = digraphs.GeneralizedDeBruijn(9, 3) + sage: G.diameter() + 2 + + TESTS:: + + sage: G = graphs.RandomGNP(40, 0.4).to_directed() + sage: d1 = G.diameter(algorithm='DiFUB', by_weight=True) + sage: d2 = max(G.eccentricity(algorithm='Dijkstra_Boost', by_weight=True)) + sage: d1 == d2 + True + sage: G = digraphs.Path(5) + sage: G.diameter(algorithm = 'DiFUB') + +Infinity + sage: G = DiGraph([(1,2,4), (2,1,7)]) + sage: G.diameter(algorithm='2Dsweep', by_weight=True) + 7.0 + sage: G.delete_edge(2,1,7); G.add_edge(2,1,-5); + sage: G.diameter(algorithm='2Dsweep', by_weight=True) + Traceback (most recent call last): + ... + ValueError: the graph contains a negative cycle + sage: G = DiGraph() + sage: G.diameter() + Traceback (most recent call last): + ... + ValueError: diameter is not defined for the empty DiGraph + """ + if not self.order(): + raise ValueError("diameter is not defined for the empty DiGraph") + + if weight_function is not None: + by_weight = True + + if by_weight and not weight_function: + def weight_function(e): + return 1 if e[2] is None else e[2] + + if algorithm is None: + algorithm = 'DiFUB' + elif algorithm == 'BFS': + algorithm = 'standard' + + if algorithm in ['2Dsweep', 'DiFUB']: + if not by_weight: + from sage.graphs.distances_all_pairs import diameter + return diameter(self, algorithm=algorithm) + else: + from sage.graphs.base.boost_graph import diameter + return diameter(self, algorithm=algorithm, + weight_function=weight_function, + check_weight=check_weight) + + if algorithm == 'standard': + if by_weight: + raise ValueError("algorithm '" + algorithm + "' does not work" + + " on weighted DiGraphs") + from sage.graphs.distances_all_pairs import diameter + return diameter(self, algorithm=algorithm) + + return max(self.eccentricity(v=None, by_weight=by_weight, + weight_function=weight_function, + check_weight=check_weight, + algorithm=algorithm)) + + def center(self, by_weight=False, algorithm=None, weight_function=None, + check_weight=True): + r""" + Return the set of vertices in the center of the DiGraph. + + The center is the set of vertices whose eccentricity is equal to the + radius of the DiGraph, i.e., achieving the minimum eccentricity. + + For more information and examples on how to use input variables, + see :meth:`~GenericGraph.shortest_paths` and + :meth:`~DiGraph.eccentricity` + + INPUT: + + - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge + weights are taken into account; if False, all edges have weight 1 + + - ``algorithm`` -- string (default: ``None``); see method + :meth:`eccentricity` for the list of available algorithms + + - ``weight_function`` -- function (default: ``None``); a function that + takes as input an edge ``(u, v, l)`` and outputs its weight. If not + ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` + and ``by_weight`` is ``True``, we use the edge label ``l`` as a + weight, if ``l`` is not ``None``, else ``1`` as a weight. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + EXAMPLES: + + Every vertex is a center in a Circuit-DiGraph:: + + sage: G = digraphs.Circuit(9) + sage: G.center() + [0, 1, 2, 3, 4, 5, 6, 7, 8] + + Center can be the whole graph:: + + sage: G.subgraph(G.center()) == G + True + + Some other graphs:: + + sage: G = digraphs.Path(5) + sage: G.center() + [0] + sage: G = DiGraph([(0,1,2), (1,2,3), (2,0,2)]) + sage: G.center(by_weight=True) + [2] + + TESTS:: + + sage: G = DiGraph() + sage: G.center() + [] + sage: G = DiGraph(3) + sage: G.center() + [0, 1, 2] + """ + ecc = self.eccentricity(v=list(self), by_weight=by_weight, + weight_function=weight_function, + algorithm=algorithm, + check_weight=check_weight, + with_labels=True) + try: + r = min(ecc.values()) + except Exception: + return [] + return [v for v in self if ecc[v] == r] + + def periphery(self, by_weight=False, algorithm=None, weight_function=None, + check_weight=True): + r""" + Return the set of vertices in the periphery of the DiGraph. + + The periphery is the set of vertices whose eccentricity is equal to the + diameter of the DiGraph, i.e., achieving the maximum eccentricity. + + For more information and examples on how to use input variables, + see :meth:`~GenericGraph.shortest_paths` and + :meth:`~DiGraph.eccentricity` + + INPUT: + + - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge + weights are taken into account; if False, all edges have weight 1 + + - ``algorithm`` -- string (default: ``None``); see method + :meth:`eccentricity` for the list of available algorithms + + - ``weight_function`` -- function (default: ``None``); a function that + takes as input an edge ``(u, v, l)`` and outputs its weight. If not + ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` + and ``by_weight`` is ``True``, we use the edge label ``l`` as a + weight, if ``l`` is not ``None``, else ``1`` as a weight. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + EXAMPLES:: + + sage: G = graphs.DiamondGraph().to_directed() + sage: G.periphery() + [0, 3] + sage: P = digraphs.Path(5) + sage: P.periphery() + [1, 2, 3, 4] + sage: G = digraphs.Complete(5) + sage: G.subgraph(G.periphery()) == G + True + + TESTS:: + + sage: G = DiGraph() + sage: G.periphery() + [] + sage: G.add_vertex() + 0 + sage: G.periphery() + [0] + """ + ecc = self.eccentricity(v=list(self), by_weight=by_weight, + weight_function=weight_function, + algorithm=algorithm, + check_weight=check_weight, + with_labels=True) + try: + d = max(ecc.values()) + except Exception: + return [] + return [v for v in self if ecc[v] == d] + ### Paths and cycles iterators def _all_cycles_iterator_vertex(self, vertex, starting_vertices=None, simple=False, @@ -2191,12 +2777,12 @@ def _all_cycles_iterator_vertex(self, vertex, starting_vertices=None, simple=Fal if len(path) > 1 and path[0] == path[-1]: yield path # Makes sure that the current cycle is not too long - # Also if a cycle has been encountered and only simple cycles are allowed, - # Then it discards the current path + # Also if a cycle has been encountered and only simple cycles are + # allowed, Then it discards the current path if len(path) <= max_length and (not simple or path.count(path[-1]) == 1): for neighbor in h.neighbor_out_iterator(path[-1]): - # If cycles are not rooted, makes sure to keep only the minimum - # cycle according to the lexicographic order + # If cycles are not rooted, makes sure to keep only the + # minimum cycle according to the lexicographic order if rooted or neighbor not in starting_vertices or path[0] <= neighbor: queue.append(path + [neighbor]) @@ -2716,7 +3302,7 @@ def layout_acyclic_dummy(self, heights=None, rankdir='up', **options): sage: H = DiGraph({0: [1, 2], 1: [3], 2: [3], 3: [], 5: [1, 6], 6: [2, 3]}) sage: H.layout_acyclic_dummy() - {0: [1.00..., 0], 1: [1.00..., 1], 2: [1.51..., 2], 3: [1.50..., 3], 5: [2.01..., 0], 6: [2.00..., 1]} + {0: [1.0..., 0], 1: [1.0..., 1], 2: [1.5..., 2], 3: [1.5..., 3], 5: [2.0..., 0], 6: [2.0..., 1]} sage: H = DiGraph({0: [1]}) sage: H.layout_acyclic_dummy(rankdir='up') @@ -3232,6 +3818,441 @@ def _girth_bfs(self, odd=False, certificate=False): return (best, list(reversed(cycles[u])) + cycles[v]) return best + def out_branchings(self, source, spanning=True): + r""" + Return an iterator over the out branchings rooted at given vertex in + ``self``. + + An out-branching is a directed tree rooted at ``source`` whose arcs are + directed from source to leaves. An out-branching is spanning if it + contains all vertices of the digraph. + + If no spanning out branching rooted at ``source`` exist, raises + ValueError or return non spanning out branching rooted at ``source``, + depending on the value of ``spanning``. + + INPUT: + + - ``source`` -- vertex used as the source for all out branchings. + + - ``spanning`` -- boolean (default: ``True``); if ``False`` return + maximum out branching from ``source``. Otherwise, return spanning out + branching if exists. + + OUTPUT: + + An iterator over the out branchings rooted in the given source. + + .. SEEALSO:: + + - :meth:`~sage.graphs.digraph.DiGraph.in_branchings` + -- iterator over in-branchings rooted at given vertex. + - :meth:`~sage.graphs.graph.Graph.spanning_trees` + -- returns all spanning trees. + - :meth:`~sage.graphs.generic_graph.GenericGraph.spanning_trees_count` + -- counts the number of spanning trees. + + ALGORITHM: + + Recursively computes all out branchings. + + At each step: + + 0. clean the graph (see below) + 1. pick an edge e out of source + 2. find all out branchings that do not contain e by first + removing it + 3. find all out branchings that do contain e by first + merging the end vertices of e + + Cleaning the graph implies to remove loops and replace multiedges by a + single one with an appropriate label since these lead to similar steps + of computation. + + EXAMPLES: + + A bidirectional 4-cycle:: + + sage: G = DiGraph({1:[2,3], 2:[1,4], 3:[1,4], 4:[2,3]}, format='dict_of_lists') + sage: list(G.out_branchings(1)) + [Digraph on 4 vertices, + Digraph on 4 vertices, + Digraph on 4 vertices, + Digraph on 4 vertices] + + With the Petersen graph turned into a symmetric directed graph:: + + sage: G = graphs.PetersenGraph().to_directed() + sage: len(list(G.out_branchings(0))) + 2000 + + With a non connected ``DiGraph`` and ``spanning = True``:: + + sage: G = graphs.PetersenGraph().to_directed() + graphs.PetersenGraph().to_directed() + sage: G.out_branchings(0, spanning=True) + Traceback (most recent call last): + ... + ValueError: no spanning out branching from vertex (0) exist + + With a non connected ``DiGraph`` and ``spanning = False``:: + + sage: g=DiGraph([(0,1), (0,1), (1,2), (3,4)],multiedges=True) + sage: list(g.out_branchings(0, spanning=False)) + [Digraph on 3 vertices, Digraph on 3 vertices] + + With multiedges:: + + sage: G = DiGraph({0:[1,1,1], 1:[2,2]}, format='dict_of_lists', multiedges=True) + sage: len(list(G.out_branchings(0))) + 6 + + With a DiGraph already being a spanning out branching:: + + sage: G = DiGraph({0:[1,2], 1:[3,4], 2:[5], 3:[], 4:[], 5:[]}, format='dict_of_lists') + sage: next(G.out_branchings(0)) == G + True + + TESTS: + + The empty ``DiGraph``:: + + sage: G = DiGraph() + sage: G.out_branchings(0) + Traceback (most recent call last): + ... + ValueError: vertex (0) is not a vertex of the digraph + + sage: edges = [(0,0,'x'), (0,0,'y')] + sage: G = DiGraph(edges, multiedges=True, loops=True, weighted=True) + sage: list(G.out_branchings(0)) + [Digraph on 1 vertex] + + sage: edges = [(0,1,'x'), (0,1,'y'), (1,2,'z'), (2,0,'w')] + sage: G = DiGraph(edges, multiedges=True, loops=True, weighted=True) + sage: len(list(G.out_branchings(0))) + 2 + """ + def _rec_out_branchings(depth): + r""" + The recursive function used to enumerate out branchings. + + This function makes use of the following to keep track of partial + out branchings: + list_edges -- list of edges in self. + list_merged_edges -- list of edges that are currently merged + graph -- a copy of self where edges have an appropriate label + """ + if not depth: + # We have enough merged edges to form a out_branching + # We iterate over the lists of labels in list_merged_edges and + # yield the corresponding out_branchings + for indexes in product(*list_merged_edges): + yield DiGraph([list_edges[index] for index in indexes], + format='list_of_edges', pos=self.get_pos()) + + # 1) Clean the graph + # delete loops on source if any + D.delete_edges(D.incoming_edge_iterator(source)) + + # merge multi-edges if any by concatenating their labels + if D.has_multiple_edges(): + merged_multiple_edges = {} + for u, v, l in D.multiple_edges(): + D.delete_edge(u, v, l) + if (u, v) not in merged_multiple_edges: + merged_multiple_edges[(u, v)] = l + else: + merged_multiple_edges[(u, v)] += l + D.add_edges([(u, v, l) for (u, v),l in merged_multiple_edges.items()]) + + # 2) Pick an edge e outgoing from the source + try: + s, x, l = next(D.outgoing_edge_iterator(source)) + except: + return + # 3) Find all out_branchings that do not contain e + # by first removing it + D.delete_edge(s, x, l) + if len(list(D.depth_first_search(source))) == depth + 1: + for out_branch in _rec_out_branchings(depth): + yield out_branch + D.add_edge(s, x, l) + + # 4) Find all out_branchings that do contain e by merging + # the end vertices of e + # store different edges to unmerged the end vertices of e + saved_edges = D.outgoing_edges(source) + saved_edges.remove((s, x, l)) + saved_edges += D.outgoing_edges(x) + saved_edges += D.incoming_edges(x) + + D.merge_vertices((source, x)) + + list_merged_edges.add(l) + + for out_branch in _rec_out_branchings(depth - 1): + yield out_branch + + list_merged_edges.remove(l) + + # unmerge the end vertices of e + D.delete_vertex(source) + D.add_edges(saved_edges) + + def _singleton_out_branching(): + r""" + Returns a DiGraph containing only ``source`` and no edges. + """ + D = DiGraph() + D.add_vertex(source) + yield D + + if not self.has_vertex(source): + raise ValueError("vertex ({0}) is not a vertex of the digraph".format(source)) + + # check if self.order == 1 + if self.order() == 1: + return _singleton_out_branching() + + # check if the source can access to every other vertex + if spanning: + depth = self.order() - 1 + if len(list(self.depth_first_search(source))) < self.order(): + raise ValueError("no spanning out branching from vertex ({0}) exist".format(source)) + else: + depth = len(list(self.depth_first_search(source))) - 1 + # if vertex is isolated + if not depth: + return _singleton_out_branching() + + # We build a copy of self in which each edge has a distinct label. + # On the way, we remove loops and edges incoming to source. + D = DiGraph(multiedges=True, loops=True) + list_edges = list(self.edges(sort=False)) + for i, (u, v, _) in enumerate(list_edges): + if u != v and v != source: + D.add_edge(u, v, (i,)) + list_merged_edges = set() + return _rec_out_branchings(depth) + + def in_branchings(self, source, spanning=True): + r""" + Return an iterator over the in branchings rooted at given vertex in + ``self``. + + An in-branching is a directed tree rooted at ``source`` whose arcs are + directed to source from leaves. An in-branching is spanning if it + contains all vertices of the digraph. + + If no spanning in branching rooted at ``source`` exist, raises + ValueError or return non spanning in branching rooted at ``source``, + depending on the value of ``spanning``. + + INPUT: + + - ``source`` -- vertex used as the source for all in branchings. + + - ``spanning`` -- boolean (default: ``True``); if ``False`` return + maximum in branching to ``source``. Otherwise, return spanning in + branching if exists. + + OUTPUT: + + An iterator over the in branchings rooted in the given source. + + .. SEEALSO:: + + - :meth:`~sage.graphs.digraph.DiGraph.out_branchings` + -- iterator over out-branchings rooted at given vertex. + - :meth:`~sage.graphs.graph.Graph.spanning_trees` + -- returns all spanning trees. + - :meth:`~sage.graphs.generic_graph.GenericGraph.spanning_trees_count` + -- counts the number of spanning trees. + + ALGORITHM: + + Recursively computes all in branchings. + + At each step: + + 0. clean the graph (see below) + 1. pick an edge e incoming to source + 2. find all in branchings that do not contain e by first + removing it + 3. find all in branchings that do contain e by first + merging the end vertices of e + + Cleaning the graph implies to remove loops and replace multiedges by a + single one with an appropriate label since these lead to similar steps + of computation. + + EXAMPLES: + + A bidirectional 4-cycle:: + + sage: G = DiGraph({1:[2,3], 2:[1,4], 3:[1,4], 4:[2,3]}, format='dict_of_lists') + sage: list(G.in_branchings(1)) + [Digraph on 4 vertices, + Digraph on 4 vertices, + Digraph on 4 vertices, + Digraph on 4 vertices] + + With the Petersen graph turned into a symmetric directed graph:: + + sage: G = graphs.PetersenGraph().to_directed() + sage: len(list(G.in_branchings(0))) + 2000 + + With a non connected ``DiGraph`` and ``spanning = True``:: + + sage: G = graphs.PetersenGraph().to_directed() + graphs.PetersenGraph().to_directed() + sage: G.in_branchings(0) + Traceback (most recent call last): + ... + ValueError: no spanning in branching to vertex (0) exist + + With a non connected ``DiGraph`` and ``spanning = False``:: + + sage: g=DiGraph([(1,0), (1,0), (2,1), (3,4)],multiedges=True) + sage: list(g.in_branchings(0,spanning=False)) + [Digraph on 3 vertices, Digraph on 3 vertices] + + With multiedges:: + + sage: G = DiGraph({0:[1,1,1], 1:[2,2]}, format='dict_of_lists', multiedges=True) + sage: len(list(G.in_branchings(2))) + 6 + + With a DiGraph already being a spanning in branching:: + + sage: G = DiGraph({0:[], 1:[0], 2:[0], 3:[1], 4:[1], 5:[2]}, format='dict_of_lists') + sage: next(G.in_branchings(0)) == G + True + + TESTS: + + The empty ``DiGraph``:: + + sage: G = DiGraph() + sage: G.in_branchings(0) + Traceback (most recent call last): + ... + ValueError: vertex (0) is not a vertex of the digraph + + sage: edges = [(0,0,'x'), (0,0,'y')] + sage: G = DiGraph(edges, multiedges=True, loops=True, weighted=True) + sage: list(G.in_branchings(0)) + [Digraph on 1 vertex] + + sage: edges = [(0,1,'x'), (0,1,'y'), (1,2,'z'), (2,0,'w')] + sage: G = DiGraph(edges, multiedges=True, loops=True, weighted=True) + sage: len(list(G.in_branchings(0))) + 1 + """ + def _rec_in_branchings(depth): + r""" + The recursive function used to enumerate in branchings. + + This function makes use of the following to keep track of partial in + branchings: + list_edges -- list of edges in self. + list_merged_edges -- list of edges that are currently merged + graph -- a copy of self where edges have an appropriate label + """ + if not depth: + # We have enough merged edges to form a in_branching + # We iterate over the lists of labels in list_merged_edges and + # yield the corresponding in_branchings + for indexes in product(*list_merged_edges): + yield DiGraph([list_edges[index] for index in indexes], + format='list_of_edges', pos=self.get_pos()) + + # 1) Clean the graph + # delete loops on source if any + D.delete_edges(D.outgoing_edge_iterator(source)) + + # merge multi-edges if any by concatenating their labels + if D.has_multiple_edges(): + merged_multiple_edges = {} + for u, v, l in D.multiple_edges(): + D.delete_edge(u, v, l) + if (u, v) not in merged_multiple_edges: + merged_multiple_edges[(u, v)] = l + else: + merged_multiple_edges[(u, v)] += l + D.add_edges([(u, v, l) for (u, v),l in merged_multiple_edges.items()]) + + # 2) Pick an edge e incoming to the source + try: + x, s, l = next(D.incoming_edge_iterator(source)) + except: + return + # 3) Find all in_branchings that do not contain e + # by first removing it + D.delete_edge(x, s, l) + if len(list(D.depth_first_search(source, neighbors=D.neighbor_in_iterator))) == depth + 1: + for in_branch in _rec_in_branchings(depth): + yield in_branch + D.add_edge(x, s, l) + + # 4) Find all in_branchings that do contain e by merging + # the end vertices of e + # store different edges to unmerged the end vertices of e + saved_edges = D.incoming_edges(source) + saved_edges.remove((x, s, l)) + saved_edges += D.outgoing_edges(x) + saved_edges += D.incoming_edges(x) + + D.merge_vertices((source, x)) + + list_merged_edges.add(l) + + for in_branch in _rec_in_branchings(depth - 1): + yield in_branch + + list_merged_edges.remove(l) + + # unmerge the end vertices of e + D.delete_vertex(source) + D.add_edges(saved_edges) + + def _singleton_in_branching(): + r""" + Returns a DiGraph containing only ``source`` and no edges. + """ + D = DiGraph() + D.add_vertex(source) + yield D + + if not self.has_vertex(source): + raise ValueError("vertex ({0}) is not a vertex of the digraph".format(source)) + + # check if self.order == 1 + if self.order() == 1: + return _singleton_in_branching() + + # check if the source can access to every other vertex + if spanning: + depth = self.order() - 1 + if len(list(self.depth_first_search(source, neighbors=self.neighbor_in_iterator))) < self.order(): + raise ValueError("no spanning in branching to vertex ({0}) exist".format(source)) + else: + depth = len(list(self.depth_first_search(source, neighbors=self.neighbor_in_iterator))) - 1 + # if vertex is isolated + if not depth: + return _singleton_in_branching() + + # We build a copy of self in which each edge has a distinct label. + # On the way, we remove loops and edges incoming to source. + D = DiGraph(multiedges=True, loops=True) + list_edges = list(self.edges(sort=False)) + for i, (u, v, _) in enumerate(list_edges): + if u != v and u != source: + D.add_edge(u, v, (i,)) + list_merged_edges = set() + return _rec_in_branchings(depth) + + # Aliases to functions defined in other modules from sage.graphs.comparability import is_transitive from sage.graphs.base.static_sparse_graph import tarjan_strongly_connected_components as strongly_connected_components diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index 89492161f5b..43e311d2158 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -62,8 +62,6 @@ # http://www.gnu.org/licenses/ ################################################################################ from __future__ import print_function, division -from six.moves import range -from six import PY2 from sage.cpython.string import bytes_to_str from sage.env import SAGE_NAUTY_BINS_PREFIX as nautyprefix @@ -579,14 +577,14 @@ def nauty_directg(self, graphs, options="", debug=False): - ``options`` (str) -- a string passed to directg as if it was run at a system command line. Available options from directg --help:: - -e# | -e#:# specify a value or range of the total number of arcs - -o orient each edge in only one direction, never both - -f# Use only the subgroup that fixes the first # vertices setwise - -V only output graphs with nontrivial groups (including exchange of - isolated vertices). The -f option is respected. - -s#/# Make only a fraction of the orientations: The first integer is - the part number (first is 0) and the second is the number of - parts. Splitting is done per input graph independently. + -e | -e: specify a value or range of the total number of arcs + -o orient each edge in only one direction, never both + -f Use only the subgroup that fixes the first vertices setwise + -V only output graphs with nontrivial groups (including exchange of + isolated vertices). The -f option is respected. + -s/ Make only a fraction of the orientations: The first integer is + the part number (first is 0) and the second is the number of + parts. Splitting is done per input graph independently. - ``debug`` (boolean) -- default: ``False`` - if ``True`` directg standard error and standard output are displayed. @@ -636,11 +634,6 @@ def nauty_directg(self, graphs, options="", debug=False): if '-q' not in options: options += ' -q' - if PY2: - enc_kwargs = {} - else: - enc_kwargs = {'encoding': 'latin-1'} - # Build directg input (graphs6 format) input = ''.join(g.graph6_string()+'\n' for g in graphs) sub = subprocess.Popen(nautyprefix+'directg {0}'.format(options), @@ -648,7 +641,7 @@ def nauty_directg(self, graphs, options="", debug=False): stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, - **enc_kwargs) + encoding='latin-1') out, err = sub.communicate(input=input) if debug: @@ -1181,9 +1174,14 @@ def RandomDirectedGN(self, n, kernel=lambda x:x, seed=None): EXAMPLES:: sage: D = digraphs.RandomDirectedGN(25) - sage: D.edges(labels=False) - [(1, 0), (2, 0), (3, 2), (4, 2), (5, 4), (6, 3), (7, 0), (8, 4), (9, 4), (10, 3), (11, 4), (12, 4), (13, 3), (14, 4), (15, 4), (16, 0), (17, 2), (18, 4), (19, 6), (20, 14), (21, 4), (22, 0), (23, 22), (24, 14)] # 32-bit - [(1, 0), (2, 1), (3, 0), (4, 2), (5, 0), (6, 2), (7, 3), (8, 2), (9, 3), (10, 4), (11, 5), (12, 9), (13, 2), (14, 2), (15, 5), (16, 2), (17, 15), (18, 1), (19, 5), (20, 2), (21, 5), (22, 1), (23, 5), (24, 14)] # 64-bit + sage: D.num_verts() + 25 + sage: D.num_edges() + 24 + sage: D.is_connected() + True + sage: D.parent() is DiGraph + True sage: D.show() # long time """ if seed is None: @@ -1247,12 +1245,11 @@ def RandomDirectedGNP(self, n, p, loops=False, seed=None): EXAMPLES:: - sage: set_random_seed(0) sage: D = digraphs.RandomDirectedGNP(10, .2) sage: D.num_verts() 10 - sage: D.edges(labels=False) - [(1, 0), (1, 2), (3, 6), (3, 7), (4, 5), (4, 7), (4, 8), (5, 2), (6, 0), (7, 2), (8, 1), (8, 9), (9, 4)] + sage: D.parent() is DiGraph + True """ from sage.graphs.graph_generators_pyx import RandomGNP if 0.0 > p or 1.0 < p: @@ -1283,8 +1280,8 @@ def RandomDirectedGNM(self, n, m, loops=False): sage: D = digraphs.RandomDirectedGNM(10, 5) sage: D.num_verts() 10 - sage: D.edges(labels=False) - [(0, 3), (1, 5), (5, 1), (7, 0), (8, 5)] + sage: D.num_edges() + 5 With loops:: diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 463db9c7a2c..d5b8171756d 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -134,6 +134,7 @@ from sage.ext.memory_allocator cimport MemoryAllocator from sage.graphs.base.static_sparse_graph cimport (short_digraph, init_short_digraph, + init_reverse, free_short_digraph, out_degree, simple_BFS) @@ -800,6 +801,138 @@ cdef uint32_t * c_eccentricity_bounding(G, vertex_list=None) except NULL: return LB +cdef uint32_t * c_eccentricity_DHV(G, vertex_list=None): + r""" + Return the vector of eccentricities using the algorithm of [Dragan2018]_. + + The array returned is of length `n`, and by default its `i`-th component is + the eccentricity of the `i`-th vertex in ``G.vertices()``. + + Optional parameter ``vertex_list`` is a list of `n` vertices specifying a + mapping from `(0, \ldots, n-1)` to vertex labels in `G`. When set, + ``ecc[i]`` is the eccentricity of vertex ``vertex_list[i]``. + + The algorithm proposed in [Dragan2018]_ is an improvement of the algorithm + proposed in [TK2013]_. It is also based on the observation that for all + nodes `v,w\in V`, we have `\max(ecc[v]-d(v,w), d(v,w))\leq ecc[w] \leq + ecc[v] + d(v,w)`. Also the algorithms iteratively improves upper and lower + bounds on the eccentricity of each vertex until no further improvements can + be done. The difference with [TK2013]_ is in the order in which improvements + are done. + + EXAMPLES:: + + sage: from sage.graphs.distances_all_pairs import eccentricity + sage: G = graphs.PathGraph(5) + sage: eccentricity(G, algorithm='DHV') + [4, 3, 2, 3, 4] + + TESTS: + + sage: G = graphs.RandomBarabasiAlbert(50, 2) + sage: eccentricity(G, algorithm='bounds') == eccentricity(G, algorithm='DHV') + True + """ + if G.is_directed(): + raise ValueError("the 'DHV' algorithm only works on undirected graphs") + + cdef uint32_t n = G.order() + if not n: + return NULL + + cdef short_digraph sd + init_short_digraph(sd, G, edge_labelled=False, vertex_list=vertex_list) + + cdef MemoryAllocator mem = MemoryAllocator() + cdef uint32_t * distances = mem.malloc(3 * n * sizeof(uint32_t)) + # For storing upper bounds on eccentricity of nodes + cdef uint32_t * ecc_upper_bound = sig_calloc(n, sizeof(uint32_t)) + if not distances or not ecc_upper_bound: + sig_free(ecc_upper_bound) + free_short_digraph(sd) + raise MemoryError() + + cdef uint32_t * waiting_list = distances + n + # For storing lower bounds on eccentricity of nodes + cdef uint32_t * ecc_lower_bound = distances + 2 * n + memset(ecc_upper_bound, -1, n * sizeof(uint32_t)) + memset(ecc_lower_bound, 0, n * sizeof(uint32_t)) + + cdef uint32_t u, ecc_u + cdef uint32_t antipode, ecc_antipode + cdef uint32_t v, tmp + cdef size_t i, idx + cdef bitset_t seen + bitset_init(seen,n) + + cdef list active = list(range(n)) + + # Algorithm + while active: + # Select vertex with minimum eccentricity in active and update + # eccentricity upper bounds. + # For this, we select u with minimum eccentricity lower bound in active + # if ecc_u == ecc_lb[u], we are done. Otherwise, we update eccentricity + # lower bounds and repeat + + tmp = UINT32_MAX + for i, v in enumerate(active): + if ecc_lower_bound[v] < tmp: + tmp = ecc_lower_bound[v] + idx = i + active[idx], active[-1] = active[-1], active[idx] + u = active.pop() + ecc_u = simple_BFS(sd, u, distances, NULL, waiting_list, seen) + ecc_upper_bound[u] = ecc_u + + if ecc_u == UINT32_MAX: # Disconnected graph + break + + if ecc_u == ecc_lower_bound[u]: + # We found the good vertex. + # Update eccentricity upper bounds and remove from active those + # vertices for which gap is closed + i = 0 + while i < len(active): + v = active[i] + ecc_upper_bound[v] = min(ecc_upper_bound[v], distances[v] + ecc_u) + if ecc_upper_bound[v] == ecc_lower_bound[v]: + active[i] = active[-1] + active.pop() + else: + i += 1 + + else: + # u was not a good choice. + # We use its antipode to update eccentricity lower bounds. + # Observe that this antipode might have already been seen. + antipode = waiting_list[n-1] + for i, v in enumerate(active): + if v == antipode: + active[i] = active[-1] + active.pop() + break + + ecc_antipode = simple_BFS(sd, antipode, distances, NULL, waiting_list, seen) + ecc_upper_bound[antipode] = ecc_antipode + + # Update eccentricity lower bounds and remove from active those + # vertices for which the gap is closed + i = 0 + while i < len(active): + v = active[i] + ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) + if ecc_upper_bound[v] == ecc_lower_bound[v]: + active[i] = active[-1] + active.pop() + else: + i += 1 + + free_short_digraph(sd) + bitset_free(seen) + + return ecc_upper_bound + def eccentricity(G, algorithm="standard", vertex_list=None): r""" Return the vector of eccentricities in G. @@ -812,9 +945,16 @@ def eccentricity(G, algorithm="standard", vertex_list=None): - ``G`` -- a Graph or a DiGraph. - ``algorithm`` -- string (default: ``'standard'``); name of the method used - to compute the eccentricity of the vertices. Available algorithms are - ``'standard'`` which performs a BFS from each vertex and ``'bounds'`` - which uses the fast algorithm proposed in [TK2013]_ for undirected graphs. + to compute the eccentricity of the vertices. + + - ``'standard'`` -- Computes eccentricity by performing a BFS from each + vertex. + + - ``'bounds'`` -- Computes eccentricity using the fast algorithm proposed + in [TK2013]_ for undirected graphs. + + - ``'DHV'`` -- Computes all eccentricities of undirected graph using the + algorithm proposed in [Dragan2018]_. - ``vertex_list`` -- list (default: ``None``); a list of `n` vertices specifying a mapping from `(0, \ldots, n-1)` to vertex labels in `G`. When @@ -841,7 +981,10 @@ def eccentricity(G, algorithm="standard", vertex_list=None): sage: from sage.graphs.distances_all_pairs import eccentricity sage: g = graphs.RandomGNP(50, .1) - sage: eccentricity(g, algorithm='standard') == eccentricity(g, algorithm='bounds') + sage: ecc = eccentricity(g, algorithm='standard') + sage: ecc == eccentricity(g, algorithm='bounds') + True + sage: ecc == eccentricity(g, algorithm='DHV') True Case of not (strongly) connected (directed) graph:: @@ -905,6 +1048,8 @@ def eccentricity(G, algorithm="standard", vertex_list=None): ecc = c_eccentricity_bounding(G, vertex_list=int_to_vertex) elif algorithm == "standard": ecc = c_eccentricity(G, vertex_list=int_to_vertex) + elif algorithm == "DHV": + ecc = c_eccentricity_DHV(G, vertex_list=int_to_vertex) else: raise ValueError("unknown algorithm '{}', please contribute".format(algorithm)) @@ -982,6 +1127,120 @@ cdef uint32_t diameter_lower_bound_2sweep(short_digraph g, return LB +cdef tuple diameter_lower_bound_2Dsweep(short_digraph g, + short_digraph rev_g, + uint32_t source): + r""" + Lower bound on the diameter of digraph using directed version of 2-sweep. + + This method computes a lower bound on the diameter of an unweighted directed + graph using directed version of the 2-sweep algorithm [Broder2000]_. + In first part, it performs a forward BFS from `source` and selects a vertex + `vf` at a maximum distance from `source` and then it calculates backward + eccentricity of `vf` using a backward BFS from `vf`. In second part, it + performs backward BFS from `source` and selects a vertex `vb` from which + `source` is at maximum distance and then it calculates forward eccentricity + of `vb` using a forward BFS from `vb`. It then calculates lower bound LB of + diameter as the maximum of backward eccentricity of `vf` and forward + eccentricity of `vb` and `s` as respective vertex. + This method returns (`LB`, `s`, `m`, `d`), where `LB` is best found lower + bound on diameter, `s` is vertex whose forward/backward eccentricity is + `LB`, `d` is vertex at a distance `LB` from/to `s`, `m` is vertex at + distance `LB/2` from/to both `s` and `d`. + + INPUT: + + - ``g`` -- a short_digraph + + - ``rev_g`` -- a copy of `g` with edges reversed. + + - ``source`` -- starting node of the forward and backward BFS + + TESTS: + + Diameter of weakly connected digraph is infinity :: + + sage: from sage.graphs.distances_all_pairs import diameter + sage: G = DiGraph([(0,1)]) + sage: diameter(G, algorithm='2Dsweep') + +Infinity + """ + cdef uint32_t LB_1, LB_2, LB, LB_m, m, s, d + cdef uint32_t n = g.n + cdef uint32_t source_1 = source + cdef uint32_t source_2 = source + cdef bitset_t seen_1, seen_2 + + # Memory allocation + cdef MemoryAllocator mem = MemoryAllocator() + bitset_init(seen_1, n) + bitset_init(seen_2, n) + cdef uint32_t * distances_1 = mem.malloc(3 * n * sizeof(uint32_t)) + cdef uint32_t * distances_2 = mem.malloc(3 * n * sizeof(uint32_t)) + if not distances_1 or not distances_2: + bitset_free(seen_1) + bitset_free(seen_2) + raise MemoryError() + + cdef uint32_t * predecessors_1 = distances_1 + n + cdef uint32_t * predecessors_2 = distances_2 + n + cdef uint32_t * waiting_list_1 = distances_1 + 2 * n + cdef uint32_t * waiting_list_2 = distances_2 + 2 * n + + # we perform forward BFS from source and get its forward eccentricity + LB_1 = simple_BFS(g, source_1, distances_1, NULL, waiting_list_1, seen_1) + + # if forward eccentricity of source is infinite, then graph is + # not strongly connected and its diameter is infinite + if LB_1 == UINT32_MAX: + bitset_free(seen_1) + bitset_free(seen_2) + return (UINT32_MAX, 0, 0, 0) + + # we perform backward BFS from source and get its backward eccentricity + LB_2 = simple_BFS(rev_g, source_2, distances_2, NULL, waiting_list_2, seen_2) + + # if backward eccentricity of source is infinite, then graph is + # not strongly connected and its diameter is infinite + if LB_2 == UINT32_MAX: + bitset_free(seen_1) + bitset_free(seen_2) + return (UINT32_MAX, 0, 0, 0) + + # Then we perform backward BFS from the last visited vertex of forward BFS + # from source and obtain its backward eccentricity. + source_1 = waiting_list_1[n - 1] + LB_1 = simple_BFS(rev_g, source_1, distances_1, predecessors_1, waiting_list_1, seen_1) + + # Then we perform forward BFS from the last visited vertex of backward BFS + # from source and obtain its forward eccentricity. + source_2 = waiting_list_2[n - 1] + LB_2 = simple_BFS(g, source_2, distances_2, predecessors_2, waiting_list_2, seen_2) + + # we select best found lower bound as LB, s and d as source and destination + # of that BFS call and m as vertex at a distance LB/2 from/to both s and d + if LB_1 < LB_2: + LB = LB_2 + s = waiting_list_2[0] + d = waiting_list_2[n - 1] + LB_m = LB_2 / 2 + m = d + while distances_2[m] > LB_m: + m = predecessors_2[m] + else: + LB = LB_1 + s = waiting_list_1[0] + d = waiting_list_1[n - 1] + LB_m = LB_1 / 2 + m = d + while distances_1[m] > LB_m: + m = predecessors_1[m] + + bitset_free(seen_1) + bitset_free(seen_2) + + return (LB, s, m, d) + cdef tuple diameter_lower_bound_multi_sweep(short_digraph g, uint32_t source): """ @@ -1114,7 +1373,7 @@ cdef uint32_t diameter_iFUB(short_digraph g, # The algorithm: # # The diameter of the graph is equal to the maximum eccentricity of a - # vertex. Let m be any vertex, and let V be partitionned into A u B where: + # vertex. Let m be any vertex, and let V be partitioned into A u B where: # # d(m,a)<=i for all a \in A # d(m,b)>=i for all b \in B @@ -1147,18 +1406,267 @@ cdef uint32_t diameter_iFUB(short_digraph g, # We finally return the computed diameter return LB +cdef uint32_t diameter_DiFUB(short_digraph sd, + uint32_t source): + r""" + Return the diameter of unweighted directed graph. + + The ``DiFUB`` (Directed iterative Fringe Upper Bound) algorithm calculates + the exact value of the diameter of an unweighted directed graph [CGLM2012]_. + + This algorithm starts from a vertex found through a 2Dsweep call (a directed + version of the 2sweep method). The worst case time complexity of the DiFUB + algorithm is `O(nm)`, but it can be very fast in practice. See the code's + documentation and [CGLM2012]_ for more details. + + If the digraph is not strongly connected, the returned value is infinity. + + INPUT: -def diameter(G, algorithm='iFUB', source=None): + - ``sd`` -- a short_digraph + + - ``source`` -- starting node of the first BFS + + TESTS:: + + The diameter of a weakly connected digraph is infinity :: + + sage: from sage.graphs.distances_all_pairs import diameter + sage: G = digraphs.Path(5) + sage: diameter(G, algorithm='DiFUB') + +Infinity + """ + cdef uint32_t n = sd.n + + if n <= 1: # Trivial case + return 0 + + cdef short_digraph rev_sd # Copy of sd with edges reversed + init_reverse(rev_sd, sd) + + cdef uint32_t LB, s, m, d, LB_1, LB_2, UB + cdef size_t i + cdef bitset_t seen + + # We select a vertex with low eccentricity using 2Dsweep + LB, s, m, d = diameter_lower_bound_2Dsweep(sd, rev_sd, source) + + # If the lower bound is a very large number, it means that the digraph is + # not strongly connected and so the diameter is infinite. + if LB == UINT32_MAX: + return LB + + # We allocate some arrays and a bitset + bitset_init(seen, n) + cdef MemoryAllocator mem = MemoryAllocator() + cdef uint32_t * distances = mem.malloc(6 * n * sizeof(uint32_t)) + + if not distances: + bitset_free(seen) + raise MemoryError() + + cdef uint32_t * waiting_list = distances + n + cdef uint32_t * order_1 = distances + 2 * n + cdef uint32_t * order_2 = distances + 3 * n + cdef uint32_t * layer_1 = distances + 4 * n + cdef uint32_t * layer_2 = distances + 5 * n + + # We order the vertices by decreasing forward / backward layers. This is the + # inverse order of a forward / backward BFS from m, and so the inverse order + # of array waiting_list. Forward / Backward distances are stored in arrays + # layer_1 / layer_2 respectively. + LB_1 = simple_BFS(sd, m, layer_1, NULL, waiting_list, seen) + for i in range(n): + order_1[i] = waiting_list[n - i - 1] + + LB_2 = simple_BFS(rev_sd, m, layer_2, NULL, waiting_list, seen) + for i in range(n): + order_2[i] = waiting_list[n - i - 1] + + # update the lower bound + LB = max(LB, LB_1, LB_2) + + if LB == UINT32_MAX: # Not strongly connected case + return LB + + # The algorithm: + # + # The diameter of the digraph is equal to the maximum forward or backward + # eccentricity of a vertex. The algorithm is based on the following two + # observations: + # 1). All the nodes `x` above the level `i` in Backward BFS of `m` having + # forward eccentricity greater than `2(i-1)` have a corresponding node `y`, + # whose backward eccentricity is greater than or equal to the forward + # eccentricity of `x`, below or on the level `i` in Forward BFS of `m`. + # + # 2). All the nodes `x` above the level `i` in Forward BFS of `m` having + # backward eccentricity greater than `2(i-1)` have a corresponding node `y`, + # whose forward eccentricity is greater than or equal to the backward + # eccentricity of `x`, below or on the level `i` in Backward BFS of `m` + # + # Therefore, we calculate backward / forward eccentricity of all nodes at + # level `i` in Forward / Backward BFS of `m` respectively. And their + # maximum is `LB`. If `LB` is greater than `2(max distance at next level)` + # then we are done, else we proceed further. + + i = 0 + UB = max(2 * layer_1[order_1[i]], 2 * layer_2[order_2[i]]) + + while LB < UB: + LB_1 = simple_BFS(rev_sd, order_1[i], distances, NULL, waiting_list, seen) + LB_2 = simple_BFS(sd, order_2[i], distances, NULL, waiting_list, seen) + + # update the lower bound + LB = max(LB, LB_1, LB_2) + i += 1 + + if LB == UINT32_MAX or i == n: + break + # maximum distance at next level + UB = max(2 * layer_1[order_1[i]], 2 * layer_2[order_2[i]]) + + bitset_free(seen) + free_short_digraph(rev_sd) + + # Finally return the computed diameter + return LB +cdef uint32_t diameter_DHV(short_digraph g): + r""" + Return the diameter of unweighted graph `g`. + + This method computes the diameter of unweighted undirected graph using the + algorithm proposed in [Dragan2018]_. + + This method returns Infinity if graph is not connected. + + INPUT: + + - ``g`` -- a short_digraph + + EXAMPLES:: + + sage: from sage.graphs.distances_all_pairs import diameter + sage: G = graphs.PathGraph(5) + sage: diameter(G, algorithm='DHV') + 4 + + TESTS: + + sage: G = graphs.RandomGNP(20,0.3) + sage: G.diameter() == diameter(G, algorithm='DHV') + True + + sage: G = Graph([(0, 1)]) + sage: diameter(G, algorithm='DHV') + 1 + """ + cdef uint32_t n = g.n + if n <= 1: + return 0 + + cdef MemoryAllocator mem = MemoryAllocator() + cdef uint32_t * distances = mem.malloc(4 * n * sizeof(uint32_t)) + if not distances: + raise MemoryError() + + cdef uint32_t * waiting_list = distances + n + + # For storing upper and lower bounds on eccentricity of nodes + cdef uint32_t * ecc_upper_bound = distances + 2 * n + cdef uint32_t * ecc_lower_bound = distances + 3 * n + memset(ecc_upper_bound, -1, n * sizeof(uint32_t)) + memset(ecc_lower_bound, 0, n * sizeof(uint32_t)) + + cdef uint32_t u, ecc_u + cdef uint32_t x, ecc_x + cdef uint32_t antipode, ecc_antipode + cdef uint32_t LB = 0 + cdef uint32_t UB = UINT32_MAX + cdef uint32_t v, tmp + cdef size_t i, idx + cdef bitset_t seen + bitset_init(seen, n) + + cdef list active = list(range(n)) + + # Algorithm + while LB < UB and active: + # 1. Select vertex u with maximum eccentricity upper bound + tmp = 0 + for i, v in enumerate(active): + if ecc_upper_bound[v] > tmp: + tmp = ecc_upper_bound[v] + idx = i + active[idx], active[-1] = active[-1], active[idx] + u = active.pop() + + # Compute the eccentricity of u and update eccentricity lower bounds + ecc_u = simple_BFS(g, u, distances, NULL, waiting_list, seen) + LB = max(LB, ecc_u) + if LB == UINT32_MAX: # Disconnected graph + break + + for v in active: + ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) + + # 2. Select x such that dist(u, x) + ecc[x] == ecc[u]. + # Since we don't know ecc[x], we select x with minimum eccentricity + # lower bound. If ecc[x] == ecc_lb[x], we are done. Otherwise, we + # update eccentricity lower bounds and repeat + while active: + # Select v with minimum eccentricity lower bound + tmp = UINT32_MAX + for i, v in enumerate(active): + if ecc_lower_bound[v] < tmp: + tmp = ecc_lower_bound[v] + idx = i + active[idx], active[-1] = active[-1], active[idx] + x = active.pop() + ecc_x = simple_BFS(g, x, distances, NULL, waiting_list, seen) + LB = max(LB, ecc_x) + + if ecc_x == ecc_lower_bound[x]: + # We found the good vertex x + # We update eccentricity upper bounds of the remaining vertices, + # set UB to the largest of these values and break + UB = 0 + for v in active: + ecc_upper_bound[v] = min(ecc_upper_bound[v], distances[v] + ecc_x) + UB = max(UB, ecc_upper_bound[v]) + break + + else: + # x was not a good choice + # We use its antipode to update eccentricity lower bounds. + # Observe that this antipode might have already been seen. + antipode = waiting_list[n-1] + for i, v in enumerate(active): + if v == antipode: + active[i] = active[-1] + tmp = active.pop() + break + + ecc_antipode = simple_BFS(g, antipode, distances, NULL, waiting_list, seen) + LB = max(LB, ecc_antipode) + for v in active: + ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) + + bitset_free(seen) + return LB + + +def diameter(G, algorithm=None, source=None): r""" Return the diameter of `G`. - This algorithm returns Infinity if the (di)graph is not connected. It can - also quickly return a lower bound on the diameter using the ``2sweep`` and - ``multi-sweep`` schemes. + This method returns Infinity if the (di)graph is not connected. It can + also quickly return a lower bound on the diameter using the ``2sweep``, + ``2Dsweep`` and ``multi-sweep`` schemes. INPUT: - - ``algorithm`` -- (default: 'iFUB') specifies the algorithm to use among: + - ``algorithm`` -- string (default: ``None``); specifies the algorithm to + use among: - ``'standard'`` -- Computes the diameter of the input (di)graph as the largest eccentricity of its vertices. This is the classical algorithm @@ -1172,6 +1680,14 @@ def diameter(G, algorithm='iFUB', source=None): of `G`. The time complexity of this algorithm is linear in the size of `G`. + - ``'2Dsweep'`` -- Computes lower bound on the diameter of an unweighted + directed graph using directed version of ``2sweep`` as proposed in + [Broder2000]_. If the digraph is not strongly connected, the returned + value is infinity. + + - ``'DHV'`` -- Computes diameter of unweighted undirected graph using the + algorithm proposed in [Dragan2018]_. + - ``'multi-sweep'`` -- Computes a lower bound on the diameter of an unweighted undirected graph using several iterations of the ``2sweep`` algorithms [CGHLM2013]_. Roughly, it first uses ``2sweep`` to identify @@ -1210,7 +1726,12 @@ def diameter(G, algorithm='iFUB', source=None): by the remark above. The worst case time complexity of the iFUB algorithm is `O(nm)`, but it can be very fast in practice. - - ``source`` -- (default: None) vertex from which to start the first BFS. + - ``'DiFUB'`` -- The directed version of iFUB (iterative Fringe Upper + Bound) algorithm. See the code's documentation and [CGLM2012]_ for more + details. If the digraph is not strongly connected, the returned value is + infinity. + + - ``source`` -- (default: ``None``) vertex from which to start the first BFS. If ``source==None``, an arbitrary vertex of the graph is chosen. Raise an error if the initial vertex is not in `G`. This parameter is not used when ``algorithm=='standard'``. @@ -1224,7 +1745,12 @@ def diameter(G, algorithm='iFUB', source=None): sage: G = Graph({0: [], 1: [], 2: [1]}) sage: diameter(G, algorithm='iFUB') +Infinity - + sage: G = digraphs.Circuit(6) + sage: diameter(G, algorithm='2Dsweep') + 5 + sage: G = graphs.PathGraph(7).to_directed() + sage: diameter(G, algorithm='DiFUB') + 6 Although max( ) is usually defined as -Infinity, since the diameter will never be negative, we define it to be zero:: @@ -1233,13 +1759,14 @@ def diameter(G, algorithm='iFUB', source=None): sage: diameter(G, algorithm='iFUB') 0 - Comparison of exact algorithms:: + Comparison of exact algorithms for graphs:: sage: G = graphs.RandomBarabasiAlbert(100, 2) sage: d1 = diameter(G, algorithm='standard') sage: d2 = diameter(G, algorithm='iFUB') sage: d3 = diameter(G, algorithm='iFUB', source=G.random_vertex()) - sage: if d1 != d2 or d1 != d3: print("Something goes wrong!") + sage: d4 = diameter(G, algorithm='DHV') + sage: if d1 != d2 or d1 != d3 or d1 != d4: print("Something goes wrong!") Comparison of lower bound algorithms:: @@ -1247,7 +1774,16 @@ def diameter(G, algorithm='iFUB', source=None): sage: lbm = diameter(G, algorithm='multi-sweep') sage: if not (lb2 <= lbm and lbm <= d3): print("Something goes wrong!") - TESTS: + Comparison of exact algorithms for digraphs:: + + sage: D = DiGraph(graphs.RandomBarabasiAlbert(50, 2)) + sage: d1 = diameter(D, algorithm='standard') + sage: d2 = diameter(D, algorithm='DiFUB') + sage: d3 = diameter(D, algorithm='DiFUB', source=D.random_vertex()) + sage: d1 == d2 and d1 == d3 + True + + TESTS:: This was causing a segfault. Fixed in :trac:`17873` :: @@ -1255,36 +1791,42 @@ def diameter(G, algorithm='iFUB', source=None): sage: diameter(G, algorithm='iFUB') 0 """ - cdef int n = G.order() - if not n: + cdef uint32_t n = G.order() + if n <= 1: return 0 - if algorithm == 'standard' or G.is_directed(): - return max(G.eccentricity()) - elif algorithm is None: - algorithm = 'iFUB' - elif not algorithm in ['2sweep', 'multi-sweep', 'iFUB']: - raise ValueError("unknown algorithm for computing the diameter") + if G.is_directed(): + if algorithm is None: + algorithm = 'DiFUB' + elif not algorithm in ['2Dsweep', 'standard', 'DiFUB']: + raise ValueError("unknown algorithm for computing the diameter of directed graph") + else: + if algorithm is None: + algorithm = 'iFUB' + elif not algorithm in ['2sweep', 'multi-sweep', 'iFUB', 'standard', 'DHV']: + raise ValueError("unknown algorithm for computing the diameter of undirected graph") + if algorithm == 'standard': + return max(G.eccentricity()) if source is None: source = next(G.vertex_iterator()) elif not G.has_vertex(source): raise ValueError("the specified source is not a vertex of the input Graph") - # Copying the whole graph to obtain the list of neighbors quicker than by # calling out_neighbors. This data structure is well documented in the # module sage.graphs.base.static_sparse_graph cdef list int_to_vertex = list(G) cdef short_digraph sd init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) + cdef short_digraph rev_sd # to store copy of sd with edges reversed # and we map the source to an int in [0,n-1] cdef uint32_t isource = 0 if source is None else int_to_vertex.index(source) cdef bitset_t seen cdef uint32_t* tab - cdef int LB + cdef uint32_t LB if algorithm == '2sweep': # We need to allocate arrays and bitset @@ -1300,9 +1842,20 @@ def diameter(G, algorithm='iFUB', source=None): bitset_free(seen) sig_free(tab) + elif algorithm == '2Dsweep': + init_reverse(rev_sd, sd) + LB = diameter_lower_bound_2Dsweep(sd, rev_sd, isource)[0] + free_short_digraph(rev_sd) + elif algorithm == 'multi-sweep': LB = diameter_lower_bound_multi_sweep(sd, isource)[0] + elif algorithm == 'DiFUB': + LB = diameter_DiFUB(sd, isource) + + elif algorithm == 'DHV': + LB = diameter_DHV(sd) + else: # algorithm == 'iFUB' LB = diameter_iFUB(sd, isource) @@ -1316,6 +1869,116 @@ def diameter(G, algorithm='iFUB', source=None): return int(LB) +########### +# Radius # +########### + +def radius_DHV(G): + r""" + Return the radius of unweighted graph `G`. + + This method computes the radius of unweighted undirected graph using the + algorithm given in [Dragan2018]_. + + This method returns Infinity if graph is not connected. + + EXAMPLES:: + + sage: from sage.graphs.distances_all_pairs import radius_DHV + sage: G = graphs.PetersenGraph() + sage: radius_DHV(G) + 2 + sage: G = graphs.RandomGNP(20,0.3) + sage: from sage.graphs.distances_all_pairs import eccentricity + sage: radius_DHV(G) == min(eccentricity(G, algorithm='bounds')) + True + + TESTS: + + sage: G = Graph() + sage: radius_DHV(G) + 0 + sage: G = Graph(1) + sage: radius_DHV(G) + 0 + sage: G = Graph(2) + sage: radius_DHV(G) + +Infinity + sage: G = graphs.PathGraph(2) + sage: radius_DHV(G) + 1 + sage: G = DiGraph(1) + sage: radius_DHV(G) + Traceback (most recent call last): + ... + TypeError: this method works for unweighted undirected graphs only + """ + if G.is_directed(): + raise TypeError("this method works for unweighted undirected graphs only") + + cdef uint32_t n = G.order() + if n <= 1: + return 0 + + cdef list int_to_vertex = list(G) + cdef short_digraph sd + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) + + cdef uint32_t source, ecc_source + cdef uint32_t antipode, ecc_antipode + cdef uint32_t UB = UINT32_MAX + cdef uint32_t LB = 0 + + cdef MemoryAllocator mem = MemoryAllocator() + cdef uint32_t * distances = mem.malloc(3 * n * sizeof(uint32_t)) + if not distances: + raise MemoryError() + + cdef uint32_t * waiting_list = distances + n + + # For storing lower bound on eccentricity of nodes + cdef uint32_t * ecc_lower_bound = distances + 2 * n + memset(ecc_lower_bound, 0, n * sizeof(uint32_t)) + + cdef bitset_t seen + bitset_init(seen, n) + + # Algorithm + source = 0 + while LB < UB: + # 1) pick vertex with minimum eccentricity lower bound + # and compute its eccentricity + ecc_source = simple_BFS(sd, source, distances, NULL, waiting_list, seen) + + if ecc_source == UINT32_MAX: # Disconnected graph + break + + UB = min(UB, ecc_source) # minimum among exact computed eccentricities + if ecc_source == ecc_lower_bound[source]: + # we have found minimum eccentricity vertex and hence the radius + break + + # 2) Take vertex at largest distance from source, called antipode (last + # vertex visited in simple_BFS), and compute its BFS distances. + # By definition of antipode, we have ecc_antipode >= ecc_source. + antipode = waiting_list[n-1] + ecc_antipode = simple_BFS(sd, antipode, distances, NULL, waiting_list, seen) + + # 3) Use distances from antipode to improve eccentricity lower bounds. + # We also determine the next source + LB = UINT32_MAX + for v in range(n): + ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) + if LB > ecc_lower_bound[v]: + LB = ecc_lower_bound[v] + source = v # vertex with minimum eccentricity lower bound + + bitset_free(seen) + if UB == UINT32_MAX: + from sage.rings.infinity import Infinity + return +Infinity + + return UB ################ # Wiener index # @@ -1510,12 +2173,7 @@ def floyd_warshall(gg, paths=True, distances=False): sage: g = graphs.Grid2dGraph(2,2) sage: from sage.graphs.distances_all_pairs import floyd_warshall - sage: print(floyd_warshall(g)) # py2 - {(0, 1): {(0, 1): None, (1, 0): (0, 0), (0, 0): (0, 1), (1, 1): (0, 1)}, - (1, 0): {(0, 1): (0, 0), (1, 0): None, (0, 0): (1, 0), (1, 1): (1, 0)}, - (0, 0): {(0, 1): (0, 0), (1, 0): (0, 0), (0, 0): None, (1, 1): (0, 1)}, - (1, 1): {(0, 1): (1, 1), (1, 0): (1, 1), (0, 0): (0, 1), (1, 1): None}} - sage: print(floyd_warshall(g)) # py3 + sage: print(floyd_warshall(g)) {(0, 0): {(0, 0): None, (0, 1): (0, 0), (1, 0): (0, 0), (1, 1): (0, 1)}, (0, 1): {(0, 1): None, (0, 0): (0, 1), (1, 0): (0, 0), (1, 1): (0, 1)}, (1, 0): {(1, 0): None, (0, 0): (1, 0), (0, 1): (0, 0), (1, 1): (1, 0)}, diff --git a/src/sage/graphs/generators/basic.py b/src/sage/graphs/generators/basic.py index 5d42dd851af..6ebad186e4d 100644 --- a/src/sage/graphs/generators/basic.py +++ b/src/sage/graphs/generators/basic.py @@ -15,7 +15,6 @@ # http://www.gnu.org/licenses/ ########################################################################### from __future__ import print_function -from six.moves import range # import from Sage library from sage.graphs.graph import Graph @@ -502,7 +501,7 @@ def CompleteBipartiteGraph(n1, n2, set_position=True): ... ValueError: the arguments n1(=1) and n2(=-1) must be positive integers """ - if n1<0 or n2<0: + if n1 < 0 or n2 < 0: raise ValueError('the arguments n1(={}) and n2(={}) must be positive integers'.format(n1,n2)) G = Graph(n1+n2, name="Complete bipartite graph of order {}+{}".format(n1, n2)) @@ -609,6 +608,67 @@ def DiamondGraph(): edges = [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)] return graph.Graph(edges, pos=pos_dict, name="Diamond Graph") +def GemGraph(): + """ + Return a gem graph with 5 nodes. + + A gem graph is a fan graph (4,1). + + PLOTTING: Upon construction, the position dictionary is filled to + override the spring-layout algorithm. By convention, the gem + graph is drawn as a gem, with the sharp part on the bottom. + + EXAMPLES: + + Construct and show a gem graph:: + + sage: g = graphs.GemGraph() + sage: g.show() # long time + """ + pos_dict = {0:(0.5,0),1:(0,0.75),2:(0.25,1),3:(0.75,1),4:(1,0.75)} + edges = [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (2, 3), (3, 4)] + return graph.Graph(edges, pos=pos_dict, name="Gem Graph") + +def ForkGraph(): + """ + Return a fork graph with 5 nodes. + + A fork graph, sometimes also called chair graph, is 5 vertex tree. + + PLOTTING: Upon construction, the position dictionary is filled to + override the spring-layout algorithm. By convention, the fork + graph is drawn as a fork, with the sharp part on the bottom. + + EXAMPLES: + + Construct and show a fork graph:: + + sage: g = graphs.ForkGraph() + sage: g.show() # long time + """ + pos_dict = {0:(0,0),1:(1,0),2:(0,1),3:(1,1),4:(0,2)} + edges = [(0, 2), (2, 3), (3, 1), (2, 4)] + return graph.Graph(edges, pos=pos_dict, name="Fork Graph") + +def DartGraph(): + """ + Return a dart graph with 5 nodes. + + PLOTTING: Upon construction, the position dictionary is filled to + override the spring-layout algorithm. By convention, the dart + graph is drawn as a dart, with the sharp part on the bottom. + + EXAMPLES: + + Construct and show a dart graph:: + + sage: g = graphs.DartGraph() + sage: g.show() # long time + """ + pos_dict = {0:(0,1),1:(-1,0),2:(1,0),3:(0,-1),4:(0,0)} + edges = [(0, 1), (0, 2), (1, 4), (2, 4), (0, 4), (3, 4)] + return graph.Graph(edges, pos=pos_dict, name="Dart Graph") + def EmptyGraph(): """ Returns an empty graph (0 nodes and 0 edges). @@ -900,20 +960,20 @@ def GridGraph(dim_list): g = Graph() n_dim = len(dim) - if n_dim==1: + if n_dim == 1: # Vertices are labeled from 0 to dim[0]-1 g = PathGraph(dim[0]) - elif n_dim==2: + elif n_dim == 2: # We use the Grid2dGraph generator to also get the positions g = Grid2dGraph(*dim) - elif n_dim>2: + elif n_dim > 2: # Vertices are tuples of dimension n_dim, and the graph contains at # least vertex (0, 0, ..., 0) g.add_vertex(tuple([0]*n_dim)) import itertools for u in itertools.product(*[range(d) for d in dim]): for i in range(n_dim): - if u[i]+1 3` is + `2d + \lfloor \frac{d}{2} \rfloor - 2` :: + + sage: d = 4 + sage: g = graphs.CubeConnectedCycle(d) + sage: g.diameter() == 2*d+d//2-2 + True + + All vertices have degree `3` when `d > 1` :: + + sage: g = graphs.CubeConnectedCycle(5) + sage: all(g.degree(v) == 3 for v in g) + True + + TESTS:: + + sage: g = graphs.CubeConnectedCycle(0) + Traceback (most recent call last): + ... + ValueError: the dimension d must be greater than 0 + """ + if d < 1: + raise ValueError('the dimension d must be greater than 0') + + G = Graph(name="Cube-Connected Cycle of dimension {}".format(d)) + + if d == 1: + G.allow_loops(True) + # only d = 1 requires loops + G.add_edges([((0,0),(0,1)), ((0,0),(0,0)), ((0,1),(0,1))]) + return G + + if d == 2: + # only d = 2 require multiple edges + G.allow_multiple_edges(True) + G.add_edges([((0, 0), (0, 1)), ((0, 0), (0, 1)), ((0, 0), (1, 0)), + ((0, 1), (2, 1)), ((1, 0), (1, 1)), ((1, 0), (1, 1)), + ((1, 1), (3, 1)), ((2, 0), (2, 1)), ((2, 0), (2, 1)), + ((2, 0), (3, 0)), ((3, 0), (3, 1)), ((3, 0), (3, 1))]) + return G + + for x in range(1<= 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: @@ -5337,6 +5400,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(): @@ -5346,7 +5410,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): @@ -5977,12 +6042,10 @@ def planar_dual(self, embedding=None): sage: g = graphs.CubeGraph(3) sage: for e in label_dict: ....: g.set_edge_label(e[0], e[1], label_dict[e]) - ....: sage: gd = g.planar_dual() sage: incident_labels = [] sage: for v in gd: ....: incident_labels.append(sorted([l for _, _, l in gd.edges_incident(v) if l])) - ....: sage: sorted(incident_labels) [[], [1], [1, 2, 3, 4], [2], [3], [4]] @@ -7067,8 +7130,9 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm="MILP", The heuristic totally agrees:: sage: g = graphs.PetersenGraph() - sage: g.longest_path(algorithm="backtrack").edges(labels=False) - [(0, 1), (1, 2), (2, 3), (3, 4), (4, 9), (5, 7), (5, 8), (6, 8), (6, 9)] + sage: p = g.longest_path(algorithm="backtrack").edges(labels=False) + sage: len(p) + 9 .. PLOT:: @@ -8229,19 +8293,24 @@ def hamiltonian_cycle(self, algorithm='tsp', solver=None, constraint_generation= sage: G=graphs.HeawoodGraph() sage: G.hamiltonian_cycle(algorithm='backtrack') - (True, [11, 10, 1, 2, 3, 4, 9, 8, 7, 6, 5, 0, 13, 12]) + (True, [...]) And now in the Petersen graph :: sage: G=graphs.PetersenGraph() - sage: G.hamiltonian_cycle(algorithm='backtrack') - (False, [6, 8, 5, 0, 1, 2, 7, 9, 4, 3]) + sage: B, P = G.hamiltonian_cycle(algorithm='backtrack') + sage: B + False + sage: len(P) + 10 + sage: G.has_edge(P[0], P[-1]) + False Finally, we test the algorithm in a cube graph, which is Hamiltonian :: sage: G=graphs.CubeGraph(3) sage: G.hamiltonian_cycle(algorithm='backtrack') - (True, ['010', '110', '100', '000', '001', '101', '111', '011']) + (True, [...]) """ if self.order() < 2: @@ -9159,7 +9228,7 @@ def _ford_fulkerson(self, s, t, use_edge_labels=False, integer=False, value_only # Building and returning the flow graph g = DiGraph() - g.add_edges((x, y, l) for (x, y), l in iteritems(flow) if l > 0) + g.add_edges((x, y, l) for (x, y), l in flow.items() if l > 0) g.set_pos(self.get_pos()) return flow_intensity, g @@ -9333,7 +9402,7 @@ def multicommodity_flow(self, terminals, integer=True, use_edge_labels=False, ve flow = p.get_values(flow) # building clean flow digraphs - flow_graphs = [g._build_flow_graph({e: f for (ii,e),f in iteritems(flow) if ii == i}, integer=integer) + flow_graphs = [g._build_flow_graph({e: f for (ii,e),f in flow.items() if ii == i}, integer=integer) for i in range(len(terminals))] # which could be .. graphs ! @@ -9393,7 +9462,7 @@ def _build_flow_graph(self, flow, integer): g = DiGraph() # add significant edges - for (u,v),l in iteritems(flow): + for (u,v),l in flow.items(): if l > 0 and not (integer and l < .5): g.add_edge(u, v, l) @@ -9634,8 +9703,8 @@ def pagerank(self, alpha=0.85, personalization=None, by_weight=False, - ``weight_function`` -- function (default: ``None``); a function that takes as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. + and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l`` + is not ``None``, else ``1`` as a weight. - ``dangling`` -- dict (default: ``None``); a dictionary keyed by a vertex the outedge of "dangling" vertices, (i.e., vertices without @@ -9759,9 +9828,13 @@ def pagerank(self, alpha=0.85, personalization=None, by_weight=False, if weight_function is not None: by_weight = True - if weight_function is None and by_weight: + if by_weight: + if weight_function is None: + def weight_function(e): + return 1 if e[2] is None else e[2] + else: def weight_function(e): - return e[2] + return 1 if by_weight: self._check_weight_function(weight_function) @@ -10102,7 +10175,7 @@ def random_edge(self, **kwds): triple ``(u, v, l)`` of values, in which ``l`` is the label of edge ``(u, v)``:: - sage: g.random_edge() + sage: g.random_edge() # random (3, 4, None) TESTS:: @@ -10429,7 +10502,7 @@ def neighbor_iterator(self, vertex, closed=False): :: sage: g = graphs.CubeGraph(3) - sage: sorted(list(g.neighbor_iterator('010', closed=True))) + sage: sorted(g.neighbor_iterator('010', closed=True)) ['000', '010', '011', '110'] :: @@ -10551,7 +10624,7 @@ def vertices(self, sort=True, key=None): if (not sort) and key: raise ValueError('sort keyword is False, yet a key function is given') if sort: - return sorted(list(self.vertex_iterator()), key=key) + return sorted(self.vertex_iterator(), key=key) return list(self.vertex_iterator()) def neighbors(self, vertex, closed=False): @@ -11161,7 +11234,6 @@ def contract_edge(self, u, v=None, label=None): sage: G.allow_loops(True); G.allow_multiple_edges(True) sage: for e in G.edges(sort=False): ....: G.set_edge_label(e[0], e[1], (e[0] + e[1])) - ....: sage: G.contract_edge(0, 1); G.edges() [(0, 2, 2), (0, 2, 3), (0, 3, 3), (0, 3, 4), (2, 3, 5)] sage: G.contract_edge(0, 2, 4); G.edges() @@ -11995,17 +12067,11 @@ def degree(self, vertices=None, labels=False): sage: D = digraphs.DeBruijn(4, 2) sage: D.delete_vertex('20') - sage: print(D.degree()) # py2 - [6, 7, 7, 7, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8, 8] - sage: print(D.degree(vertices=list(D))) # py2 - [6, 7, 7, 7, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8, 8] - sage: print(D.degree(vertices=D.vertices())) # py2 - [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] - sage: print(D.degree()) # py3 + sage: print(D.degree()) [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] - sage: print(D.degree(vertices=list(D))) # py3 + sage: print(D.degree(vertices=list(D))) [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] - sage: print(D.degree(vertices=D.vertices())) # py3 + sage: print(D.degree(vertices=D.vertices())) [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] """ if labels: @@ -12105,32 +12171,16 @@ def degree_iterator(self, vertices=None, labels=False): EXAMPLES:: sage: G = graphs.Grid2dGraph(3, 4) - sage: for i in G.degree_iterator(): # py2 - ....: print(i) # py2 - 3 - 4 - 2 - ... - 2 - 4 - sage: for i in G.degree_iterator(): # py3 - ....: print(i) # py3 + sage: for i in G.degree_iterator(): + ....: print(i) 2 3 3 ... 3 2 - sage: for i in G.degree_iterator(labels=True): # py2 - ....: print(i) # py2 - ((0, 1), 3) - ((1, 2), 4) - ((0, 0), 2) - ... - ((0, 3), 2) - ((1, 1), 4) - sage: for i in G.degree_iterator(labels=True): # py3 - ....: print(i) # py3 + sage: for i in G.degree_iterator(labels=True): + ....: print(i) ((0, 0), 2) ((0, 1), 3) ((0, 2), 3) @@ -12141,29 +12191,15 @@ def degree_iterator(self, vertices=None, labels=False): :: sage: D = graphs.Grid2dGraph(2,4).to_directed() - sage: for i in D.degree_iterator(): # py2 - ....: print(i) # py2 - 6 - 6 - ... - 4 - 6 - sage: for i in D.degree_iterator(): # py3 - ....: print(i) # py3 + sage: for i in D.degree_iterator(): + ....: print(i) 4 6 ... 6 4 - sage: for i in D.degree_iterator(labels=True): # py2 - ....: print(i) # py2 - ((0, 1), 6) - ((1, 2), 6) - ... - ((1, 0), 4) - ((0, 2), 6) - sage: for i in D.degree_iterator(labels=True): # py3 - ....: print(i) # py3 + sage: for i in D.degree_iterator(labels=True): + ....: print(i) ((0, 0), 4) ((0, 1), 6) ... @@ -12175,13 +12211,9 @@ def degree_iterator(self, vertices=None, labels=False): sage: V = list(D) sage: D = digraphs.DeBruijn(4, 2) sage: D.delete_vertex('20') - sage: print(list(D.degree_iterator())) # py2 - [6, 7, 7, 7, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8, 8] - sage: print([D.degree(v) for v in D]) # py2 - [6, 7, 7, 7, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8, 8] - sage: print(list(D.degree_iterator())) # py3 + sage: print(list(D.degree_iterator())) [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] - sage: print([D.degree(v) for v in D]) # py3 + sage: print([D.degree(v) for v in D]) [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] """ if vertices is None: @@ -13110,7 +13142,7 @@ def random_subgraph(self, p, inplace=False): sage: P = graphs.PetersenGraph() sage: P.random_subgraph(.25) - Subgraph of (Petersen graph): Graph on 4 vertices + Subgraph of (Petersen graph): Graph on ... vert... """ p = float(p) if p < 0 or p > 1: @@ -14145,7 +14177,7 @@ def cluster_triangles(self, nbunch=None, implementation=None): if nbunch is None: return triangles_count(self) - return {v: c for v, c in iteritems(triangles_count(self)) if v in nbunch} + return {v: c for v, c in triangles_count(self).items() if v in nbunch} def clustering_average(self, implementation=None): r""" @@ -14354,11 +14386,11 @@ def coeff_from_triangle_count(v, count): elif implementation == 'sparse_copy': from sage.graphs.base.static_sparse_graph import triangles_count return {v: coeff_from_triangle_count(v, count) - for v, count in iteritems(triangles_count(self))} + for v, count in triangles_count(self).items()} elif implementation =="dense_copy": from sage.graphs.base.static_dense_graph import triangles_count return {v: coeff_from_triangle_count(v, count) - for v, count in iteritems(triangles_count(self))} + for v, count in triangles_count(self).items()} def cluster_transitivity(self): r""" @@ -14459,8 +14491,8 @@ def distance_all_pairs(self, by_weight=False, algorithm=None, - ``weight_function`` -- function (default: ``None``); a function that takes as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. + and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l`` + is not ``None``, else ``1`` as a weight. - ``check_weight`` -- boolean (default: ``True``); whether to check that the ``weight_function`` outputs a number for each edge. @@ -14507,478 +14539,6 @@ def distance_all_pairs(self, by_weight=False, algorithm=None, weight_function=weight_function, check_weight=check_weight)[0] - def eccentricity(self, v=None, by_weight=False, algorithm=None, - weight_function=None, check_weight=True, dist_dict=None, - with_labels=False): - """ - Return the eccentricity of vertex (or vertices) ``v``. - - The eccentricity of a vertex is the maximum distance to any other - vertex. - - For more information and examples on how to use input variables, see - :meth:`~GenericGraph.shortest_paths` - - INPUT: - - - ``v`` - either a single vertex or a list of vertices. If it is not - specified, then it is taken to be all vertices. - - - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge - weights are taken into account; if False, all edges have weight 1 - - - ``algorithm`` -- string (default: ``None``); one of the following - algorithms: - - - ``'BFS'`` - the computation is done through a BFS centered on each - vertex successively. Works only if ``by_weight==False``. - - - ``'Floyd-Warshall-Cython'`` - a Cython implementation of the - Floyd-Warshall algorithm. Works only if ``by_weight==False`` and - ``v is None``. - - - ``'Floyd-Warshall-Python'`` - a Python implementation of the - Floyd-Warshall algorithm. Works also with weighted graphs, even with - negative weights (but no negative cycle is allowed). However, ``v`` - must be ``None``. - - - ``'Dijkstra_NetworkX'`` - the Dijkstra algorithm, implemented in - NetworkX. It works with weighted graphs, but no negative weight is - allowed. - - - ``'Dijkstra_Boost'`` - the Dijkstra algorithm, implemented in Boost - (works only with positive weights). - - - ``'Johnson_Boost'`` - the Johnson algorithm, implemented in - Boost (works also with negative weights, if there is no negative - cycle). - - - ``'From_Dictionary'`` - uses the (already computed) distances, that - are provided by input variable ``dist_dict``. - - - ``None`` (default): Sage chooses the best algorithm: - ``'From_Dictionary'`` if ``dist_dict`` is not None, ``'BFS'`` for - unweighted graphs, ``'Dijkstra_Boost'`` if all weights are - positive, ``'Johnson_Boost'`` otherwise. - - - ``weight_function`` -- function (default: ``None``); a function that - takes as input an edge ``(u, v, l)`` and outputs its weight. If not - ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. - - - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check - that the ``weight_function`` outputs a number for each edge - - - ``dist_dict`` -- a dictionary (default: ``None``); a dict of dicts of - distances (used only if ``algorithm=='From_Dictionary'``) - - - ``with_labels`` -- boolean (default: ``False``); whether to return a - list or a dictionary keyed by vertices. - - EXAMPLES:: - - sage: G = graphs.KrackhardtKiteGraph() - sage: G.eccentricity() - [4, 4, 4, 4, 4, 3, 3, 2, 3, 4] - sage: G.vertices() - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - sage: G.eccentricity(7) - 2 - sage: G.eccentricity([7,8,9]) - [2, 3, 4] - sage: G.eccentricity([7,8,9], with_labels=True) == {8: 3, 9: 4, 7: 2} - True - sage: G = Graph( { 0 : [], 1 : [], 2 : [1] } ) - sage: G.eccentricity() - [+Infinity, +Infinity, +Infinity] - sage: G = Graph({0:[]}) - sage: G.eccentricity(with_labels=True) - {0: 0} - sage: G = Graph({0:[], 1:[]}) - sage: G.eccentricity(with_labels=True) - {0: +Infinity, 1: +Infinity} - sage: G = Graph([(0,1,1), (1,2,1), (0,2,3)]) - sage: G.eccentricity(algorithm = 'BFS') - [1, 1, 1] - sage: G.eccentricity(algorithm = 'Floyd-Warshall-Cython') - [1, 1, 1] - sage: G.eccentricity(by_weight = True, algorithm = 'Dijkstra_NetworkX') - [2, 1, 2] - sage: G.eccentricity(by_weight = True, algorithm = 'Dijkstra_Boost') - [2, 1, 2] - sage: G.eccentricity(by_weight = True, algorithm = 'Johnson_Boost') - [2, 1, 2] - sage: G.eccentricity(by_weight = True, algorithm = 'Floyd-Warshall-Python') - [2, 1, 2] - sage: G.eccentricity(dist_dict = G.shortest_path_all_pairs(by_weight = True)[0]) - [2, 1, 2] - - TESTS: - - A non-implemented algorithm:: - - sage: G.eccentricity(algorithm = 'boh') - Traceback (most recent call last): - ... - ValueError: unknown algorithm "boh" - - An algorithm that does not work with edge weights:: - - sage: G.eccentricity(by_weight = True, algorithm = 'BFS') - Traceback (most recent call last): - ... - ValueError: algorithm 'BFS' does not work with weights - sage: G.eccentricity(by_weight = True, algorithm = 'Floyd-Warshall-Cython') - Traceback (most recent call last): - ... - ValueError: algorithm 'Floyd-Warshall-Cython' does not work with weights - - An algorithm that computes the all-pair-shortest-paths when not all - vertices are needed:: - - sage: G.eccentricity(0, algorithm = 'Floyd-Warshall-Cython') - Traceback (most recent call last): - ... - ValueError: algorithm 'Floyd-Warshall-Cython' works only if all eccentricities are needed - sage: G.eccentricity(0, algorithm = 'Floyd-Warshall-Python') - Traceback (most recent call last): - ... - ValueError: algorithm 'Floyd-Warshall-Python' works only if all eccentricities are needed - sage: G.eccentricity(0, algorithm = 'Johnson_Boost') - Traceback (most recent call last): - ... - ValueError: algorithm 'Johnson_Boost' works only if all eccentricities are needed - """ - if weight_function is not None: - by_weight = True - elif by_weight: - def weight_function(e): - return e[2] - - if algorithm is None: - if dist_dict is not None: - algorithm = 'From_Dictionary' - elif not by_weight: - algorithm = 'BFS' - else: - for e in self.edge_iterator(): - try: - if float(weight_function(e)) < 0: - algorithm = 'Johnson_Boost' - break - except (ValueError, TypeError): - raise ValueError("the weight function cannot find the" - " weight of " + str(e)) - if algorithm is None: - algorithm = 'Dijkstra_Boost' - - if v is None: - # If we want to use BFS, we use the Cython routine - if algorithm == 'BFS': - if by_weight: - raise ValueError("algorithm 'BFS' does not work with weights") - from sage.graphs.distances_all_pairs import eccentricity - algo = 'standard' if self.is_directed() else 'bounds' - if with_labels: - vertex_list = list(self) - return dict(zip(vertex_list, eccentricity(self, algorithm=algo, vertex_list=vertex_list))) - else: - return eccentricity(self, algorithm=algo) - - if algorithm in ['Floyd-Warshall-Python', 'Floyd-Warshall-Cython', 'Johnson_Boost']: - dist_dict = self.shortest_path_all_pairs(by_weight, algorithm, - weight_function, - check_weight)[0] - algorithm = 'From_Dictionary' - - v = self.vertices() - - elif algorithm in ['Floyd-Warshall-Python', 'Floyd-Warshall-Cython', 'Johnson_Boost']: - raise ValueError("algorithm '" + algorithm + "' works only if all" + - " eccentricities are needed") - - if not isinstance(v, list): - v = [v] - ecc = {} - - from sage.rings.infinity import Infinity - - for u in v: - if algorithm == 'From_Dictionary': - length = dist_dict[u] - else: - # If algorithm is wrong, the error is raised by the - # shortest_path_lengths function - length = self.shortest_path_lengths(u, by_weight=by_weight, - algorithm=algorithm, - weight_function=weight_function, - check_weight=check_weight) - - if len(length) != self.num_verts(): - ecc[u] = Infinity - else: - ecc[u] = max(length.values()) - - if with_labels: - return ecc - else: - if len(ecc) == 1: - # return single value - v, = ecc.values() - return v - return [ecc[u] for u in v] - - def radius(self, by_weight=False, algorithm=None, weight_function=None, - check_weight=True): - r""" - Return the radius of the (di)graph. - - The radius is defined to be the minimum eccentricity of any vertex, - where the eccentricity is the maximum distance to any other - vertex. For more information and examples on how to use input variables, - see :meth:`~GenericGraph.shortest_paths` and - :meth:`~GenericGraph.eccentricity` - - INPUT: - - - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge - weights are taken into account; if False, all edges have weight 1 - - - ``algorithm`` -- string (default: ``None``); see method - :meth:`eccentricity` for the list of available algorithms - - - ``weight_function`` -- function (default: ``None``); a function that - takes as input an edge ``(u, v, l)`` and outputs its weight. If not - ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. - - - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check - that the ``weight_function`` outputs a number for each edge - - EXAMPLES: - - The more symmetric a graph is, the smaller (diameter - radius) is:: - - sage: G = graphs.BarbellGraph(9, 3) - sage: G.radius() - 3 - sage: G.diameter() - 6 - - :: - - sage: G = graphs.OctahedralGraph() - sage: G.radius() - 2 - sage: G.diameter() - 2 - - TESTS:: - - sage: g = Graph() - sage: g.radius() - Traceback (most recent call last): - ... - ValueError: radius is not defined for the empty graph - """ - if not self.order(): - raise ValueError("radius is not defined for the empty graph") - - return min(self.eccentricity(v=list(self), by_weight=by_weight, - weight_function=weight_function, - check_weight=check_weight, - algorithm=algorithm)) - - def diameter(self, by_weight=False, algorithm=None, weight_function=None, - check_weight=True): - r""" - Return the diameter of the (di)graph. - - The diameter is defined to be the maximum distance between two vertices. - It is infinite if the (di)graph is not (strongly) connected. - - For more information and examples on how to use input variables, see - :meth:`~GenericGraph.shortest_paths` and - :meth:`~GenericGraph.eccentricity` - - INPUT: - - - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge - weights are taken into account; if False, all edges have weight 1 - - - ``algorithm`` -- string (default: ``None``); one of the following - algorithms: - - - ``'BFS'``: the computation is done through a BFS centered on each - vertex successively. Works only if ``by_weight==False``. - - - ``'Floyd-Warshall-Cython'``: a Cython implementation of the - Floyd-Warshall algorithm. Works only if ``by_weight==False`` and ``v - is None``. - - - ``'Floyd-Warshall-Python'``: a Python implementation of the - Floyd-Warshall algorithm. Works also with weighted graphs, even with - negative weights (but no negative cycle is allowed). However, ``v`` - must be ``None``. - - - ``'Dijkstra_NetworkX'``: the Dijkstra algorithm, implemented in - NetworkX. It works with weighted graphs, but no negative weight is - allowed. - - - ``'standard'``, ``'2sweep'``, ``'multi-sweep'``, ``'iFUB'``: these - algorithms are implemented in - :func:`sage.graphs.distances_all_pairs.diameter` - They work only if ``by_weight==False``. See the function - documentation for more information. - - - ``'Dijkstra_Boost'``: the Dijkstra algorithm, implemented in Boost - (works only with positive weights). - - - ``'Johnson_Boost'``: the Johnson algorithm, implemented in - Boost (works also with negative weights, if there is no negative - cycle). - - - ``None`` (default): Sage chooses the best algorithm: ``'iFUB'`` for - unweighted graphs, ``'Dijkstra_Boost'`` if all weights are positive, - ``'Johnson_Boost'`` otherwise. - - - ``weight_function`` -- function (default: ``None``); a function that - takes as input an edge ``(u, v, l)`` and outputs its weight. If not - ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. - - - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check - that the ``weight_function`` outputs a number for each edge - - EXAMPLES: - - The more symmetric a graph is, the smaller (diameter - radius) is:: - - sage: G = graphs.BarbellGraph(9, 3) - sage: G.radius() - 3 - sage: G.diameter() - 6 - - :: - - sage: G = graphs.OctahedralGraph() - sage: G.radius() - 2 - sage: G.diameter() - 2 - - TESTS:: - - sage: g = Graph() - sage: g.diameter() - Traceback (most recent call last): - ... - ValueError: diameter is not defined for the empty graph - sage: g = Graph([(1, 2, {'weight': 1})]) - sage: g.diameter(algorithm='iFUB', weight_function=lambda e: e[2]['weight']) - Traceback (most recent call last): - ... - ValueError: algorithm 'iFUB' does not work on weighted graphs - """ - if not self.order(): - raise ValueError("diameter is not defined for the empty graph") - - if weight_function is not None: - by_weight = True - - if algorithm is None and not by_weight: - algorithm = 'iFUB' - elif algorithm == 'BFS': - algorithm = 'standard' - - if algorithm in ['standard', '2sweep', 'multi-sweep', 'iFUB']: - if by_weight: - raise ValueError("algorithm '" + algorithm + "' does not work" + - " on weighted graphs") - from sage.graphs.distances_all_pairs import diameter - return diameter(self, algorithm=algorithm) - - return max(self.eccentricity(v=list(self), by_weight=by_weight, - weight_function=weight_function, - check_weight=check_weight, - algorithm=algorithm)) - - def center(self, by_weight=False, algorithm=None, weight_function=None, - check_weight=True): - r""" - Return the set of vertices in the center of the (di)graph. - - The center is the set of vertices whose eccentricity is equal to the - radius of the (di)graph, i.e., achieving the minimum eccentricity. - - For more information and examples on how to use input variables, - see :meth:`~GenericGraph.shortest_paths` and - :meth:`~GenericGraph.eccentricity` - - INPUT: - - - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge - weights are taken into account; if False, all edges have weight 1 - - - ``algorithm`` -- string (default: ``None``); see method - :meth:`eccentricity` for the list of available algorithms - - - ``weight_function`` -- function (default: ``None``); a function that - takes as input an edge ``(u, v, l)`` and outputs its weight. If not - ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. - - - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check - that the ``weight_function`` outputs a number for each edge - - EXAMPLES: - - Is Central African Republic in the center of Africa in graph theoretic - sense? Yes:: - - sage: A = graphs.AfricaMap(continental=True) - sage: sorted(A.center()) - ['Cameroon', 'Central Africa'] - - Some other graphs. Center can be the whole graph:: - - sage: G = graphs.DiamondGraph() - sage: G.center() - [1, 2] - sage: P = graphs.PetersenGraph() - sage: P.subgraph(P.center()) == P - True - sage: S = graphs.StarGraph(19) - sage: S.center() - [0] - - TESTS:: - - sage: G = Graph() - sage: G.center() - [] - sage: G.add_vertex() - 0 - sage: G.center() - [0] - """ - ecc = self.eccentricity(v=list(self), by_weight=by_weight, - weight_function=weight_function, - algorithm=algorithm, - check_weight=check_weight, - with_labels=True) - try: - r = min(ecc.values()) - except Exception: - return [] - return [v for v in self if ecc[v] == r] - - def distance_graph(self, dist): r""" Return the graph on the same vertex set as the original graph but @@ -15452,66 +15012,6 @@ def _girth_bfs(self, odd=False, certificate=False): else: return best - - def periphery(self, by_weight=False, algorithm=None, weight_function=None, - check_weight=True): - r""" - Return the set of vertices in the periphery of the (di)graph. - - The periphery is the set of vertices whose eccentricity is equal to the - diameter of the (di)graph, i.e., achieving the maximum eccentricity. - - For more information and examples on how to use input variables, - see :meth:`~GenericGraph.shortest_paths` and - :meth:`~GenericGraph.eccentricity` - - INPUT: - - - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge - weights are taken into account; if False, all edges have weight 1 - - - ``algorithm`` -- string (default: ``None``); see method - :meth:`eccentricity` for the list of available algorithms - - - ``weight_function`` -- function (default: ``None``); a function that - takes as input an edge ``(u, v, l)`` and outputs its weight. If not - ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. - - - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check - that the ``weight_function`` outputs a number for each edge - - EXAMPLES:: - - sage: G = graphs.DiamondGraph() - sage: G.periphery() - [0, 3] - sage: P = graphs.PetersenGraph() - sage: P.subgraph(P.periphery()) == P - True - sage: S = graphs.StarGraph(19) - sage: S.periphery() - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] - sage: G = Graph() - sage: G.periphery() - [] - sage: G.add_vertex() - 0 - sage: G.periphery() - [0] - """ - ecc = self.eccentricity(v=list(self), by_weight=by_weight, - weight_function=weight_function, - algorithm=algorithm, - check_weight=check_weight, - with_labels=True) - try: - d = max(ecc.values()) - except Exception: - return [] - return [v for v in self if ecc[v] == d] - ### Centrality def centrality_betweenness(self, k=None, normalized=True, weight=None, @@ -15687,7 +15187,7 @@ def centrality_closeness(self, vert=None, by_weight=False, algorithm=None, takes as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. + weight, if ``l`` is not ``None``, else ``1`` as a weight. - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check that the ``weight_function`` outputs a number for each edge. @@ -15815,7 +15315,8 @@ def centrality_closeness(self, vert=None, by_weight=False, algorithm=None, if weight_function is not None: by_weight=True elif by_weight: - weight_function = lambda e: e[2] + def weight_function(e): + return 1 if e[2] is None else e[2] onlyone = False if vert in self: @@ -15860,7 +15361,7 @@ def centrality_closeness(self, vert=None, by_weight=False, algorithm=None, degree = self.out_degree if self.is_directed() else self.degree if vert is None: closeness = networkx.closeness_centrality(G, vert, distance='weight' if by_weight else None) - return {v: c for v, c in iteritems(closeness) if degree(v)} + return {v: c for v, c in closeness.items() if degree(v)} closeness = {} for x in v_iter: if degree(x): @@ -16010,11 +15511,11 @@ def triangles_count(self, algorithm=None): return Integer(tr // 6) elif algorithm == "sparse_copy": from sage.graphs.base.static_sparse_graph import triangles_count - return sum(itervalues(triangles_count(self))) // 3 + return sum(triangles_count(self).values()) // 3 elif algorithm == "dense_copy": from sage.graphs.base.static_dense_graph import triangles_count - return sum(itervalues(triangles_count(self))) // 3 - elif algorithm=='matrix': + return sum(triangles_count(self).values()) // 3 + elif algorithm == 'matrix': return (self.adjacency_matrix(vertices=list(self))**3).trace() // 6 else: raise ValueError('unknown algorithm "{}"'.format(algorithm)) @@ -16075,8 +15576,8 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, - ``weight_function`` -- function (default: ``None``); a function that takes as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. + and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l`` + is not ``None``, else ``1`` as a weight. - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check that the weight_function outputs a number for each edge @@ -16172,10 +15673,6 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, return all_paths[v] return [] - if weight_function is None and by_weight: - def weight_function(e): - return e[2] - if u == v: # to avoid a NetworkX bug return [u] @@ -16183,6 +15680,11 @@ def weight_function(e): if algorithm == 'BFS_Bid': raise ValueError("the 'BFS_Bid' algorithm does not " "work on weighted graphs") + + if not weight_function: + def weight_function(e): + return 1 if e[2] is None else e[2] + if check_weight: self._check_weight_function(weight_function) else: @@ -16263,8 +15765,8 @@ def shortest_path_length(self, u, v, by_weight=False, algorithm=None, - ``weight_function`` -- function (default: ``None``); a function that takes as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. + and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l`` + is not ``None``, else ``1`` as a weight. - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check that the weight_function outputs a number for each edge @@ -16361,10 +15863,6 @@ def shortest_path_length(self, u, v, by_weight=False, algorithm=None, if algorithm is None: algorithm = 'Dijkstra_Bid' if by_weight else 'BFS_Bid' - if weight_function is None and by_weight: - def weight_function(e): - return e[2] - if algorithm in ['BFS', 'Dijkstra_NetworkX', 'Bellman-Ford_Boost']: all_path_lengths = self.shortest_path_lengths(u, by_weight, algorithm, weight_function, check_weight) if v in all_path_lengths: @@ -16376,6 +15874,11 @@ def weight_function(e): if algorithm == 'BFS_Bid': raise ValueError("the 'BFS_Bid' algorithm does not " "work on weighted graphs") + + if not weight_function: + def weight_function(e): + return 1 if e[2] is None else e[2] + if check_weight: self._check_weight_function(weight_function) else: @@ -16499,8 +16002,8 @@ def shortest_paths(self, u, by_weight=False, algorithm=None, - ``weight_function`` -- function (default: ``None``); a function that takes as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. + and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l`` + is not ``None``, else ``1`` as a weight. - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check that the weight_function outputs a number for each edge @@ -16607,7 +16110,7 @@ def shortest_paths(self, u, by_weight=False, algorithm=None, by_weight = True elif by_weight: def weight_function(e): - return e[2] + return 1 if e[2] is None else e[2] else: def weight_function(e): return 1 @@ -16682,7 +16185,7 @@ def _path_length(self, path, by_weight=False, weight_function=None): takes as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. + weight, if ``l`` is not ``None``, else ``1`` as a weight. EXAMPLES: @@ -16720,7 +16223,8 @@ def _path_length(self, path, by_weight=False, weight_function=None): if by_weight or weight_function is not None: if weight_function is None: - weight_function = lambda e: e[2] + def weight_function(e): + return 1 if e[2] is None else e[2] wt = 0 for u, v in zip(path[:-1], path[1:]): @@ -16771,8 +16275,8 @@ def shortest_path_lengths(self, u, by_weight=False, algorithm=None, - ``weight_function`` -- function (default: ``None``); a function that takes as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. + and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l`` + is not ``None``, else ``1`` as a weight. - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check that the weight_function outputs a number for each edge @@ -16831,7 +16335,7 @@ def shortest_path_lengths(self, u, by_weight=False, algorithm=None, by_weight = True elif by_weight: def weight_function(e): - return e[2] + return 1 if e[2] is None else e[2] else: def weight_function(e): return 1 @@ -16867,7 +16371,6 @@ def weight_function(e): return networkx.single_source_dijkstra_path_length(G, u) elif algorithm in ['Dijkstra_Boost', 'Bellman-Ford_Boost', None]: - self.weighted(True) from sage.graphs.base.boost_graph import shortest_paths return shortest_paths(self, u, weight_function, algorithm)[0] @@ -16918,8 +16421,8 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, - ``weight_function`` -- function (default: ``None``); a function that takes as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. + and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l`` + is not ``None``, else ``1`` as a weight. - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check that the weight_function outputs a number for each edge @@ -17120,9 +16623,17 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, """ if weight_function is not None: by_weight = True - elif by_weight: + + if by_weight: + if not weight_function: + def weight_function(e): + return 1 if e[2] is None else e[2] + + if check_weight: + self._check_weight_function(weight_function) + else: def weight_function(e): - return e[2] + return 1 if algorithm is None: if by_weight: @@ -17152,16 +16663,10 @@ def weight_function(e): return floyd_warshall(self, distances=True) elif algorithm == "Floyd-Warshall_Boost": - if not by_weight: - def weight_function(e): - return 1 from sage.graphs.base.boost_graph import floyd_warshall_shortest_paths return floyd_warshall_shortest_paths(self, weight_function, distances=True, predecessors=True) elif algorithm == "Johnson_Boost": - if not by_weight: - def weight_function(e): - return 1 from sage.graphs.base.boost_graph import johnson_shortest_paths return johnson_shortest_paths(self, weight_function, distances=True, predecessors=True) @@ -17169,9 +16674,6 @@ def weight_function(e): from sage.graphs.base.boost_graph import shortest_paths dist = dict() pred = dict() - if by_weight and weight_function is None: - def weight_function(e): - return e[2] for u in self: dist[u], pred[u] = shortest_paths(self, u, weight_function, algorithm) return dist, pred @@ -17185,9 +16687,9 @@ def weight_function(e): weight_function=weight_function) dist[u] = {v: self._path_length(p, by_weight=by_weight, weight_function=weight_function) - for v, p in iteritems(paths)} + for v, p in paths.items()} pred[u] = {v: None if len(p) <= 1 else p[1] - for v, p in iteritems(paths)} + for v, p in paths.items()} return dist, pred elif algorithm != "Floyd-Warshall-Python": @@ -17195,13 +16697,6 @@ def weight_function(e): from sage.rings.infinity import Infinity - if by_weight: - if weight_function is None: - def weight_function(e): - return e[2] - if check_weight: - self._check_weight_function(weight_function) - if self.is_directed(): neighbor = self.neighbor_out_iterator else: @@ -17293,7 +16788,7 @@ def wiener_index(self, by_weight=False, algorithm=None, takes as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. + weight, if ``l`` is not ``None``, else ``1`` as a weight. - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check that the weight_function outputs a number for each edge @@ -17381,8 +16876,8 @@ def average_distance(self, by_weight=False, algorithm=None, - ``weight_function`` -- function (default: ``None``); a function that takes as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` - and ``by_weight`` is ``True``, we use the edge label ``l`` as a - weight. + and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l`` + is not ``None``, else ``1`` as a weight. - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check that the weight_function outputs a number for each edge @@ -17677,7 +17172,7 @@ def breadth_first_search(self, start, ignore_direction=False, yield w def depth_first_search(self, start, ignore_direction=False, - distance=None, neighbors=None): + neighbors=None, edges=False): """ Return an iterator over the vertices in a depth-first ordering. @@ -17690,14 +17185,16 @@ def depth_first_search(self, start, ignore_direction=False, directed graphs. If ``True``, searches across edges in either direction. - - ``distance`` -- Deprecated. Broken, do not use. - - ``neighbors`` -- function (default: ``None``); a function that inputs a vertex and return a list of vertices. For an undirected graph, ``neighbors`` is by default the :meth:`.neighbors` function. For a digraph, the ``neighbors`` function defaults to the :meth:`~DiGraph.neighbor_out_iterator` function of the graph. + - ``edges`` -- boolean (default: ``False``); whether to return the edges + of the DFS tree in the order of visit or the vertices (default). + Edges are directed in root to leaf orientation of the tree. + .. SEEALSO:: - :meth:`breadth_first_search` @@ -17748,21 +17245,43 @@ def depth_first_search(self, start, ignore_direction=False, sage: list(D.breadth_first_search(5, neighbors=D.neighbors_out)) [5, 33, 6, 34, 7, 35, 8, 9] + You can get edges of the DFS tree instead of the vertices using the + ``edges`` parameter:: + + sage: D = DiGraph({1: [2, 3], 2: [4], 3: [4], 4: [1, 5], 5: [2, 6]}) + sage: list(D.depth_first_search(1, edges=True)) + [(1, 3), (3, 4), (4, 5), (5, 6), (5, 2)] + sage: list(D.depth_first_search(1, ignore_direction=True, edges=True)) + [(1, 4), (4, 5), (5, 6), (5, 2), (4, 3)] + TESTS:: sage: D = DiGraph({1: [0], 2: [0]}) sage: list(D.depth_first_search(0)) [0] - sage: list(D.depth_first_search(0, ignore_direction=True)) - [0, 2, 1] + sage: G = DiGraph([(0, 1), (1, 2), (3, 4), (4, 5)]) + sage: list(G.depth_first_search([0], edges=True)) + [(0, 1), (1, 2)] + sage: list(G.depth_first_search([0, 3], edges=True)) + [(0, 1), (1, 2), (3, 4), (4, 5)] + sage: D = DiGraph({1: [2, 3], 3: [4, 6], 4: [6], 5: [4, 7], 6: [7]}) + sage: list(D.depth_first_search(1)) + [1, 3, 6, 7, 4, 2] + sage: list(D.depth_first_search(1, edges=True)) + [(1, 3), (3, 6), (6, 7), (3, 4), (1, 2)] + sage: list(D.depth_first_search([1, 3], edges=True)) + [(1, 3), (3, 6), (6, 7), (3, 4), (1, 2)] + sage: list(D.depth_first_search([], ignore_direction=True, edges=True)) + [] + sage: list(D.depth_first_search(1, ignore_direction=True)) + [1, 3, 6, 4, 5, 7, 2] + sage: list(D.depth_first_search(1, ignore_direction=True, edges=True)) + [(1, 3), (3, 6), (6, 7), (7, 5), (5, 4), (1, 2)] """ - if distance is not None: - deprecation(19227, "Parameter 'distance' is broken. Do not use.") - # Preferably use the Cython implementation - if (neighbors is None and not isinstance(start, list) and distance is None - and hasattr(self._backend, "depth_first_search")): + if (neighbors is None and not isinstance(start, list) + and hasattr(self._backend, "depth_first_search") and not edges): for v in self._backend.depth_first_search(start, ignore_direction=ignore_direction): yield v else: @@ -17778,15 +17297,26 @@ def depth_first_search(self, start, ignore_direction=False, else: queue = [(start, 0)] - while queue: - v, d = queue.pop() - if v not in seen: - yield v - seen.add(v) - if distance is None or d < distance: + if not edges: + while queue: + v, d = queue.pop() + if v not in seen: + yield v + seen.add(v) for w in neighbors(v): if w not in seen: queue.append((w, d + 1)) + else: + queue = [(None, v, d) for v, d in queue] + while queue: + v, w, d = queue.pop() + if w not in seen: + if v is not None: + yield v, w + seen.add(w) + for x in neighbors(w): + if x not in seen: + queue.append((w, x, d + 1)) ### Constructors @@ -17844,7 +17374,6 @@ def add_clique(self, vertices, loops=False): Using different kinds of iterable container of vertices, :trac:`22906`:: - sage: from six.moves import range sage: G = Graph(4) sage: G.add_clique(G) sage: G.is_clique() @@ -18846,9 +18375,7 @@ def _color_by_label(self, format='hex', as_function=False, default_color="black" We first request the coloring as a function:: sage: f = G._color_by_label(as_function=True) - sage: [f(1), f(2), f(3)] # py2 - ['#ff0000', '#0000ff', '#00ff00'] - sage: [f(1), f(2), f(3)] # py3 + sage: [f(1), f(2), f(3)] ['#0000ff', '#ff0000', '#00ff00'] sage: f = G._color_by_label({1: "blue", 2: "red", 3: "green"}, as_function=True) sage: [f(1), f(2), f(3)] @@ -18862,20 +18389,12 @@ def _color_by_label(self, format='hex', as_function=False, default_color="black" The default output is a dictionary assigning edges to colors:: - sage: G._color_by_label() # py2 - {'#0000ff': [((1,3,2,4), (1,2,4), 2), ...], - '#00ff00': [((1,3,2,4), (1,4)(2,3), 3), ...], - '#ff0000': [((1,3,2,4), (1,3)(2,4), 1), ...]} - sage: G._color_by_label() # py3 + sage: G._color_by_label() {'#0000ff': [((), (1,2), 1), ...], '#00ff00': [((), (3,4), 3), ...], '#ff0000': [((), (2,3), 2), ...]} - sage: G._color_by_label({1: "blue", 2: "red", 3: "green"}) # py2 - {'blue': [((1,3,2,4), (1,3)(2,4), 1), ...], - 'green': [((1,3,2,4), (1,4)(2,3), 3), ...], - 'red': [((1,3,2,4), (1,2,4), 2), ...]} - sage: G._color_by_label({1: "blue", 2: "red", 3: "green"}) # py3 + sage: G._color_by_label({1: "blue", 2: "red", 3: "green"}) {'blue': [((), (1,2), 1), ...], 'green': [((), (3,4), 3), ...], 'red': [((), (2,3), 2), ...]} @@ -18988,8 +18507,8 @@ def layout(self, layout=None, pos=None, dim=2, save_pos=False, **options): - ``layout`` -- string (default: ``None``); specifies a layout algorithm among ``"acyclic"``, ``"acyclic_dummy"``, ``"circular"``, - ``"ranked"``, ``"graphviz"``, ``"planar"``, ``"spring"``, or - ``"tree"`` + ``"ranked"``, ``"graphviz"``, ``"planar"``, ``"spring"``, + ``"forest"`` or ``"tree"`` - ``pos`` -- dictionary (default: ``None``); a dictionary of positions @@ -19051,6 +18570,7 @@ def layout(self, layout=None, pos=None, dim=2, save_pos=False, **options): ....: print("option {} : {}".format(key, value)) option by_component : Whether to do the spring layout by connected component -- a boolean. option dim : The dimension of the layout -- 2 or 3. + option forest_roots : An iterable specifying which vertices to use as roots for the ``layout='forest'`` option. If no root is specified for a tree, then one is chosen close to the center of the tree. Ignored unless ``layout='forest'``. option heights : A dictionary mapping heights to the list of vertices at this height. option iterations : The number of times to execute the spring layout algorithm. option layout : A layout algorithm -- one of : "acyclic", "circular" (plots the graph with vertices evenly distributed on a circle), "ranked", "graphviz", "planar", "spring" (traditional spring layout, using the graph's current positions as initial positions), or "tree" (the tree will be plotted in levels, depending on minimum distance for the root). @@ -19058,7 +18578,7 @@ def layout(self, layout=None, pos=None, dim=2, save_pos=False, **options): option save_pos : Whether or not to save the computed position for the graph. option spring : Use spring layout to finalize the current layout. option tree_orientation : The direction of tree branches -- 'up', 'down', 'left' or 'right'. - option tree_root : A vertex designation for drawing trees. A vertex of the tree to be used as the root for the ``layout='tree'`` option. If no root is specified, then one is chosen close to the center of the tree. Ignored unless ``layout='tree'`` + option tree_root : A vertex designation for drawing trees. A vertex of the tree to be used as the root for the ``layout='tree'`` option. If no root is specified, then one is chosen close to the center of the tree. Ignored unless ``layout='tree'``. Some of them only apply to certain layout algorithms. For details, see :meth:`.layout_acyclic`, :meth:`.layout_planar`, @@ -19118,13 +18638,13 @@ def layout_spring(self, by_component=True, **options): EXAMPLES:: sage: g = graphs.LadderGraph(3) #TODO!!!! - sage: g.layout_spring() - {0: [0.73..., -0.29...], - 1: [1.37..., 0.30...], - 2: [2.08..., 0.89...], - 3: [1.23..., -0.83...], - 4: [1.88..., -0.30...], - 5: [2.53..., 0.22...]} + sage: g.layout_spring() # random + {0: [1.0, -0.29...], + 1: [1.64..., 0.30...], + 2: [2.34..., 0.89...], + 3: [1.49..., -0.83...], + 4: [2.14..., -0.30...], + 5: [2.80..., 0.22...]} sage: g = graphs.LadderGraph(7) sage: g.plot(layout="spring") Graphics object consisting of 34 graphics primitives @@ -19158,7 +18678,7 @@ def layout_ranked(self, heights=None, dim=2, spring=False, **options): EXAMPLES:: sage: g = graphs.LadderGraph(3) - sage: g.layout_ranked(heights={i: (i, i+3) for i in range(3)}) + sage: g.layout_ranked(heights={i: (i, i+3) for i in range(3)}) # random {0: [0.668..., 0], 1: [0.667..., 1], 2: [0.677..., 2], @@ -19303,12 +18823,78 @@ def layout_circular(self, dim=2, center=(0, 0), radius=1, shift=0, angle=0, **op return self._circle_embedding(self.vertices(), center=(0, 0), radius=1, shift=0, angle=pi/2, return_dict=True) + def layout_forest(self, tree_orientation="down", forest_roots=None, + **options): + """ + Return an ordered forest layout for this graph. + + The function relies on :meth:`~GenericGraph.layout_tree` to deal with + each connected component. + + INPUT: + + - ``forest_roots`` -- an iterable of vertices (default: ``None``); + the root vertices of the trees in the forest; a vertex is chosen + close to the center of each component for which no root is specified + in ``forest_roots`` or if ``forest_roots`` is ``None`` + + - ``tree_orientation`` -- string (default: ``'down'``); the direction in + which the tree is growing, can be ``'up'``, ``'down'``, ``'left'`` or + ``'right'`` + + - ``**options`` -- other parameters ignored here + + EXAMPLES:: + + sage: G = graphs.RandomTree(4) + graphs.RandomTree(5) + graphs.RandomTree(6) + sage: p = G.layout_forest() + sage: G.plot(pos=p) # random + Graphics object consisting of 28 graphics primitives + + sage: H = graphs.PathGraph(5) + graphs.PathGraph(5) + graphs.BalancedTree(2,2) + sage: p = H.layout_forest(forest_roots=[14,3]) + sage: H.plot(pos=p) + Graphics object consisting of 32 graphics primitives + + TESTS:: + + sage: G = Graph(0) + sage: G.plot(layout='forest') + Graphics object consisting of 0 graphics primitives + + Works for forests that are trees:: + + sage: g = graphs.StarGraph(4) + sage: p = g.layout_forest(forest_roots=[1]) + sage: sorted(p.items()) + [(0, [2.0, -1]), (1, [2.0, 0]), (2, [3.0, -2]), (3, [2.0, -2]), (4, [1.0, -2])] + + The parameter ``forest_roots`` should be an iterable (or ``None``):: + + sage: H = graphs.PathGraph(5) + sage: p = H.layout_forest(forest_roots=3) + Traceback (most recent call last): + ... + TypeError: forest_roots should be an iterable of vertices + """ + if not self: + return dict() + else: + # Compute the layout component by component + return layout_split(self.__class__.layout_tree, + self, + tree_orientation=tree_orientation, + forest_roots=forest_roots, + **options) + def layout_tree(self, tree_orientation="down", tree_root=None, dim=2, **options): r""" Return an ordered tree layout for this graph. - The graph must be a tree (no non-oriented cycles). + The graph must be a tree (no non-oriented cycles). In case of doubt + whether the graph is connected or not, prefer + :meth:`~GenericGraph.layout_forest`. INPUT: @@ -19347,13 +18933,13 @@ def layout_tree(self, tree_orientation="down", tree_root=None, sage: G = graphs.BalancedTree(2, 2) sage: G.layout_tree(tree_root=0) - {0: (1.5, 0), - 1: (2.5, -1), - 2: (0.5, -1), - 3: (3.0, -2), - 4: (2.0, -2), - 5: (1.0, -2), - 6: (0.0, -2)} + {0: [1.5, 0], + 1: [2.5, -1], + 2: [0.5, -1], + 3: [3.0, -2], + 4: [2.0, -2], + 5: [1.0, -2], + 6: [0.0, -2]} sage: G = graphs.BalancedTree(2, 4) sage: G.plot(layout="tree", tree_root=0, tree_orientation="up") @@ -19365,29 +18951,45 @@ def layout_tree(self, tree_orientation="down", tree_root=None, sage: T.set_embedding({0: [1, 6, 3], 1: [2, 5, 0], 2: [1], 3: [4, 7, 8, 0], ....: 4: [3], 5: [1], 6: [0], 7: [3], 8: [3]}) sage: T.layout_tree() - {0: (2.166..., 0), - 1: (3.5, -1), - 2: (4.0, -2), - 3: (1.0, -1), - 4: (2.0, -2), - 5: (3.0, -2), - 6: (2.0, -1), - 7: (1.0, -2), - 8: (0.0, -2)} + {0: [2.166..., 0], + 1: [3.5, -1], + 2: [4.0, -2], + 3: [1.0, -1], + 4: [2.0, -2], + 5: [3.0, -2], + 6: [2.0, -1], + 7: [1.0, -2], + 8: [0.0, -2]} sage: T.plot(layout="tree", tree_root=3) Graphics object consisting of 18 graphics primitives TESTS:: + sage: G = graphs.BalancedTree(2, 2) + sage: G.layout_tree(tree_root=0, tree_orientation='left') + {0: [0, 1.5], + 1: [-1, 2.5], + 2: [-1, 0.5], + 3: [-2, 3.0], + 4: [-2, 2.0], + 5: [-2, 1.0], + 6: [-2, 0.0]} + sage: G = graphs.CycleGraph(3) sage: G.plot(layout='tree') Traceback (most recent call last): ... RuntimeError: cannot use tree layout on this graph: self.is_tree() returns False + sage: G = Graph(0) + sage: G.plot(layout='tree') + Graphics object consisting of 0 graphics primitives """ if dim != 2: raise ValueError('only implemented in 2D') + if not self: + return dict() + from sage.graphs.all import Graph if not Graph(self).is_tree(): raise RuntimeError("cannot use tree layout on this graph: " @@ -19444,7 +19046,7 @@ def slide(v, dx): x, y = pos[u] x += dx obstruction[y] = max(x + 1, obstruction[y]) - pos[u] = x, y + pos[u] = [x, y] nextlevel += children[u] level = nextlevel @@ -19463,12 +19065,12 @@ def slide(v, dx): # If p has no children, we draw it at the leftmost position # which has not been forbidden x = obstruction[y] - pos[p] = x, y + pos[p] = [x, y] else: # If p has children, we put v on a vertical line going # through the barycenter of its children x = sum(pos[c][0] for c in cp) / len(cp) - pos[p] = x, y + pos[p] = [x, y] ox = obstruction[y] if x < ox: slide(p, ox - x) @@ -19500,7 +19102,7 @@ def slide(v, dx): stick.append(t) if tree_orientation in ['right', 'left']: - return {p: (py, px) for p, (px, py) in iteritems(pos)} + return {p: [py, px] for p, [px, py] in pos.items()} return pos @@ -19593,7 +19195,7 @@ def layout_graphviz(self, dim=2, prog='dot', **options): import dot2tex positions = dot2tex.dot2tex(self.graphviz_string(**options), format="positions", prog=prog) - return {key_to_vertex[key]: pos for key, pos in iteritems(positions)} + return {key_to_vertex[key]: pos for key, pos in positions.items()} def _layout_bounding_box(self, pos): """ @@ -20913,23 +20515,7 @@ def graphviz_string(self, **options): The following digraph has tuples as vertices:: - sage: print(DiGraph(graphs.Grid2dGraph(2,2)).graphviz_string()) # py2 - digraph { - node_0 [label="(0, 1)"]; - node_1 [label="(1, 0)"]; - node_2 [label="(0, 0)"]; - node_3 [label="(1, 1)"]; - - node_0 -> node_2; - node_0 -> node_3; - node_1 -> node_2; - node_1 -> node_3; - node_2 -> node_0; - node_2 -> node_1; - node_3 -> node_0; - node_3 -> node_1; - } - sage: print(DiGraph(graphs.Grid2dGraph(2,2)).graphviz_string()) # py3 + sage: print(DiGraph(graphs.Grid2dGraph(2,2)).graphviz_string()) digraph { node_0 [label="(0, 0)"]; node_1 [label="(0, 1)"]; @@ -21815,7 +21401,7 @@ def relabel(self, perm=None, inplace=True, return_map=False, check_input=True, c for attr in attributes_to_update: if hasattr(self, attr) and getattr(self, attr) is not None: new_attr = {} - for v, value in iteritems(getattr(self, attr)): + for v, value in getattr(self, attr).items(): if attr != '_embedding': new_attr[perm[v]] = value else: @@ -22374,7 +21960,7 @@ def automorphism_group(self, partition=None, verbosity=0, # We relabel the cycles using the vertices' names instead of integers n = self.order() - int_to_vertex = {((i + 1) if i != n else 1): v for v, i in iteritems(b)} + int_to_vertex = {((i + 1) if i != n else 1): v for v, i in b.items()} gens = [[tuple(int_to_vertex[i] for i in cycle) for cycle in gen] for gen in gens] output.append(PermutationGroup(gens=gens, domain=int_to_vertex.values())) else: @@ -22822,7 +22408,7 @@ def is_isomorphic(self, other, certificate=False, verbosity=0, edge_labels=False isom_trans[v] = other_vertices[isom[G_to[v]]] return True, isom_trans - def canonical_label(self, partition=None, certificate=False, verbosity=0, + def canonical_label(self, partition=None, certificate=False, edge_labels=False, algorithm=None, return_graph=True): r""" Return the canonical graph. @@ -22864,8 +22450,6 @@ class by some canonization function `c`. If `G` and `H` are graphs, instead of the canonical graph; only available when ``'bliss'`` is explicitly set as algorithm. - - ``verbosity`` -- deprecated, does nothing - EXAMPLES: Canonization changes isomorphism to equality:: @@ -22884,9 +22468,7 @@ class by some canonization function `c`. If `G` and `H` are graphs, sage: g, c = g1.canonical_label(algorithm='sage', certificate=True) sage: g Grid Graph for [2, 3]: Graph on 6 vertices - sage: c # py2 - {(0, 0): 2, (0, 1): 4, (0, 2): 3, (1, 0): 1, (1, 1): 5, (1, 2): 0} - sage: c # py3 + sage: c {(0, 0): 3, (0, 1): 4, (0, 2): 2, (1, 0): 0, (1, 1): 5, (1, 2): 1} Multigraphs and directed graphs work too:: @@ -23004,9 +22586,6 @@ class by some canonization function `c`. If `G` and `H` are graphs, ....: assert gcan0 == gcan1, (edges, labels, part, pp) ....: assert gcan0 == gcan2, (edges, labels, part, pp) """ - # Deprecation - if verbosity != 0: - deprecation(19517, "Verbosity-parameter is removed.") # Check parameter combinations if algorithm not in [None, 'sage', 'bliss']: diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index fdfd9694e41..244b59faf53 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,23 +73,77 @@ 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)) + + on_embedding = options.get('on_embedding', None) + forest_roots = options.get('forest_roots', None) + try: + forest_roots = list(forest_roots) if forest_roots else None + except TypeError: + raise TypeError('forest_roots should be an iterable of vertices') + + if forest_roots or on_embedding: + options = copy(options) + options.pop('forest_roots', None) + options.pop('on_embedding', None) + for g in Gs: - cur_pos = spring_layout_fast(g, **options) + if on_embedding: + # Restrict ``on_embedding`` to ``g`` + embedding_g = {v: on_embedding[v] for v in g} + cur_pos = layout_function(g, on_embedding=embedding_g, **options) + elif forest_roots: + # Find a root for ``g`` (if any) + tree_root = next((v for v in forest_roots if v in g), None) + cur_pos = layout_function(g, tree_root=tree_root, **options) + 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: - buffer = (xmax - xmin)/sqrt(len(g)) + buffer = max(1, (xmax - xmin)/sqrt(len(g))) for v, loc in cur_pos.items(): 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 +190,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 @@ -1130,6 +1186,10 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, EXAMPLES: + For demonstration purposes we fix a random seed:: + + sage: set_random_seed(0) + First we try the algorithm in the Dodecahedral graph, which is Hamiltonian, so we are able to find a Hamiltonian cycle and a Hamiltonian path:: diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 5e1b868fda0..9de2c628e93 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -82,7 +82,7 @@ - Amanda Francis, Caitlin Lienkaemper, Kate Collins, Rajat Mittal (2019-03-10): methods for computing effective resistance - + - Amanda Francis, Caitlin Lienkaemper, Kate Collins, Rajat Mittal (2019-03-19): most_common_neighbors and common_neighbors_matrix added. @@ -411,8 +411,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -import six -from six.moves import range import itertools from copy import copy @@ -1128,21 +1126,25 @@ def __init__(self, data=None, pos=None, loops=None, format=None, if format == 'weighted_adjacency_matrix': if weighted is False: raise ValueError("Format was weighted_adjacency_matrix but weighted was False.") - if weighted is None: weighted = True - if multiedges is None: multiedges = False + if weighted is None: + weighted = True + if multiedges is None: + multiedges = False format = 'adjacency_matrix' # At this point, 'format' has been set. We build the graph if format == 'graph6': - if weighted is None: weighted = False + if weighted is None: + weighted = False self.allow_loops(loops if loops else False, check=False) self.allow_multiple_edges(multiedges if multiedges else False, check=False) from .graph_input import from_graph6 from_graph6(self, data) elif format == 'sparse6': - if weighted is None: weighted = False + if weighted is None: + weighted = False self.allow_loops(False if loops is False else True, check=False) self.allow_multiple_edges(False if multiedges is False else True, check=False) from .graph_input import from_sparse6 @@ -1165,9 +1167,12 @@ def __init__(self, data=None, pos=None, loops=None, format=None, from .graph_input import from_seidel_adjacency_matrix from_seidel_adjacency_matrix(self, data) elif format == 'Graph': - if loops is None: loops = data.allows_loops() - if multiedges is None: multiedges = data.allows_multiple_edges() - if weighted is None: weighted = data.weighted() + if loops is None: + loops = data.allows_loops() + if multiedges is None: + multiedges = data.allows_multiple_edges() + if weighted is None: + weighted = data.weighted() self.allow_loops(loops, check=False) self.allow_multiple_edges(multiedges, check=False) if data.get_pos() is not None: @@ -1215,8 +1220,10 @@ def __init__(self, data=None, pos=None, loops=None, format=None, elif format == 'rule': f = data[1] verts = data[0] - if loops is None: loops = any(f(v,v) for v in verts) - if weighted is None: weighted = False + if loops is None: + loops = any(f(v,v) for v in verts) + if weighted is None: + weighted = False self.allow_loops(loops, check=False) self.allow_multiple_edges(True if multiedges else False, check=False) self.add_vertices(verts) @@ -1255,7 +1262,8 @@ def __init__(self, data=None, pos=None, loops=None, format=None, else: raise ValueError("Unknown input format '{}'".format(format)) - if weighted is None: weighted = False + if weighted is None: + weighted = False self._weighted = getattr(self, '_weighted', weighted) self._pos = copy(pos) @@ -2803,9 +2811,12 @@ def treewidth(self, k=None, certificate=False, algorithm=None): # Stupid cases if not g.order(): - if certificate: return Graph() - elif k is None: return -1 - else: return True + if certificate: + return Graph() + elif k is None: + return -1 + else: + return True if k is not None and k >= g.order() - 1: if certificate: @@ -3859,7 +3870,7 @@ def bipartite_sets(self): left = set() right = set() - for u,s in six.iteritems(color): + for u,s in color.items(): if s: left.add(u) else: @@ -4347,6 +4358,16 @@ def matching(self, value_only=False, algorithm="Edmonds", the method :meth:`sage.numerical.mip.MixedIntegerLinearProgram.solve` of the class :class:`sage.numerical.mip.MixedIntegerLinearProgram`. + OUTPUT: + + - When ``value_only=False`` (default), this method returns the list of + edges of a maximum matching of `G`. + + - When ``value_only=True``, this method returns the sum of the + weights (default: ``1``) of the edges of a maximum matching of `G`. + The type of the output may vary according to the type of the edge + labels and the algorithm used. + ALGORITHM: The problem is solved using Edmond's algorithm implemented in NetworkX, @@ -4809,7 +4830,7 @@ def maximum_average_degree(self, value_only=True, solver=None, verbose=0): # should be safe :-) m = 1/(10 *Integer(g.order())) d_val = p.get_values(d) - g_mad = g.subgraph(v for v,l in six.iteritems(d_val) if l > m) + g_mad = g.subgraph(v for v,l in d_val.items() if l > m) if value_only: return g_mad.average_degree() @@ -5184,6 +5205,628 @@ def centrality_degree(self, v=None): else: return self.degree(v)/n_minus_one + ### Distances + + @doc_index("Distances") + def eccentricity(self, v=None, by_weight=False, algorithm=None, + weight_function=None, check_weight=True, dist_dict=None, + with_labels=False): + """ + Return the eccentricity of vertex (or vertices) ``v``. + + The eccentricity of a vertex is the maximum distance to any other + vertex. + + For more information and examples on how to use input variables, see + :meth:`~GenericGraph.shortest_path_all_pairs`, + :meth:`~GenericGraph.shortest_path_lengths` and + :meth:`~GenericGraph.shortest_paths` + + INPUT: + + - ``v`` - either a single vertex or a list of vertices. If it is not + specified, then it is taken to be all vertices. + + - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge + weights are taken into account; if False, all edges have weight 1 + + - ``algorithm`` -- string (default: ``None``); one of the following + algorithms: + + - ``'BFS'`` - the computation is done through a BFS centered on each + vertex successively. Works only if ``by_weight==False``. + + - ``'DHV'`` - the computation is done using the algorithm proposed in + [Dragan2018]_. Works only if ``self`` has non-negative edge weights + and ``v is None`` or ``v`` should contain all vertices of ``self``. + For more information see method + :func:`sage.graphs.distances_all_pairs.eccentricity` and + :func:`sage.graphs.base.boost_graph.eccentricity_DHV`. + + - ``'Floyd-Warshall-Cython'`` - a Cython implementation of the + Floyd-Warshall algorithm. Works only if ``by_weight==False`` and + ``v is None`` or ``v`` should contain all vertices of ``self``. + + - ``'Floyd-Warshall-Python'`` - a Python implementation of the + Floyd-Warshall algorithm. Works also with weighted graphs, even with + negative weights (but no negative cycle is allowed). However, ``v`` + must be ``None`` or ``v`` should contain all vertices of ``self``. + + - ``'Dijkstra_NetworkX'`` - the Dijkstra algorithm, implemented in + NetworkX. It works with weighted graphs, but no negative weight is + allowed. + + - ``'Dijkstra_Boost'`` - the Dijkstra algorithm, implemented in Boost + (works only with positive weights). + + - ``'Johnson_Boost'`` - the Johnson algorithm, implemented in + Boost (works also with negative weights, if there is no negative + cycle). Works only if ``v is None`` or ``v`` should contain all + vertices of ``self``. + + - ``'From_Dictionary'`` - uses the (already computed) distances, that + are provided by input variable ``dist_dict``. + + - ``None`` (default): Sage chooses the best algorithm: + ``'From_Dictionary'`` if ``dist_dict`` is not None, ``'BFS'`` for + unweighted graphs, ``'Dijkstra_Boost'`` if all weights are + positive, ``'Johnson_Boost'`` otherwise. + + - ``weight_function`` -- function (default: ``None``); a function that + takes as input an edge ``(u, v, l)`` and outputs its weight. If not + ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` + and ``by_weight`` is ``True``, we use the edge label ``l`` as a + weight, if ``l`` is not ``None``, else ``1`` as a weight. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + - ``dist_dict`` -- a dictionary (default: ``None``); a dict of dicts of + distances (used only if ``algorithm=='From_Dictionary'``) + + - ``with_labels`` -- boolean (default: ``False``); whether to return a + list or a dictionary keyed by vertices. + + EXAMPLES:: + + sage: G = graphs.KrackhardtKiteGraph() + sage: G.eccentricity() + [4, 4, 4, 4, 4, 3, 3, 2, 3, 4] + sage: G.vertices() + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + sage: G.eccentricity(7) + 2 + sage: G.eccentricity([7,8,9]) + [2, 3, 4] + sage: G.eccentricity([7,8,9], with_labels=True) == {8: 3, 9: 4, 7: 2} + True + sage: G = Graph( { 0 : [], 1 : [], 2 : [1] } ) + sage: G.eccentricity() + [+Infinity, +Infinity, +Infinity] + sage: G = Graph({0:[]}) + sage: G.eccentricity(with_labels=True) + {0: 0} + sage: G = Graph({0:[], 1:[]}) + sage: G.eccentricity(with_labels=True) + {0: +Infinity, 1: +Infinity} + sage: G = Graph([(0,1,1), (1,2,1), (0,2,3)]) + sage: G.eccentricity(algorithm = 'BFS') + [1, 1, 1] + sage: G.eccentricity(algorithm = 'Floyd-Warshall-Cython') + [1, 1, 1] + sage: G.eccentricity(by_weight = True, algorithm = 'Dijkstra_NetworkX') + [2, 1, 2] + sage: G.eccentricity(by_weight = True, algorithm = 'Dijkstra_Boost') + [2, 1, 2] + sage: G.eccentricity(by_weight = True, algorithm = 'Johnson_Boost') + [2, 1, 2] + sage: G.eccentricity(by_weight = True, algorithm = 'Floyd-Warshall-Python') + [2, 1, 2] + sage: G.eccentricity(dist_dict = G.shortest_path_all_pairs(by_weight = True)[0]) + [2, 1, 2] + sage: G.eccentricity(by_weight = False, algorithm = 'DHV') + [1, 1, 1] + sage: G.eccentricity(by_weight = True, algorithm = 'DHV') + [2.0, 1.0, 2.0] + + TESTS: + + A non-implemented algorithm:: + + sage: G.eccentricity(algorithm = 'boh') + Traceback (most recent call last): + ... + ValueError: unknown algorithm "boh" + + An algorithm that does not work with edge weights:: + + sage: G.eccentricity(by_weight = True, algorithm = 'BFS') + Traceback (most recent call last): + ... + ValueError: algorithm 'BFS' does not work with weights + sage: G.eccentricity(by_weight = True, algorithm = 'Floyd-Warshall-Cython') + Traceback (most recent call last): + ... + ValueError: algorithm 'Floyd-Warshall-Cython' does not work with weights + + An algorithm that computes the all-pair-shortest-paths when not all + vertices are needed:: + + sage: G.eccentricity(0, algorithm = 'Floyd-Warshall-Cython') + Traceback (most recent call last): + ... + ValueError: algorithm 'Floyd-Warshall-Cython' works only if all eccentricities are needed + sage: G.eccentricity(0, algorithm = 'Floyd-Warshall-Python') + Traceback (most recent call last): + ... + ValueError: algorithm 'Floyd-Warshall-Python' works only if all eccentricities are needed + sage: G.eccentricity(0, algorithm = 'Johnson_Boost') + Traceback (most recent call last): + ... + ValueError: algorithm 'Johnson_Boost' works only if all eccentricities are needed + sage: G.eccentricity(0, algorithm = 'DHV') + Traceback (most recent call last): + ... + ValueError: algorithm 'DHV' works only if all eccentricities are needed + """ + if weight_function is not None: + by_weight = True + elif by_weight: + def weight_function(e): + return 1 if e[2] is None else e[2] + + if algorithm is None: + if dist_dict is not None: + algorithm = 'From_Dictionary' + elif not by_weight: + algorithm = 'BFS' + else: + for e in self.edge_iterator(): + try: + if float(weight_function(e)) < 0: + algorithm = 'Johnson_Boost' + break + except (ValueError, TypeError): + raise ValueError("the weight function cannot find the" + " weight of " + str(e)) + if algorithm is None: + algorithm = 'Dijkstra_Boost' + + if v is not None and not isinstance(v, list): + v = [v] + + if v is None or all(u in v for u in self): + if v is None: + v = list(self) + # If we want to use BFS, we use the Cython routine + if algorithm == 'BFS': + if by_weight: + raise ValueError("algorithm 'BFS' does not work with weights") + from sage.graphs.distances_all_pairs import eccentricity + algo = 'bounds' + if with_labels: + return dict(zip(v, eccentricity(self, algorithm=algo, vertex_list=v))) + else: + return eccentricity(self, algorithm=algo,vertex_list=v) + + if algorithm == 'DHV': + if by_weight: + from sage.graphs.base.boost_graph import eccentricity_DHV + if with_labels: + return dict(zip(v, eccentricity_DHV(self, vertex_list=v, + weight_function=weight_function, + check_weight=check_weight))) + else: + return eccentricity_DHV(self, vertex_list=v, + weight_function=weight_function, + check_weight=check_weight) + else: + from sage.graphs.distances_all_pairs import eccentricity + if with_labels: + return dict(zip(v, eccentricity(self, algorithm=algorithm, + vertex_list=v))) + else: + return eccentricity(self, algorithm=algorithm, vertex_list=v) + + if algorithm in ['Floyd-Warshall-Python', 'Floyd-Warshall-Cython', 'Johnson_Boost']: + dist_dict = self.shortest_path_all_pairs(by_weight, algorithm, + weight_function, + check_weight)[0] + algorithm = 'From_Dictionary' + + elif algorithm in ['Floyd-Warshall-Python', 'Floyd-Warshall-Cython', 'Johnson_Boost','DHV']: + raise ValueError("algorithm '" + algorithm + "' works only if all" + + " eccentricities are needed") + + ecc = {} + + from sage.rings.infinity import Infinity + + for u in v: + if algorithm == 'From_Dictionary': + length = dist_dict[u] + else: + # If algorithm is wrong, the error is raised by the + # shortest_path_lengths function + length = self.shortest_path_lengths(u, by_weight=by_weight, + algorithm=algorithm, + weight_function=weight_function, + check_weight=check_weight) + + if len(length) != self.num_verts(): + ecc[u] = Infinity + else: + ecc[u] = max(length.values()) + + if with_labels: + return ecc + else: + if len(ecc) == 1: + # return single value + v, = ecc.values() + return v + return [ecc[u] for u in v] + + @doc_index("Distances") + def radius(self, by_weight=False, algorithm='DHV', weight_function=None, + check_weight=True): + r""" + Return the radius of the graph. + + The radius is defined to be the minimum eccentricity of any vertex, + where the eccentricity is the maximum distance to any other + vertex. For more information and examples on how to use input variables, + see :meth:`~GenericGraph.shortest_paths` and + :meth:`~Graph.eccentricity` + + INPUT: + + - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge + weights are taken into account; if False, all edges have weight 1 + + - ``algorithm`` -- string (default: ``'DHV'``). + + - ``'DHV'`` - Radius computation is done using the algorithm proposed + in [Dragan2018]_. Works for graph with non-negative edge weights. + For more information see method + :func:`sage.graphs.distances_all_pairs.radius_DHV` and + :func:`sage.graphs.base.boost_graph.radius_DHV`. + + - see method :meth:`eccentricity` for the list of remaining algorithms + + - ``weight_function`` -- function (default: ``None``); a function that + takes as input an edge ``(u, v, l)`` and outputs its weight. If not + ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` + and ``by_weight`` is ``True``, we use the edge label ``l`` as a + weight, if ``l`` is not ``None``, else ``1`` as a weight. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + EXAMPLES: + + The more symmetric a graph is, the smaller (diameter - radius) is:: + + sage: G = graphs.BarbellGraph(9, 3) + sage: G.radius() + 3 + sage: G.diameter() + 6 + + :: + + sage: G = graphs.OctahedralGraph() + sage: G.radius() + 2 + sage: G.diameter() + 2 + + TESTS:: + + sage: g = Graph() + sage: g.radius() + Traceback (most recent call last): + ... + ValueError: radius is not defined for the empty graph + """ + if not self.order(): + raise ValueError("radius is not defined for the empty graph") + + if weight_function is not None: + by_weight = True + + if by_weight and not weight_function: + def weight_function(e): + return 1 if e[2] is None else e[2] + + if not algorithm: + algorithm = 'DHV' + + if algorithm == 'DHV': + if by_weight: + from sage.graphs.base.boost_graph import radius_DHV + return radius_DHV(self, weight_function=weight_function, + check_weight=check_weight) + else: + from sage.graphs.distances_all_pairs import radius_DHV + return radius_DHV(self) + + return min(self.eccentricity(v=None,by_weight=by_weight, + weight_function=weight_function, + check_weight=check_weight, + algorithm=algorithm)) + + @doc_index("Distances") + def diameter(self, by_weight=False, algorithm=None, weight_function=None, + check_weight=True): + r""" + Return the diameter of the graph. + + The diameter is defined to be the maximum distance between two vertices. + It is infinite if the graph is not connected. + + For more information and examples on how to use input variables, see + :meth:`~GenericGraph.shortest_paths` and + :meth:`~Graph.eccentricity` + + INPUT: + + - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge + weights are taken into account; if False, all edges have weight 1 + + - ``algorithm`` -- string (default: ``None``); one of the following + algorithms: + + - ``'BFS'``: the computation is done through a BFS centered on each + vertex successively. Works only if ``by_weight==False``. + + - ``'Floyd-Warshall-Cython'``: a Cython implementation of the + Floyd-Warshall algorithm. Works only if ``by_weight==False`` and ``v + is None``. + + - ``'Floyd-Warshall-Python'``: a Python implementation of the + Floyd-Warshall algorithm. Works also with weighted graphs, even with + negative weights (but no negative cycle is allowed). However, ``v`` + must be ``None``. + + - ``'Dijkstra_NetworkX'``: the Dijkstra algorithm, implemented in + NetworkX. It works with weighted graphs, but no negative weight is + allowed. + + - ``'DHV'`` - diameter computation is done using the algorithm + proposed in [Dragan2018]_. Works only for non-negative edge weights. + For more information see method + :func:`sage.graphs.distances_all_pairs.diameter_DHV` and + :func:`sage.graphs.base.boost_graph.diameter_DHV`. + + - ``'standard'``, ``'2sweep'``, ``'multi-sweep'``, ``'iFUB'``: + these algorithms are implemented in + :func:`sage.graphs.distances_all_pairs.diameter` + They work only if ``by_weight==False``. See the function + documentation for more information. + + - ``'Dijkstra_Boost'``: the Dijkstra algorithm, implemented in Boost + (works only with positive weights). + + - ``'Johnson_Boost'``: the Johnson algorithm, implemented in + Boost (works also with negative weights, if there is no negative + cycle). + + - ``None`` (default): Sage chooses the best algorithm: ``'iFUB'`` for + unweighted graphs, ``'Dijkstra_Boost'`` if all weights are positive, + ``'Johnson_Boost'`` otherwise. + + - ``weight_function`` -- function (default: ``None``); a function that + takes as input an edge ``(u, v, l)`` and outputs its weight. If not + ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` + and ``by_weight`` is ``True``, we use the edge label ``l`` as a + weight, if ``l`` is not ``None``, else ``1`` as a weight. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + EXAMPLES: + + The more symmetric a graph is, the smaller (diameter - radius) is:: + + sage: G = graphs.BarbellGraph(9, 3) + sage: G.radius() + 3 + sage: G.diameter() + 6 + + :: + + sage: G = graphs.OctahedralGraph() + sage: G.radius() + 2 + sage: G.diameter() + 2 + + TESTS:: + + sage: g = Graph() + sage: g.diameter() + Traceback (most recent call last): + ... + ValueError: diameter is not defined for the empty graph + sage: g = Graph([(1, 2, {'weight': 1})]) + sage: g.diameter(algorithm='iFUB', weight_function=lambda e: e[2]['weight']) + Traceback (most recent call last): + ... + ValueError: algorithm 'iFUB' does not work on weighted graphs + """ + if not self.order(): + raise ValueError("diameter is not defined for the empty graph") + + if weight_function is not None: + by_weight = True + + if by_weight and not weight_function: + def weight_function(e): + return 1 if e[2] is None else e[2] + + if algorithm is None: + if by_weight: + algorithm = 'iFUB' + else: + algorithm = 'DHV' + elif algorithm == 'BFS': + algorithm = 'standard' + + if algorithm == 'DHV': + if by_weight: + from sage.graphs.base.boost_graph import diameter_DHV + return diameter_DHV(self, weight_function=weight_function, + check_weight=check_weight) + else: + from sage.graphs.distances_all_pairs import diameter + return diameter(self, algorithm=algorithm) + + if algorithm in ['standard', '2sweep', 'multi-sweep', 'iFUB']: + if by_weight: + raise ValueError("algorithm '" + algorithm + "' does not work" + + " on weighted graphs") + from sage.graphs.distances_all_pairs import diameter + return diameter(self, algorithm=algorithm) + + return max(self.eccentricity(v=list(self), by_weight=by_weight, + weight_function=weight_function, + check_weight=check_weight, + algorithm=algorithm)) + + @doc_index("Distances") + def center(self, by_weight=False, algorithm=None, weight_function=None, + check_weight=True): + r""" + Return the set of vertices in the center of the graph. + + The center is the set of vertices whose eccentricity is equal to the + radius of the graph, i.e., achieving the minimum eccentricity. + + For more information and examples on how to use input variables, + see :meth:`~GenericGraph.shortest_paths` and + :meth:`~Graph.eccentricity` + + INPUT: + + - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge + weights are taken into account; if False, all edges have weight 1 + + - ``algorithm`` -- string (default: ``None``); see method + :meth:`eccentricity` for the list of available algorithms + + - ``weight_function`` -- function (default: ``None``); a function that + takes as input an edge ``(u, v, l)`` and outputs its weight. If not + ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` + and ``by_weight`` is ``True``, we use the edge label ``l`` as a + weight, if ``l`` is not ``None``, else ``1`` as a weight. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + EXAMPLES: + + Is Central African Republic in the center of Africa in graph theoretic + sense? Yes:: + + sage: A = graphs.AfricaMap(continental=True) + sage: sorted(A.center()) + ['Cameroon', 'Central Africa'] + + Some other graphs. Center can be the whole graph:: + + sage: G = graphs.DiamondGraph() + sage: G.center() + [1, 2] + sage: P = graphs.PetersenGraph() + sage: P.subgraph(P.center()) == P + True + sage: S = graphs.StarGraph(19) + sage: S.center() + [0] + + TESTS:: + + sage: G = Graph() + sage: G.center() + [] + sage: G.add_vertex() + 0 + sage: G.center() + [0] + """ + ecc = self.eccentricity(v=list(self), by_weight=by_weight, + weight_function=weight_function, + algorithm=algorithm, + check_weight=check_weight, + with_labels=True) + try: + r = min(ecc.values()) + except Exception: + return [] + return [v for v in self if ecc[v] == r] + + @doc_index("Distances") + def periphery(self, by_weight=False, algorithm=None, weight_function=None, + check_weight=True): + r""" + Return the set of vertices in the periphery of the graph. + + The periphery is the set of vertices whose eccentricity is equal to the + diameter of the graph, i.e., achieving the maximum eccentricity. + + For more information and examples on how to use input variables, + see :meth:`~GenericGraph.shortest_paths` and + :meth:`~Graph.eccentricity` + + INPUT: + + - ``by_weight`` -- boolean (default: ``False``); if ``True``, edge + weights are taken into account; if False, all edges have weight 1 + + - ``algorithm`` -- string (default: ``None``); see method + :meth:`eccentricity` for the list of available algorithms + + - ``weight_function`` -- function (default: ``None``); a function that + takes as input an edge ``(u, v, l)`` and outputs its weight. If not + ``None``, ``by_weight`` is automatically set to ``True``. If ``None`` + and ``by_weight`` is ``True``, we use the edge label ``l`` as a + weight, if ``l`` is not ``None``, else ``1`` as a weight. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + EXAMPLES:: + + sage: G = graphs.DiamondGraph() + sage: G.periphery() + [0, 3] + sage: P = graphs.PetersenGraph() + sage: P.subgraph(P.periphery()) == P + True + sage: S = graphs.StarGraph(19) + sage: S.periphery() + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + sage: G = Graph() + sage: G.periphery() + [] + sage: G.add_vertex() + 0 + sage: G.periphery() + [0] + """ + ecc = self.eccentricity(v=list(self), by_weight=by_weight, + weight_function=weight_function, + algorithm=algorithm, + check_weight=check_weight, + with_labels=True) + try: + d = max(ecc.values()) + except Exception: + return [] + return [v for v in self if ecc[v] == d] + ### Constructors @doc_index("Basic methods") @@ -5936,7 +6579,7 @@ def clique_number(self, algorithm="Cliquer", cliques=None, solver=None, verbose= (see :class:`~sage.numerical.mip.MixedIntegerLinearProgram`) - If ``algorithm = "mcqd"``, uses the MCQD solver - (``_). Note that the MCQD + (``_). Note that the MCQD package must be installed. - ``cliques`` -- an optional list of cliques that can be input if @@ -6916,6 +7559,7 @@ def cores(self, k=None, with_labels=False): [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] sage: (graphs.FruchtGraph()).cores(with_labels=True) {0: 3, 1: 3, 2: 3, 3: 3, 4: 3, 5: 3, 6: 3, 7: 3, 8: 3, 9: 3, 10: 3, 11: 3} + sage: set_random_seed(0) sage: a = random_matrix(ZZ, 20, x=2, sparse=True, density=.1) sage: b = Graph(20) sage: b.add_edges(a.nonzero_positions(), loops=False) @@ -6981,7 +7625,7 @@ def cores(self, k=None, with_labels=False): return list(core.values()) @doc_index("Leftovers") - def modular_decomposition(self, algorithm='habib'): + def modular_decomposition(self, algorithm='habib', style='tuple'): r""" Return the modular decomposition of the current graph. @@ -7002,6 +7646,13 @@ def modular_decomposition(self, algorithm='habib'): - ``'habib'`` -- `O(n^3)` algorithm of [HM1979]_. This algorithm is much simpler and so possibly less prone to errors. + - ``style`` -- string (default: ``'tuple'``); specifies the output + format: + + - ``'tuple'`` -- as nested tuples. + + - ``'tree'`` -- as :class:`~sage.combinat.rooted_tree.LabelledRootedTree`. + OUTPUT: A pair of two values (recursively encoding the decomposition) : @@ -7060,25 +7711,21 @@ def modular_decomposition(self, algorithm='habib'): The Bull Graph is prime:: - sage: graphs.BullGraph().modular_decomposition() # py2 - (PRIME, [0, 3, 4, 2, 1]) - sage: graphs.BullGraph().modular_decomposition() # py3 + sage: graphs.BullGraph().modular_decomposition() (PRIME, [1, 2, 0, 3, 4]) The Petersen Graph too:: - sage: graphs.PetersenGraph().modular_decomposition() # py2 - (PRIME, [6, 2, 5, 1, 9, 3, 0, 7, 8, 4]) - sage: graphs.PetersenGraph().modular_decomposition() # py3 + sage: graphs.PetersenGraph().modular_decomposition() (PRIME, [1, 4, 5, 0, 2, 6, 3, 7, 8, 9]) This a clique on 5 vertices with 2 pendant edges, though, has a more - interesting decomposition :: + interesting decomposition:: sage: g = graphs.CompleteGraph(5) sage: g.add_edge(0,5) sage: g.add_edge(0,6) - sage: g.modular_decomposition() + sage: g.modular_decomposition(algorithm='habib') (SERIES, [(PARALLEL, [(SERIES, [1, 2, 3, 4]), 5, 6]), 0]) We get an equivalent tree when we use the algorithm of [TCHP2008]_:: @@ -7086,6 +7733,20 @@ def modular_decomposition(self, algorithm='habib'): sage: g.modular_decomposition(algorithm='tedder') (SERIES, [(PARALLEL, [(SERIES, [4, 3, 2, 1]), 5, 6]), 0]) + We can choose output to be a + :class:`~sage.combinat.rooted_tree.LabelledRootedTree`:: + + sage: g.modular_decomposition(style='tree') + SERIES[0[], PARALLEL[5[], 6[], SERIES[1[], 2[], 3[], 4[]]]] + sage: ascii_art(g.modular_decomposition(style='tree')) + __SERIES + / / + 0 ___PARALLEL + / / / + 5 6 __SERIES + / / / / + 1 2 3 4 + ALGORITHM: When ``algorithm='tedder'`` this function uses python implementation of @@ -7097,12 +7758,31 @@ def modular_decomposition(self, algorithm='habib'): - :meth:`is_prime` -- Tests whether a graph is prime. + - :class:`~sage.combinat.rooted_tree.LabelledRootedTree`. + TESTS: Empty graph:: - sage: graphs.EmptyGraph().modular_decomposition() + sage: graphs.EmptyGraph().modular_decomposition(algorithm='habib') () + sage: graphs.EmptyGraph().modular_decomposition(algorithm='tedder') + () + sage: graphs.EmptyGraph().modular_decomposition(algorithm='habib', style='tree') + None[] + sage: graphs.EmptyGraph().modular_decomposition(algorithm='tedder', style='tree') + None[] + + Singleton Vertex:: + + sage: Graph(1).modular_decomposition(algorithm='habib') + (PRIME, [0]) + sage: Graph(1).modular_decomposition(algorithm='tedder') + (PRIME, [0]) + sage: Graph(1).modular_decomposition(algorithm='habib', style='tree') + PRIME[0[]] + sage: Graph(1).modular_decomposition(algorithm='tedder', style='tree') + PRIME[0[]] Vertices may be arbitrary --- check that :trac:`24898` is fixed:: @@ -7115,31 +7795,56 @@ def modular_decomposition(self, algorithm='habib'): Traceback (most recent call last): ... ValueError: algorithm must be 'habib' or 'tedder' + + Unknown style:: + + sage: graphs.PathGraph(2).modular_decomposition(style='xyz') + Traceback (most recent call last): + ... + ValueError: style must be 'tuple' or 'tree' """ - from sage.graphs.graph_decompositions.modular_decomposition import modular_decomposition, NodeType, habib_maurer_algorithm + from sage.graphs.graph_decompositions.modular_decomposition import (modular_decomposition, + NodeType, + habib_maurer_algorithm, + create_prime_node, + create_normal_node) self._scream_if_not_simple() if not self.order(): - return tuple() - - if self.order() == 1: - return (NodeType.PRIME, list(self)) - - if algorithm == 'habib': - D = habib_maurer_algorithm(self) - elif algorithm == 'tedder': - D = modular_decomposition(self) + D = None + elif self.order() == 1: + D = create_prime_node() + D.children.append(create_normal_node(self.vertices()[0])) else: - raise ValueError("algorithm must be 'habib' or 'tedder'") - - def relabel(x): - if x.node_type == NodeType.NORMAL: - return x.children[0] + if algorithm == 'habib': + D = habib_maurer_algorithm(self) + elif algorithm == 'tedder': + D = modular_decomposition(self) else: - return x.node_type, [relabel(y) for y in x.children] - - return relabel(D) + raise ValueError("algorithm must be 'habib' or 'tedder'") + + if style == 'tuple': + if D is None: + return tuple() + def relabel(x): + if x.node_type == NodeType.NORMAL: + return x.children[0] + else: + return x.node_type, [relabel(y) for y in x.children] + return relabel(D) + elif style == 'tree': + from sage.combinat.rooted_tree import LabelledRootedTree + if D is None: + return LabelledRootedTree([]) + def to_tree(x): + if x.node_type == NodeType.NORMAL: + return LabelledRootedTree([], label=x.children[0]) + else: + return LabelledRootedTree([to_tree(y) for y in x.children], label=x.node_type) + return to_tree(D) + else: + raise ValueError("style must be 'tuple' or 'tree'") @doc_index("Graph properties") def is_polyhedral(self): @@ -8012,7 +8717,6 @@ def perfect_matchings(self, labels=False): def has_perfect_matching(self, algorithm="Edmonds", solver=None, verbose=0): r""" Return whether this graph has a perfect matching. - INPUT: - ``algorithm`` -- string (default: ``"Edmonds"``) @@ -8183,7 +8887,7 @@ def effective_resistance(self, i, j): if i not in self: raise ValueError("vertex ({0}) is not a vertex of the graph".format(repr(i))) elif j not in self: - raise ValueError("vertex ({0}) is not a vertex of the graph".format(repr(j))) + raise ValueError("vertex ({0}) is not a vertex of the graph".format(repr(j))) if i == j : return 0 @@ -8291,7 +8995,7 @@ def effective_resistance_matrix(self, vertices=None, nonedgesonly=True): multiedges. Perhaps this method can be updated to handle them, but in the meantime if you want to use it please disallow multiedges using allow_multiple_edges(). - + sage: graphs.CompleteGraph(4).effective_resistance_matrix(nonedgesonly=False) [ 0 1/2 1/2 1/2] [1/2 0 1/2 1/2] @@ -8416,10 +9120,10 @@ def least_effective_resistance(self, nonedgesonly=True): edges = self.complement().edges(labels=False) else: edges = [(verts[i], verts[j]) for i in range(n) for j in range(i + 1, n)] - + rmin = min(S[(verttoidx[e[0]], verttoidx[e[1]])] for e in edges) return [e for e in edges if S[(verttoidx[e[0]], verttoidx[e[1]])] == rmin] - + @doc_index("Leftovers") def common_neighbors_matrix(self, vertices=None, nonedgesonly=True): r""" @@ -8487,9 +9191,9 @@ def common_neighbors_matrix(self, vertices=None, nonedgesonly=True): sage: G.common_neighbors_matrix() Traceback (most recent call last): ... - ValueError: This method is not known to work on graphs with loops. - Perhaps this method can be updated to handle them, but in the - meantime if you want to use it please disallow loops using + ValueError: This method is not known to work on graphs with loops. + Perhaps this method can be updated to handle them, but in the + meantime if you want to use it please disallow loops using allow_loops(). .. SEEALSO:: @@ -8533,7 +9237,7 @@ def most_common_neighbors(self, nonedgesonly=True): Return vertex pairs with maximal number of common neighbors. This method is only valid for simple (no loops, no multiple edges) - graphs with order `\geq 2` + graphs with order `\geq 2` INPUT: @@ -8603,6 +9307,59 @@ def most_common_neighbors(self, nonedgesonly=True): if M[v, w] == maximum: output.append((verts[v], verts[w])) return output + @doc_index("Leftovers") + def arboricity(self, certificate=False): + r""" + Return the arboricity of the graph and an optional certificate. + + The arboricity is the minimum number of forests that covers the + graph. + + See :wikipedia:`Arboricity` + + INPUT: + + - ``certificate`` -- boolean (default: ``False``); whether to return + a certificate. + + OUTPUT: + + When ``certificate = True``, then the function returns `(a, F)` + where `a` is the arboricity and `F` is a list of `a` disjoint forests + that partitions the edge set of `g`. The forests are represented as + subgraphs of the original graph. + + If ``certificate = False``, the function returns just a integer + indicating the arboricity. + + ALGORITHM: + + Represent the graph as a graphical matroid, then apply matroid + :meth:`sage.matroid.partition` algorithm from the matroids module. + + EXAMPLES:: + + sage: G = graphs.PetersenGraph() + sage: a,F = G.arboricity(True) + sage: a + 2 + sage: all([f.is_forest() for f in F]) + True + sage: len(set.union(*[set(f.edges()) for f in F])) == G.size() + True + + TESTS:: + + sage: g = Graph() + sage: g.arboricity(True) + (0, []) + """ + from sage.matroids.constructor import Matroid + P = Matroid(self).partition() + if certificate: + return (len(P), [self.subgraph(edges=forest) for forest in P]) + else: + return len(P) # Aliases to functions defined in other modules from sage.graphs.weakly_chordal import is_long_hole_free, is_long_antihole_free, is_weakly_chordal @@ -8613,6 +9370,7 @@ def most_common_neighbors(self, nonedgesonly=True): from sage.graphs.graph_decompositions.clique_separators import atoms_and_clique_separators from sage.graphs.matchpoly import matching_polynomial from sage.graphs.cliquer import all_max_clique as cliques_maximum + from sage.graphs.cliquer import all_cliques from sage.graphs.spanning_tree import random_spanning_tree from sage.graphs.graph_decompositions.graph_products import is_cartesian_product from sage.graphs.distances_all_pairs import is_distance_regular @@ -8632,6 +9390,7 @@ def most_common_neighbors(self, nonedgesonly=True): from sage.graphs.domination import minimal_dominating_sets from sage.graphs.traversals import (lex_M, maximum_cardinality_search, maximum_cardinality_search_M) + from sage.graphs.isoperimetric_inequalities import cheeger_constant, edge_isoperimetric_number, vertex_isoperimetric_number _additional_categories = { "is_long_hole_free" : "Graph properties", @@ -8644,6 +9403,7 @@ def most_common_neighbors(self, nonedgesonly=True): "matching_polynomial" : "Algorithmically hard stuff", "all_max_clique" : "Clique-related methods", "cliques_maximum" : "Clique-related methods", + "all_cliques" : "Clique-related methods", "atoms_and_clique_separators" : "Clique-related methods", "random_spanning_tree" : "Connectivity, orientations, trees", "is_cartesian_product" : "Graph properties", @@ -8667,7 +9427,10 @@ def most_common_neighbors(self, nonedgesonly=True): "minimal_dominating_sets" : "Domination", "lex_M" : "Traversals", "maximum_cardinality_search" : "Traversals", - "maximum_cardinality_search_M" : "Traversals" + "maximum_cardinality_search_M" : "Traversals", + "cheeger_constant" : "Expansion properties", + "edge_isoperimetric_number" : "Expansion properties", + "vertex_isoperimetric_number": "Expansion properties" } __doc__ = __doc__.replace("{INDEX_OF_METHODS}",gen_thematic_rest_table_index(Graph,_additional_categories)) diff --git a/src/sage/graphs/graph_coloring.pyx b/src/sage/graphs/graph_coloring.pyx index 47b93c1f386..7f82c457121 100644 --- a/src/sage/graphs/graph_coloring.pyx +++ b/src/sage/graphs/graph_coloring.pyx @@ -53,7 +53,6 @@ Methods # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # **************************************************************************** -from six.moves import range from copy import copy from sage.combinat.matrices.dlxcpp import DLXCPP @@ -65,6 +64,7 @@ from libcpp.pair cimport pair from sage.numerical.mip import MixedIntegerLinearProgram from sage.numerical.mip import MIPSolverException + def all_graph_colorings(G, n, count_only=False, hex_colors=False, vertex_color_dict=False): r""" Compute all `n`-colorings of a graph. @@ -1675,7 +1675,7 @@ cdef class Test: def random(self, tests=1000): r""" - Call ``self.random_all_graph_colorings()``. + Call ``self.random_all_graph_colorings()``. In the future, if other methods are added, it should call them, too. diff --git a/src/sage/graphs/graph_database.py b/src/sage/graphs/graph_database.py index a14cc595405..9f63bae48f2 100644 --- a/src/sage/graphs/graph_database.py +++ b/src/sage/graphs/graph_database.py @@ -309,7 +309,8 @@ def __init__(self, query_string, database=None, param_tuple=None): 21 D@O 5 2 22 D?[ 5 3 """ - if database is None: database = GraphDatabase() + if database is None: + database = GraphDatabase() if not isinstance(database, GraphDatabase): raise TypeError('%s is not a valid GraphDatabase'%database) SQLQuery.__init__(self, database, query_string, param_tuple) @@ -411,7 +412,8 @@ class located in :mod:`sage.databases.sql_db` to make the query F_?Hg 7 [1, 1, 1, 1, 1, 2, 3] F_?XO 7 [1, 1, 1, 1, 2, 2, 2] """ - if graph_db is None: graph_db = GraphDatabase() + if graph_db is None: + graph_db = GraphDatabase() if query_dict is not None: if query_dict['expression'][0] == 'degree_sequence': query_dict['expression'][3] = degseq_to_data(query_dict['expression'][3]) @@ -438,11 +440,16 @@ class located in :mod:`sage.databases.sql_db` to make the query # them including repeats) # set table name - if key in graph_data: qdict['table_name'] = 'graph_data' - elif key in aut_grp: qdict['table_name'] = 'aut_grp' - elif key in degrees: qdict['table_name'] = 'degrees' - elif key in misc: qdict['table_name'] = 'misc' - elif key in spectrum: qdict['table_name'] = 'spectrum' + if key in graph_data: + qdict['table_name'] = 'graph_data' + elif key in aut_grp: + qdict['table_name'] = 'aut_grp' + elif key in degrees: + qdict['table_name'] = 'degrees' + elif key in misc: + qdict['table_name'] = 'misc' + elif key in spectrum: + qdict['table_name'] = 'spectrum' # set expression if not isinstance(kwds[key], list): @@ -481,11 +488,16 @@ class located in :mod:`sage.databases.sql_db` to make the query # organize display if display_cols is not None: for col in display_cols: - if col in graph_data: graph_data_disp.append(col) - elif col in aut_grp: aut_grp_disp.append(col) - elif col in degrees: degrees_disp.append(col) - elif col in misc: misc_disp.append(col) - elif col in spectrum: spectrum_disp.append(col) + if col in graph_data: + graph_data_disp.append(col) + elif col in aut_grp: + aut_grp_disp.append(col) + elif col in degrees: + degrees_disp.append(col) + elif col in misc: + misc_disp.append(col) + elif col in spectrum: + spectrum_disp.append(col) # finish filling master join with display tables for tab in disp_tables: @@ -501,10 +513,14 @@ class located in :mod:`sage.databases.sql_db` to make the query disp_list = ['SELECT graph_data.graph6, '] for col in graph_data_disp[1:]: if col != 'graph6': disp_list.append('graph_data.%s, '%col) - for col in aut_grp_disp[1:]: disp_list.append('aut_grp.%s, '%col) - for col in degrees_disp[1:]: disp_list.append('degrees.%s, '%col) - for col in misc_disp[1:]: disp_list.append('misc.%s, '%col) - for col in spectrum_disp[1:]: disp_list.append('spectrum.%s, '%col) + for col in aut_grp_disp[1:]: + disp_list.append('aut_grp.%s, '%col) + for col in degrees_disp[1:]: + disp_list.append('degrees.%s, '%col) + for col in misc_disp[1:]: + disp_list.append('misc.%s, '%col) + for col in spectrum_disp[1:]: + disp_list.append('spectrum.%s, '%col) disp_list[-1] = disp_list[-1].rstrip(', ') + ' ' disp_str = ''.join(disp_list) diff --git a/src/sage/graphs/graph_decompositions/clique_separators.pyx b/src/sage/graphs/graph_decompositions/clique_separators.pyx index 3df6224d839..3c4e1b06723 100644 --- a/src/sage/graphs/graph_decompositions/clique_separators.pyx +++ b/src/sage/graphs/graph_decompositions/clique_separators.pyx @@ -157,6 +157,7 @@ cdef inline bint is_clique(short_digraph sd, vector[int] Hx): return False return True + def atoms_and_clique_separators(G, tree=False, rooted_tree=False, separators=False): r""" Return the atoms of the decomposition of `G` by clique minimal separators. @@ -200,7 +201,7 @@ def atoms_and_clique_separators(G, tree=False, rooted_tree=False, separators=Fal OUTPUT: - By default, return a tuple `(A, S_c)`, where `A` is the list of atoms of - the graph in the order of dicovery, and `S_c` is the list of clique + the graph in the order of discovery, and `S_c` is the list of clique separators, with possible repetitions, in the order the separator has been considered. If furthermore ``separators`` is ``True``, return a tuple `(A, S_h, S_c)`, where `S_c` is the list of considered separators of the graph diff --git a/src/sage/graphs/graph_decompositions/modular_decomposition.py b/src/sage/graphs/graph_decompositions/modular_decomposition.py index 93f7305c511..a34c218d719 100644 --- a/src/sage/graphs/graph_decompositions/modular_decomposition.py +++ b/src/sage/graphs/graph_decompositions/modular_decomposition.py @@ -1160,7 +1160,6 @@ def check_parallel(graph, root, left, right, # module can be formed if source_index != new_right_index: node = create_parallel_node() - temp = source_index for temp in range(source_index, new_right_index + 1): # if module X to be included in the new parallel module Y is also @@ -2540,20 +2539,20 @@ def habib_maurer_algorithm(graph, g_classes=None): The Icosahedral graph is Prime:: sage: from sage.graphs.graph_decompositions.modular_decomposition import * - sage: print_md_tree(habib_maurer_algorithm(graphs.IcosahedralGraph())) # py2 + sage: print_md_tree(habib_maurer_algorithm(graphs.IcosahedralGraph())) PRIME + 1 + 5 + 7 8 + 11 0 - 1 + 2 + 6 3 - 7 + 9 4 - 5 - 2 10 - 11 - 9 - 6 The Octahedral graph is not Prime:: @@ -3296,6 +3295,7 @@ def random_md_tree(max_depth, max_fan_out, leaf_probability): EXAMPLES:: sage: from sage.graphs.graph_decompositions.modular_decomposition import * + sage: set_random_seed(0) sage: tree_to_nested_tuple(random_md_tree(2, 5, 0.5)) (PRIME, [0, 1, (PRIME, [2, 3, 4, 5, 6]), 7, (PARALLEL, [8, 9, 10])]) """ diff --git a/src/sage/graphs/graph_decompositions/rankwidth.pyx b/src/sage/graphs/graph_decompositions/rankwidth.pyx index a5c3b4d917f..c21e259a3b5 100644 --- a/src/sage/graphs/graph_decompositions/rankwidth.pyx +++ b/src/sage/graphs/graph_decompositions/rankwidth.pyx @@ -1,4 +1,5 @@ # cython: binding=True +# distutils: libraries = rw r""" Rank Decompositions of graphs diff --git a/src/sage/graphs/graph_decompositions/tdlib.pyx b/src/sage/graphs/graph_decompositions/tdlib.pyx index 5ffc28685a2..c4d86dc0a8a 100644 --- a/src/sage/graphs/graph_decompositions/tdlib.pyx +++ b/src/sage/graphs/graph_decompositions/tdlib.pyx @@ -1,3 +1,6 @@ +# distutils: language = c++ +# sage_setup: distribution = sage-tdlib + r""" Interface with TdLib (algorithms for tree decompositions) @@ -152,6 +155,7 @@ def treedecomposition_exact(G, lb=-1): return T + def get_width(T): """ Return the width of a given tree decomposition. @@ -167,6 +171,7 @@ def get_width(T): - The width of ``T`` EXAMPLES:: + sage: import sage.graphs.graph_decompositions.tdlib as tdlib # optional - tdlib sage: G = graphs.PetersenGraph() # optional - tdlib sage: T = tdlib.treedecomposition_exact(G) # optional - tdlib diff --git a/src/sage/graphs/graph_decompositions/vertex_separation.pyx b/src/sage/graphs/graph_decompositions/vertex_separation.pyx index 5f9604c124a..abc3e79cfa0 100644 --- a/src/sage/graphs/graph_decompositions/vertex_separation.pyx +++ b/src/sage/graphs/graph_decompositions/vertex_separation.pyx @@ -914,9 +914,7 @@ def vertex_separation_exp(G, verbose=False): sage: from sage.graphs.graph_decompositions.vertex_separation import vertex_separation_exp sage: D=digraphs.DeBruijn(2,3) - sage: vertex_separation_exp(D) # py2 - (2, ['010', '110', '111', '011', '001', '000', '100', '101']) - sage: vertex_separation_exp(D) # py3 + sage: vertex_separation_exp(D) (2, ['000', '001', '100', '010', '101', '011', '110', '111']) Given a too large graph:: diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index a5794c1a6c0..6cafb8aa463 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -16,8 +16,6 @@ build by typing ``graphs.`` in Sage and then hitting tab. """ from __future__ import print_function, absolute_import, division -from six.moves import range -from six import PY2 from sage.env import SAGE_NAUTY_BINS_PREFIX as nautyprefix import subprocess @@ -69,6 +67,9 @@ def __append_to_doc(methods): "CompleteGraph", "CompleteMultipartiteGraph", "DiamondGraph", + "GemGraph", + "DartGraph", + "ForkGraph", "DipoleGraph", "EmptyGraph", "Grid2dGraph", @@ -211,6 +212,7 @@ def __append_to_doc(methods): "CirculantGraph", "cospectral_graphs", "CubeGraph", + "CubeConnectedCycle", "DorogovtsevGoltsevMendesGraph", "EgawaGraph", "FibonacciTree", @@ -793,7 +795,8 @@ def extra_property(x): gens = [] for i in range(vertices-1): gen = list(range(i)) - gen.append(i+1); gen.append(i) + gen.append(i+1) + gen.append(i) gen += list(range(i + 2, vertices)) gens.append(gen) for gg in canaug_traverse_edge(g, gens, property, loops=loops, sparse=sparse): @@ -824,25 +827,25 @@ def nauty_geng(self, options="", debug=False): The possible options, obtained as output of ``geng --help``:: - n : the number of vertices - mine:maxe : a range for the number of edges - #:0 means '# or more' except in the case 0:0 + n : the number of vertices + mine:maxe : : a range for the number of edges + :0 means ' or more' except in the case 0:0 res/mod : only generate subset res out of subsets 0..mod-1 - -c : only write connected graphs - -C : only write biconnected graphs - -t : only generate triangle-free graphs - -f : only generate 4-cycle-free graphs - -b : only generate bipartite graphs - (-t, -f and -b can be used in any combination) - -m : save memory at the expense of time (only makes a - difference in the absence of -b, -t, -f and n <= 28). - -d# : a lower bound for the minimum degree - -D# : a upper bound for the maximum degree - -v : display counts by number of edges - -l : canonically label output graphs - - -q : suppress auxiliary output (except from -v) + -c : only write connected graphs + -C : only write biconnected graphs + -t : only generate triangle-free graphs + -f : only generate 4-cycle-free graphs + -b : only generate bipartite graphs + (-t, -f and -b can be used in any combination) + -m : save memory at the expense of time (only makes a + difference in the absence of -b, -t, -f and n <= 28). + -d : a lower bound for the minimum degree + -D : a upper bound for the maximum degree + -v : display counts by number of edges + -l : canonically label output graphs + + -q : suppress auxiliary output (except from -v) Options which cause ``geng`` to use an output format different than the graph6 format are not listed above (-u, -g, -s, -y, -h) as they will @@ -891,6 +894,12 @@ def nauty_geng(self, options="", debug=False): sage: len(list(gen)) 853 + A list of connected degree exactly 2 graphs on 5 vertices. :: + + sage: gen = graphs.nauty_geng("5 -c -d2 -D2") + sage: len(list(gen)) + 1 + The ``debug`` switch can be used to examine ``geng``'s reaction to the input in the ``options`` string. We illustrate success. (A failure will be a string beginning with ">E".) Passing the "-q" switch to @@ -918,15 +927,11 @@ def nauty_geng(self, options="", debug=False): sage: list(graphs.nauty_geng("-c 3", debug=True)) ['>A ...geng -cd1D2 n=3 e=2-3\n', Graph on 3 vertices, Graph on 3 vertices] """ - if PY2: - enc_kwargs = {} - else: - enc_kwargs = {'encoding': 'latin-1'} sp = subprocess.Popen(nautyprefix+"geng {0}".format(options), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, - **enc_kwargs) + encoding='latin-1') msg = sp.stderr.readline() if debug: yield msg @@ -1094,7 +1099,7 @@ def _read_planar_code(self, code_input): The following example creates a small planar code file in memory and reads it using the ``_read_planar_code`` method:: - sage: from six import StringIO + sage: from io import StringIO sage: code_input = StringIO('>>planar_code<<') sage: _ = code_input.write('>>planar_code<<') sage: for c in [4,2,3,4,0,1,4,3,0,1,2,4,0,1,3,2,0]: @@ -1260,18 +1265,12 @@ def fullerenes(self, order, ipr=False): command = 'buckygen -'+('I' if ipr else '')+'d {0}d'.format(order) - if PY2: - enc_kwargs = {} - else: - enc_kwargs = {'encoding': 'latin-1'} - sp = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, - **enc_kwargs) + encoding='latin-1') - if not PY2: - sp.stdout.reconfigure(newline='') + sp.stdout.reconfigure(newline='') for G in graphs._read_planar_code(sp.stdout): yield(G) @@ -1353,18 +1352,12 @@ def fusenes(self, hexagon_count, benzenoids=False): command = 'benzene '+('b' if benzenoids else '')+' {0} p'.format(hexagon_count) - if PY2: - enc_kwargs = {} - else: - enc_kwargs = {'encoding': 'latin-1'} - sp = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, - **enc_kwargs) + encoding='latin-1') - if not PY2: - sp.stdout.reconfigure(newline='') + sp.stdout.reconfigure(newline='') for G in graphs._read_planar_code(sp.stdout): yield(G) @@ -1551,18 +1544,12 @@ def planar_graphs(self, order, minimum_degree=None, 'd' if dual else '', order) - if PY2: - enc_kwargs = {} - else: - enc_kwargs = {'encoding': 'latin-1'} - sp = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, - **enc_kwargs) + encoding='latin-1') - if not PY2: - sp.stdout.reconfigure(newline='') + sp.stdout.reconfigure(newline='') for G in graphs._read_planar_code(sp.stdout): yield(G) @@ -1742,18 +1729,12 @@ def triangulations(self, order, minimum_degree=None, minimum_connectivity=None, 'd' if dual else '', order) - if PY2: - enc_kwargs = {} - else: - enc_kwargs = {'encoding': 'latin-1'} - sp = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, - **enc_kwargs) + encoding='latin-1') - if not PY2: - sp.stdout.reconfigure(newline='') + sp.stdout.reconfigure(newline='') for G in graphs._read_planar_code(sp.stdout): yield(G) @@ -1892,18 +1873,12 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None 'd' if dual else '', order) - if PY2: - enc_kwargs = {} - else: - enc_kwargs = {'encoding': 'latin-1'} - sp = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, - **enc_kwargs) + encoding='latin-1') - if not PY2: - sp.stdout.reconfigure(newline='') + sp.stdout.reconfigure(newline='') for G in graphs._read_planar_code(sp.stdout): yield(G) @@ -1921,6 +1896,9 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None CompleteBipartiteGraph = staticmethod(basic.CompleteBipartiteGraph) CompleteMultipartiteGraph= staticmethod(basic.CompleteMultipartiteGraph) DiamondGraph = staticmethod(basic.DiamondGraph) + GemGraph = staticmethod(basic.GemGraph) + DartGraph = staticmethod(basic.DartGraph) + ForkGraph = staticmethod(basic.ForkGraph) EmptyGraph = staticmethod(basic.EmptyGraph) Grid2dGraph = staticmethod(basic.Grid2dGraph) GridGraph = staticmethod(basic.GridGraph) @@ -2049,6 +2027,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None chang_graphs = staticmethod(families.chang_graphs) CirculantGraph = staticmethod(families.CirculantGraph) CubeGraph = staticmethod(families.CubeGraph) + CubeConnectedCycle = staticmethod(families.CubeConnectedCycle) DipoleGraph = staticmethod(families.DipoleGraph) DorogovtsevGoltsevMendesGraph = staticmethod(families.DorogovtsevGoltsevMendesGraph) EgawaGraph = staticmethod(families.EgawaGraph) @@ -2426,7 +2405,8 @@ def canaug_traverse_edge(g, aut_gens, property, dig=False, loops=False, sparse=T max_size = n*(n-1) else: max_size = (n*(n-1))>>1 # >> 1 is just / 2 (this is n choose 2) - if loops: max_size += n + if loops: + max_size += n if g.size() < max_size: # build a list representing C(g) - the edge to be added # is one of max_size choices diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index dc4a21f9931..06c708486cf 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -19,8 +19,6 @@ """ from __future__ import absolute_import, division -from six import iteritems -from six.moves import range from sage.cpython.string import bytes_to_str @@ -330,17 +328,18 @@ def from_incidence_matrix(G, M, loops=False, multiedges=False, weighted=False): if loops is None: loops = True positions.append((NZ[0], NZ[0])) - elif len(NZ) != 2 or \ - (oriented and not ((M[NZ[0], i] == +1 and M[NZ[1], i] == -1) or \ - (M[NZ[0], i] == -1 and M[NZ[1], i] == +1))) or \ - (not oriented and (M[NZ[0], i] != 1 or M[NZ[1], i] != 1)): + elif (len(NZ) != 2 or + (oriented and not ((M[NZ[0], i] == +1 and M[NZ[1], i] == -1) or + (M[NZ[0], i] == -1 and M[NZ[1], i] == +1))) or + (not oriented and (M[NZ[0], i] != 1 or M[NZ[1], i] != 1))): msg = "there must be one or two nonzero entries per column in an incidence matrix, " msg += "got entries {} in column {}".format([M[j, i] for j in NZ], i) raise ValueError(msg) else: positions.append(tuple(NZ)) - if weighted is None: G._weighted = False + if weighted is None: + G._weighted = False if multiedges is None: total = len(positions) multiedges = len(set(positions)) < total @@ -382,6 +381,27 @@ def from_oriented_incidence_matrix(G, M, loops=False, multiedges=False, weighted Traceback (most recent call last): ... ValueError: each column represents an edge: -1 goes to 1 + + Handle incidence matrix containing a column with only zeros (:trac:`29275`):: + + sage: m = Matrix([[0,1],[0,-1],[0,0]]) + sage: m + [ 0 1] + [ 0 -1] + [ 0 0] + sage: G = DiGraph(m,format='incidence_matrix') + sage: list(G.edges(labels=False)) + [(1, 0)] + + Handle incidence matrix [[1],[-1]] (:trac:`29275`):: + + sage: m = Matrix([[1],[-1]]) + sage: m + [ 1] + [-1] + sage: G = DiGraph(m,format='incidence_matrix') + sage: list(G.edges(labels=False)) + [(1, 0)] """ from sage.structure.element import is_Matrix assert is_Matrix(M) @@ -389,16 +409,19 @@ def from_oriented_incidence_matrix(G, M, loops=False, multiedges=False, weighted positions = [] for c in M.columns(): NZ = c.nonzero_positions() + if not NZ: + continue if len(NZ) != 2: raise ValueError("there must be two nonzero entries (-1 & 1) per column") - L = sorted(set(c.list())) - if L != [-1, 0, 1]: + L = sorted([c[i] for i in NZ]) + if L != [-1, 1]: raise ValueError("each column represents an edge: -1 goes to 1") if c[NZ[0]] == -1: positions.append(tuple(NZ)) else: positions.append((NZ[1], NZ[0])) - if weighted is None: weighted = False + if weighted is None: + weighted = False if multiedges is None: total = len(positions) multiedges = len(set(positions)) < total @@ -436,9 +459,9 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv raise ValueError("input dict must be a consistent format") if not loops: - if any(u in neighb for u,neighb in iteritems(M)): + if any(u in neighb for u,neighb in M.items()): if loops is False: - u = next(u for u,neighb in iteritems(M) if u in neighb) + u = next(u for u,neighb in M.items() if u in neighb) raise ValueError("the graph was built with loops=False but input M has a loop at {}".format(u)) loops = True if loops is None: @@ -503,9 +526,9 @@ def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False): """ verts = set().union(D.keys(), *D.values()) if not loops: - if any(u in neighb for u, neighb in iteritems(D)): + if any(u in neighb for u, neighb in D.items()): if loops is False: - u = next(u for u, neighb in iteritems(D) if u in neighb) + u = next(u for u, neighb in D.items() if u in neighb) raise ValueError("the graph was built with loops=False but input D has a loop at {}".format(u)) loops = True if loops is None: diff --git a/src/sage/graphs/graph_latex.py b/src/sage/graphs/graph_latex.py index 6cc062f127f..a9b75e82d62 100644 --- a/src/sage/graphs/graph_latex.py +++ b/src/sage/graphs/graph_latex.py @@ -1071,7 +1071,7 @@ def set_option(self, option_name, option_value=None): # shape" to mirror vertex shapes # - "line width" works for vertices, should be configurable # - allow injection of latex code to style a pre-built style for - # example, \SetUpVertex[style={fill=green}] could overide color + # example, \SetUpVertex[style={fill=green}] could override color # selection in a style like "Art" # - "inner sep" is distance from vertex label to edge of vertex this # should be set as small as possible - but bigger than the line width. @@ -1313,6 +1313,37 @@ def latex(self): \Edge[lw=0.1cm,style={color=cv0v1,},](v0)(v1) % \end{tikzpicture} + + We check that :trac:`22070` is fixed:: + + sage: edges = [(i,(i+1)%3,a) for i,a in enumerate('abc')] + sage: G_with_labels = DiGraph(edges) + sage: C = [[0,1], [2]] + sage: kwds = dict(subgraph_clusters=C,color_by_label=True,prog='dot',format='dot2tex') + sage: opts = G_with_labels.latex_options() + sage: opts.set_options(edge_labels=True, **kwds) # optional - dot2tex graphviz + sage: latex(G_with_labels) # optional - dot2tex graphviz + \begin{tikzpicture}[>=latex,line join=bevel,] + %% + \begin{scope} + \pgfsetstrokecolor{black} + \definecolor{strokecol}{rgb}{...}; + \pgfsetstrokecolor{strokecol} + \definecolor{fillcol}{rgb}{...}; + \pgfsetfillcolor{fillcol} + \filldraw ... cycle; + \end{scope} + \begin{scope} + \pgfsetstrokecolor{black} + \definecolor{strokecol}{rgb}{...}; + \pgfsetstrokecolor{strokecol} + \definecolor{fillcol}{rgb}{...}; + \pgfsetfillcolor{fillcol} + \filldraw ... cycle; + \end{scope} + ... + \end{tikzpicture} + """ format = self.get_option('format') if format == "tkz_graph": diff --git a/src/sage/graphs/graph_list.py b/src/sage/graphs/graph_list.py index 21f50b1c6de..376a5f7eabf 100644 --- a/src/sage/graphs/graph_list.py +++ b/src/sage/graphs/graph_list.py @@ -303,6 +303,9 @@ def show_graphs(graph_list, **kwds): sage: glist.append(graphs.BarbellGraph(7, 4)) sage: glist.append(graphs.CycleGraph(15)) sage: glist.append(graphs.DiamondGraph()) + sage: glist.append(graphs.GemGraph()) + sage: glist.append(graphs.DartGraph()) + sage: glist.append(graphs.ForkGraph()) sage: glist.append(graphs.HouseGraph()) sage: glist.append(graphs.HouseXGraph()) sage: glist.append(graphs.KrackhardtKiteGraph()) @@ -316,7 +319,7 @@ def show_graphs(graph_list, **kwds): Check that length is <= 20:: sage: len(glist) - 14 + 17 Show the graphs in a graphics array:: diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index 32167ac8920..d2d353e4920 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -11,7 +11,7 @@ sage: P.show() # long time .. PLOT:: - + sphinx_plot(graphs.WheelGraph(15)) If you create a graph in Sage using the ``Graph`` command, then plot that graph, @@ -24,7 +24,7 @@ sage: petersen_spring.show() # long time .. PLOT:: - + petersen_spring = Graph(':I`ES@obGkqegW~') sphinx_plot(petersen_spring) @@ -34,7 +34,7 @@ sage: petersen_database.show() # long time .. PLOT:: - + petersen_database = graphs.PetersenGraph() sphinx_plot(petersen_database) @@ -109,17 +109,31 @@ """ layout_options = { - 'layout': 'A layout algorithm -- one of : "acyclic", "circular" (plots the graph with vertices evenly distributed on a circle), "ranked", "graphviz", "planar", "spring" (traditional spring layout, using the graph\'s current positions as initial positions), or "tree" (the tree will be plotted in levels, depending on minimum distance for the root).', - 'iterations': 'The number of times to execute the spring layout algorithm.', - 'heights': 'A dictionary mapping heights to the list of vertices at this height.', - 'spring': 'Use spring layout to finalize the current layout.', - 'tree_root': 'A vertex designation for drawing trees. A vertex of the tree to be used as the root for the ``layout=\'tree\'`` option. If no root is specified, then one is chosen close to the center of the tree. Ignored unless ``layout=\'tree\'``', - 'tree_orientation': 'The direction of tree branches -- \'up\', \'down\', \'left\' or \'right\'.', - 'save_pos': 'Whether or not to save the computed position for the graph.', - 'dim': 'The dimension of the layout -- 2 or 3.', - 'prog': 'Which graphviz layout program to use -- one of "circo", "dot", "fdp", "neato", or "twopi".', - 'by_component': 'Whether to do the spring layout by connected component -- a boolean.', - } + 'layout': 'A layout algorithm -- one of : "acyclic", "circular" (plots the ' + 'graph with vertices evenly distributed on a circle), "ranked", ' + '"graphviz", "planar", "spring" (traditional spring layout, using the ' + 'graph\'s current positions as initial positions), or "tree" (the tree ' + 'will be plotted in levels, depending on minimum distance for the root).', + 'iterations': 'The number of times to execute the spring layout algorithm.', + 'heights': 'A dictionary mapping heights to the list of vertices at this height.', + 'spring': 'Use spring layout to finalize the current layout.', + 'tree_root': 'A vertex designation for drawing trees. A vertex of the tree ' + 'to be used as the root for the ``layout=\'tree\'`` option. If no root ' + 'is specified, then one is chosen close to the center of the tree. ' + 'Ignored unless ``layout=\'tree\'``.', + 'forest_roots': 'An iterable specifying which vertices to use as roots for ' + 'the ``layout=\'forest\'`` option. If no root is specified for a tree, ' + 'then one is chosen close to the center of the tree. ' + 'Ignored unless ``layout=\'forest\'``.', + 'tree_orientation': 'The direction of tree branches -- \'up\', \'down\', ' + '\'left\' or \'right\'.', + 'save_pos': 'Whether or not to save the computed position for the graph.', + 'dim': 'The dimension of the layout -- 2 or 3.', + 'prog': 'Which graphviz layout program to use -- one of "circo", "dot", ' + '"fdp", "neato", or "twopi".', + 'by_component': 'Whether to do the spring layout by connected component ' + '-- a boolean.', + } graphplot_options = layout_options.copy() @@ -157,15 +171,14 @@ 'graph_border': 'Whether or not to draw a frame around the graph.', 'edge_labels_background' : 'The color of the background of the edge labels'}) -from six import iteritems _PLOT_OPTIONS_TABLE = "" -for key, value in iteritems(graphplot_options): +for key, value in graphplot_options.items(): _PLOT_OPTIONS_TABLE += " ``"+str(key)+"`` | "+str(value)+"\n" __doc__ = __doc__.format(PLOT_OPTIONS_TABLE=_PLOT_OPTIONS_TABLE) -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2009 Emily Kirkman # 2009 Robert L. Miller # @@ -178,12 +191,11 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.structure.sage_object import SageObject from sage.plot.all import Graphics, scatter_plot, bezier_path, line, arrow, text, circle from math import sqrt, cos, sin, atan, pi -from six import text_type as str DEFAULT_SHOW_OPTIONS = { "figsize" : [4,4] @@ -248,7 +260,7 @@ def __init__(self, graph, options): """ # Setting the default values if needed - for k, value in iteritems(DEFAULT_PLOT_OPTIONS): + for k, value in DEFAULT_PLOT_OPTIONS.items(): if k not in options: options[k] = value self._plot_components = {} @@ -307,7 +319,7 @@ def set_pos(self): Graphics object consisting of 14 graphics primitives .. PLOT:: - + g = Graph({0:[1,2], 2:[3], 4:[0,1]}) g.graphplot(save_pos=True, layout='circular') # indirect doctest T = list(graphs.trees(7)) @@ -339,7 +351,7 @@ def set_pos(self): self._pos = self._graph.layout(**self._options) # make sure the positions are floats (trac #10124) self._pos = {k: (float(v[0]), float(v[1])) - for k, v in iteritems(self._pos)} + for k, v in self._pos.items()} def set_vertices(self, **vertex_options): """ @@ -368,7 +380,7 @@ def set_vertices(self, **vertex_options): g = Graph({}, loops=True, multiedges=True, sparse=True) g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'),(0,1,'e'),(0,1,'f'), (0,1,'f'),(2,1,'g'),(2,2,'h')]) - GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, + GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, edge_style='dashed') GP.set_vertices(talk=True) sphinx_plot(GP) @@ -385,8 +397,6 @@ def set_vertices(self, **vertex_options): sphinx_plot(GP) """ - from sage.misc.superseded import deprecation - # Handle base vertex options voptions = {} @@ -407,10 +417,6 @@ def set_vertices(self, **vertex_options): else: vertex_color = self._options['vertex_color'] - if ('vertex_colors' in self._options and - not isinstance(self._options['vertex_colors'], dict)): - deprecation(21048, "Use of vertex_colors= is deprecated, use vertex_color= and/or vertex_colors=.") - if 'vertex_colors' not in self._options or self._options['vertex_colors'] is None: if self._options['partition'] is not None: from sage.plot.colors import rainbow @@ -598,7 +604,8 @@ def set_edges(self, **edge_options): """ for arg in edge_options: self._options[arg] = edge_options[arg] - if 'edge_colors' in edge_options: self._options['color_by_label'] = False + if 'edge_colors' in edge_options: + self._options['color_by_label'] = False if self._options['edge_labels_background'] == "transparent": self._options['edge_labels_background'] = "None" @@ -669,10 +676,10 @@ def append_or_set(key, label, color, head): # Add unspecified edges (default color black set in DEFAULT_PLOT_OPTIONS) for edge in self._graph.edge_iterator(): - if (edge[0], edge[1], edge[2]) not in edges_drawn and \ - ( self._graph.is_directed() or - (edge[1], edge[0], edge[2]) not in edges_drawn - ): + if ((edge[0], edge[1], edge[2]) not in edges_drawn and + (self._graph.is_directed() or + (edge[1], edge[0], edge[2]) not in edges_drawn + )): if v_to_int[edge[0]] < v_to_int[edge[1]]: key = (edge[0], edge[1]) head = 1 @@ -893,7 +900,7 @@ def show(self, **kwds): """ # Setting the default values if needed - for k, value in iteritems(DEFAULT_SHOW_OPTIONS): + for k, value in DEFAULT_SHOW_OPTIONS.items(): if k not in kwds: kwds[k] = value @@ -945,12 +952,12 @@ def plot(self, **kwds): x = float(cos(pi/2 + ((2*pi)/5)*i)) y = float(sin(pi/2 + ((2*pi)/5)*i)) pos_dict[i] = [x,y] - + for i in range(5,10): x = float(0.5*cos(pi/2 + ((2*pi)/5)*i)) y = float(0.5*sin(pi/2 + ((2*pi)/5)*i)) pos_dict[i] = [x,y] - + pl = P.graphplot(pos=pos_dict, vertex_colors=d) sphinx_plot(pl) diff --git a/src/sage/graphs/graph_plot_js.py b/src/sage/graphs/graph_plot_js.py index b145920c33b..f845d7ef115 100644 --- a/src/sage/graphs/graph_plot_js.py +++ b/src/sage/graphs/graph_plot_js.py @@ -2,12 +2,15 @@ Graph plotting in Javascript with d3.js This module implements everything that can be used to draw graphs with `d3.js -`_ in Sage. +`_ in Sage. On Python's side, this is mainly done by wrapping a graph's edges and vertices in a structure that can then be used in the javascript code. This javascript code is then inserted into a .html file to be opened by a browser. +In the browser, the displayed page contains at the bottom right a menu +that allows to save the picture under the svg file format. + What Sage feeds javascript with is a "graph" object with the following content: - ``vertices`` -- each vertex is a dictionary defining : @@ -62,7 +65,7 @@ - Nathann Cohen, Brice Onfroy -- July 2013 -- Initial version of the Sage code, - Javascript code, using examples from `d3.js `_. + Javascript code, using examples from `d3.js `_. - Thierry Monteil (June 2014): allow offline use of d3.js provided by d3js spkg. @@ -98,7 +101,7 @@ def gen_html_code(G, vertex_size=7, edge_thickness=4): r""" - Creates a .html file showing the graph using `d3.js `_. + Create a .html file showing the graph using `d3.js `_. This function returns the name of the .html file. If you want to visualize the actual graph use :meth:`~sage.graphs.generic_graph.GenericGraph.show`. @@ -325,8 +328,7 @@ def gen_html_code(G, # Writes the temporary .html file filename = tmp_filename(ext='.html') - f = open(filename, 'w') - f.write(js_code) - f.close() + with open(filename, 'w') as f: + f.write(js_code) return filename diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index b222789183c..e7741b7f6f7 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -1152,8 +1152,10 @@ def hyperbolicity(G, (1/2, [0, 1, 2, 3], 1/2) sage: L,C,U = hyperbolicity(G, algorithm='basic'); L,sorted(C),U (1/2, [0, 1, 2, 3], 1/2) - sage: L,C,U = hyperbolicity(G, algorithm='dom'); L,sorted(C),U - (0, [0, 1, 2, 6], 1) + sage: L,C,U = hyperbolicity(G, algorithm='dom'); L,U + (0, 1) + sage: sorted(C) # random + [0, 1, 2, 6] Asking for an approximation in a grid graph:: diff --git a/src/sage/graphs/hypergraph_generators.py b/src/sage/graphs/hypergraph_generators.py index aa3d5e39885..eed0c6af967 100644 --- a/src/sage/graphs/hypergraph_generators.py +++ b/src/sage/graphs/hypergraph_generators.py @@ -51,7 +51,9 @@ def nauty(self, number_of_sets, number_of_vertices, INPUT: - - ``number_of_sets``, ``number_of_vertices`` -- integers. + - ``number_of_sets`` -- integer, at most 64 minus ``number_of_vertices`` + + - ``number_of_vertices`` -- integer, at most 30 - ``multiple_sets`` -- boolean (default: ``False``); whether to allow several sets of the hypergraph to be equal. @@ -79,19 +81,19 @@ def nauty(self, number_of_sets, number_of_vertices, hypergraphs to be connected. - ``debug`` -- boolean (default: ``False``); if ``True`` the first line - of genbg's output to standard error is captured and the first call to + of genbgL's output to standard error is captured and the first call to the generator's ``next()`` function will return this line as a string. A line leading with ">A" indicates a successful initiation of the program with some information on the arguments, while a line beginning with ">E" indicates an error with the input. - ``options`` -- string (default: ``""``) -- anything else that should - be forwarded as input to Nauty's genbg. See its documentation for more + be forwarded as input to Nauty's genbgL. See its documentation for more information : ``_. .. NOTE:: - For genbg the *first class* elements are vertices, and *second + For genbgL the *first class* elements are vertices, and *second class* elements are the hypergraph's sets. OUTPUT: @@ -128,7 +130,25 @@ def nauty(self, number_of_sets, number_of_vertices, sage: fano = next(hypergraphs.nauty(7, 7, regular=3, max_intersection=1)) sage: print(fano) ((0, 1, 2), (0, 3, 4), (0, 5, 6), (1, 3, 5), (2, 4, 5), (2, 3, 6), (1, 4, 6)) + + TESTS:: + + sage: len(list(hypergraphs.nauty(20, 20, uniform=2, regular=2,max_intersection=1))) + 49 + sage: list(hypergraphs.nauty(40, 40, uniform=2, regular=2,max_intersection=1)) + Traceback (most recent call last): + ... + ValueError: cannot have more than 30 vertices + sage: list(hypergraphs.nauty(40, 30, uniform=2, regular=2,max_intersection=1)) + Traceback (most recent call last): + ... + ValueError: cannot have more than 64 sets+vertices """ + if number_of_vertices > 30: + raise ValueError("cannot have more than 30 vertices") + if number_of_sets + number_of_vertices > 64: + raise ValueError("cannot have more than 64 sets+vertices") + import subprocess nauty_input = options @@ -162,7 +182,7 @@ def nauty(self, number_of_sets, number_of_vertices, nauty_input += " " + str(number_of_vertices) + " " + str(number_of_sets) + " " - sp = subprocess.Popen(nautyprefix+"genbg {0}".format(nauty_input), shell=True, + sp = subprocess.Popen(nautyprefix + "genbgL {0}".format(nauty_input), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) @@ -251,7 +271,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 +282,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/independent_sets.pyx b/src/sage/graphs/independent_sets.pyx index 63cf7b7ac97..35fb6562fe8 100644 --- a/src/sage/graphs/independent_sets.pyx +++ b/src/sage/graphs/independent_sets.pyx @@ -370,7 +370,7 @@ cdef class IndependentSets: True """ if not self.n: - return S == [] + return not S cdef int i # Set of vertices as a bitset diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index 9cd43d11b4d..b1023c093cb 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -393,13 +393,14 @@ class is defined by the exclusion of subgraphs, one can write a generic """ from __future__ import print_function -from six import itervalues - from sage.structure.sage_object import SageObject from sage.structure.unique_representation import CachedRepresentation, UniqueRepresentation from sage.misc.unknown import Unknown from sage.env import GRAPHS_DATA_DIR -import six + +import os +import zipfile +from urllib.request import urlopen #***************************************************************************** # Copyright (C) 2011 Nathann Cohen @@ -499,9 +500,8 @@ def __ge__(self, other): sage: graph_classes.Chordal >= graph_classes.Tree True """ - inclusion_digraph = GraphClasses().inclusion_digraph() - if inclusion_digraph.shortest_path(self._gc_id,other._gc_id) != []: + if inclusion_digraph.shortest_path(self._gc_id,other._gc_id): return True else: return Unknown @@ -826,16 +826,11 @@ def _download_db(self): sage: graph_classes._download_db() # Not tested -- requires internet """ - # import compatible with py2 and py3 - from six.moves.urllib.request import urlopen - from sage.misc.misc import SAGE_TMP - import os.path u = urlopen('http://www.graphclasses.org/data.zip') localFile = open(os.path.join(SAGE_TMP, 'isgci.zip'), 'w') localFile.write(u.read()) localFile.close() - import os, zipfile z = zipfile.ZipFile(os.path.join(SAGE_TMP, 'isgci.zip')) # Save a systemwide updated copy whenever possible @@ -871,7 +866,7 @@ def _parse_db(self, directory): DB = _XML_to_dict(root) classes = {c['id']: c for c in DB['GraphClasses']["GraphClass"]} - for c in itervalues(classes): + for c in classes.values(): c["problem"] = {pb.pop("name"): pb for pb in c["problem"]} inclusions = DB['Inclusions']['incl'] @@ -1005,7 +1000,7 @@ def sort_key(x): MAX[key] = len(max((str(x.get(key, "")) for x in classes_list), key=len)) # At most MAX characters per field - for key, length in six.iteritems(MAX): + for key, length in MAX.items(): MAX[key] = min(length, MAX_LEN) # Head of the table diff --git a/src/sage/graphs/isoperimetric_inequalities.pyx b/src/sage/graphs/isoperimetric_inequalities.pyx new file mode 100644 index 00000000000..8bb0abd931b --- /dev/null +++ b/src/sage/graphs/isoperimetric_inequalities.pyx @@ -0,0 +1,450 @@ +# cython: binding = True +r""" +Isoperimetric inequalities + +This module contains various functions to compute isoperimetric numbers +of a graph. + +Authors: + +- Peleg Michaeli +- Vincent Delecroix +""" +#***************************************************************************** +# Copyright (C) 2018 Vincent Delecroix <20100.delecroix@gmail.com> +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from __future__ import print_function + +from cysignals.signals cimport sig_on, sig_off +from cysignals.memory cimport check_malloc, sig_free + +from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph, out_degree +include "sage/data_structures/binary_matrix.pxi" +from sage.graphs.base.static_dense_graph cimport dense_graph_init + +from sage.rings.infinity import Infinity +from sage.rings.rational_field import QQ + +def cheeger_constant(g): + r""" + Return the cheeger constant of the graph. + + The Cheeger constant of a graph `G = (V,E)` is the minimum of `|\partial S| + / |Vol(S)|` where `Vol(S)` is the sum of degrees of element in `S`, + `\partial S` is the edge boundary of `S` (number of edges with one end in + `S` and one end in `V \setminus S`) and the minimum is taken over all + non-empty subsets `S` of vertices so that `|Vol(S)| \leq |E|`. + + .. SEEALSO:: + + Alternative but similar quantities can be obtained via the methods + :meth:`edge_isoperimetric_number` and :meth:`vertex_isoperimetric_number`. + + EXAMPLES:: + + sage: graphs.PetersenGraph().cheeger_constant() + 1/3 + + The Cheeger constant of a cycle on `n` vertices is + `1/\lfloor n/2 \rfloor`:: + + sage: [graphs.CycleGraph(k).cheeger_constant() for k in range(2,10)] + [1, 1, 1/2, 1/2, 1/3, 1/3, 1/4, 1/4] + + The Cheeger constant of a complete graph on `n` vertices is + `\lceil n/2 \rceil / (n-1)`:: + + sage: [graphs.CompleteGraph(k).cheeger_constant() for k in range(2,10)] + [1, 1, 2/3, 3/4, 3/5, 2/3, 4/7, 5/8] + + For complete bipartite:: + + sage: [graphs.CompleteBipartiteGraph(i,j).cheeger_constant() for i in range(2,7) for j in range(2, i)] + [3/5, 1/2, 3/5, 5/9, 4/7, 5/9, 1/2, 5/9, 1/2, 5/9] + + More examples:: + + sage: G = Graph([(0, 1), (0, 3), (0, 8), (1, 4), (1, 6), (2, 4), (2, 7), (2, 9), + ....: (3, 6), (3, 8), (4, 9), (5, 6), (5, 7), (5, 8), (7, 9)]) + sage: G.cheeger_constant() + 1/6 + + sage: G = Graph([(0, 1), (0, 2), (1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (3, 4), (3, 5)]) + sage: G.cheeger_constant() + 1/2 + + sage: Graph([[1,2,3,4],[(1,2),(3,4)]]).cheeger_constant() + 0 + + TESTS:: + + sage: graphs.EmptyGraph().cheeger_constant() + Traceback (most recent call last): + ... + ValueError: Cheeger constant is not defined for the empty graph + """ + if g.is_directed(): + raise ValueError("Cheeger constant is only defined on non-oriented graph") + g._scream_if_not_simple() + if g.num_verts() == 0: + raise ValueError("Cheeger constant is not defined for the empty graph") + elif g.num_verts() == 1: + return Infinity + elif not g.is_connected(): + return QQ((0,1)) + + cdef short_digraph sd # a copy of the graph g + cdef int * subgraph # vertices of the subgraph (stack) + cdef int * bitsubgraph # vertices of the subgraph (bit array of +1 (in) or -1 (not in)) + cdef int k = 0 # number of vertices in subgraph + cdef int vol = 0 # number of edges in the subgraph + cdef int boundary = 0 # number of edges in the boundary + cdef int u = 0 # current vertex + cdef unsigned long bmin = 1 # value of boundary for the min + cdef unsigned long vmin = 1 # value of the volume for the min + cdef int i + + init_short_digraph(sd, g) + + subgraph = check_malloc(sd.n * sizeof(int)) + bitsubgraph = check_malloc(sd.n * sizeof(int)) + + sig_on() + try: + for i in range(sd.n): + bitsubgraph[i] = -1 + while True: + while True: + # add vertex u to the subgraph, update the boundary/volume + # we repeat the operation until we reach the maximum volume + # or have no more vertex available + for i in range(sd.neighbors[u+1] - sd.neighbors[u]): + boundary -= bitsubgraph[sd.neighbors[u][i]] + vol += 1 + + bitsubgraph[u] = 1 + subgraph[k] = u + u += 1 + k += 1 + + if vol > sd.m: + break + + if boundary * vmin < bmin * vol: + bmin = boundary + vmin = vol + + if u == sd.n: + break + + # backtrack + k -= 1 + u = subgraph[k] + + bitsubgraph[u] = -1 + for i in range(sd.neighbors[u+1] - sd.neighbors[u]): + boundary += bitsubgraph[sd.neighbors[u][i]] + vol -= 1 + u += 1 + + if u == sd.n: + if k == 0: + # end of the loop + break + else: + # remove one more vertex in order to continue + k -= 1 + u = subgraph[k] + + bitsubgraph[u] = -1 + for i in range(sd.neighbors[u+1] - sd.neighbors[u]): + boundary += bitsubgraph[sd.neighbors[u][i]] + vol -= 1 + u += 1 + return QQ((bmin, vmin)) + + finally: + free_short_digraph(sd) + sig_free(subgraph) + sig_free(bitsubgraph) + sig_off() + +def edge_isoperimetric_number(g): + r""" + Return the edge-isoperimetric number of the graph. + + The edge-isoperimetric number of a graph `G = (V,E)` is also sometimes + called the *isoperimetric number*. It is defined as the minimum of + `|\partial S| / |S|` where `\partial S` is the edge boundary of `S` (number + of edges with one end in `S` and one end in `V \setminus S`) and the + minimum is taken over all subsets of vertices whose cardinality does not + exceed half the size `|V|` of the graph. + + .. SEEALSO:: + + Alternative but similar quantities can be obtained via the methods + :meth:`cheeger_constant` and :meth:`vertex_isoperimetric_number`. + + EXAMPLES: + + The edge-isoperimetric number of a complete graph on `n` vertices is + `\lceil n/2 \rceil`:: + + sage: [graphs.CompleteGraph(n).edge_isoperimetric_number() for n in range(2,10)] + [1, 2, 2, 3, 3, 4, 4, 5] + + The edge-isoperimetric constant of a cycle on `n` vertices is + `2/\lfloor n/2 \rfloor`:: + + sage: [graphs.CycleGraph(n).edge_isoperimetric_number() for n in range(2,15)] + [1, 2, 1, 1, 2/3, 2/3, 1/2, 1/2, 2/5, 2/5, 1/3, 1/3, 2/7] + + In general, for `d`-regular graphs the edge-isoperimetric number is + `d` times larger than the Cheeger constant of the graph:: + + sage: g = graphs.RandomRegular(3, 10) + sage: g.edge_isoperimetric_number() == g.cheeger_constant() * 3 + True + + And the edge-isoperimetric constant of a disconnected graph is `0`:: + + sage: Graph([[1,2,3,4],[(1,2),(3,4)]]).edge_isoperimetric_number() + 0 + + TESTS:: + + sage: graphs.EmptyGraph().edge_isoperimetric_number() + Traceback (most recent call last): + ... + ValueError: edge-isoperimetric number not defined for the empty graph + """ + if g.is_directed(): + raise ValueError("edge isoperimetric number is only defined on non-oriented graph") + g._scream_if_not_simple() + if g.num_verts() == 0: + raise ValueError("edge-isoperimetric number not defined for the empty graph") + elif g.num_verts() == 1: + return Infinity + elif not g.is_connected(): + return QQ((0,1)) + + cdef short_digraph sd # a copy of the graph g + cdef int * subgraph # vertices of the subgraph (stack) + cdef int * bitsubgraph # vertices of the subgraph (bit array of +1 (in) or -1 (not in)) + cdef int k = 0 # number of vertices in subgraph + cdef unsigned long vol = 0 # number of edges in the subgraph + cdef unsigned long boundary = 0 # number of edges in the boundary + cdef int u = 0 # current vertex + cdef int i + + init_short_digraph(sd, g) + + cdef unsigned long bmin = sd.neighbors[1] - sd.neighbors[0] # value of boundary for the min + cdef unsigned long vmin = 1 # value of the volume for the min + + subgraph = check_malloc(sd.n * sizeof(int)) + bitsubgraph = check_malloc(sd.n * sizeof(int)) + + sig_on() + try: + for i in range(sd.n): + bitsubgraph[i] = -1 + + while True: + while True: + # add vertex u to the subgraph, update the boundary/volume + # we repeat the operation until we reach the maximum volume + # or have no more vertex available + for i in range(sd.neighbors[u+1] - sd.neighbors[u]): + boundary -= bitsubgraph[sd.neighbors[u][i]] + vol += 1 + + bitsubgraph[u] = 1 + subgraph[k] = u + u += 1 + k += 1 + + if 2*vol > sd.n: + break + + if boundary * vmin < bmin * vol: + bmin = boundary + vmin = vol + + if u == sd.n: + break + + # backtrack + k -= 1 + u = subgraph[k] + + bitsubgraph[u] = -1 + for i in range(sd.neighbors[u+1] - sd.neighbors[u]): + boundary += bitsubgraph[sd.neighbors[u][i]] + vol -= 1 + u += 1 + + if u == sd.n: + if k == 0: + # end of the loop + break + else: + # remove one more vertex in order to continue + k -= 1 + u = subgraph[k] + + bitsubgraph[u] = -1 + for i in range(sd.neighbors[u+1] - sd.neighbors[u]): + boundary += bitsubgraph[sd.neighbors[u][i]] + vol -= 1 + u += 1 + + return QQ((bmin, vmin)) + + finally: + sig_off() + free_short_digraph(sd) + sig_free(subgraph) + sig_free(bitsubgraph) + +def vertex_isoperimetric_number(g): + r""" + Return the vertex-isoperimetric number of the graph. + + The vertex-isoperimetric number of a graph `G = (V,E)` is also sometimes + called the *magnifying constant*. It is defined as the minimum of `|N(S)| / + |S|` where `|N(S)|` is the vertex boundary of `S` and the minimum is taken + over the subsets `S` of vertices of size at most half of the vertices. + + .. SEEALSO:: + + Alternative but similar quantities can be obtained via the methods + :meth:`cheeger_constant` and :meth:`edge_isoperimetric_number`. + + EXAMPLES: + + The vertex-isoperimetric number of a complete graph on `n` vertices is + `\lceil n/2 \rceil/\lfloor n/2 \rfloor`:: + + sage: [graphs.CompleteGraph(k).vertex_isoperimetric_number() for k in range(2,15)] + [1, 2, 1, 3/2, 1, 4/3, 1, 5/4, 1, 6/5, 1, 7/6, 1] + + The vertex-isoperimetric number of a cycle on `n` vertices is + `2/\lfloor n/2 \rfloor`:: + + sage: [graphs.CycleGraph(k).vertex_isoperimetric_number() for k in range(2,15)] + [1, 2, 1, 1, 2/3, 2/3, 1/2, 1/2, 2/5, 2/5, 1/3, 1/3, 2/7] + + And the vertex-isoperimetric number of a disconnected graph is `0`:: + + sage: Graph([[1,2,3],[(1,2)]]).vertex_isoperimetric_number() + 0 + + The vertex-isoperimetric number is independent of edge multiplicity:: + + sage: G = graphs.CycleGraph(6) + sage: G.vertex_isoperimetric_number() + 2/3 + sage: G.allow_multiple_edges(True) + sage: G.add_edges(G.edges()) + sage: G.vertex_isoperimetric_number() + 2/3 + + TESTS:: + + sage: graphs.EmptyGraph().vertex_isoperimetric_number() + Traceback (most recent call last): + ... + ValueError: vertex-isoperimetric number not defined for the empty graph + """ + if g.is_directed(): + raise ValueError("vertex-isoperimetric number is only defined on non-oriented graph") + if not g: + raise ValueError("vertex-isoperimetric number not defined for the empty graph") + elif g.order() == 1: + return Infinity + elif not g.is_connected(): + return QQ((0, 1)) + + # We convert the graph to a static dense graph + cdef binary_matrix_t DG + dense_graph_init(DG, g) + cdef int n = g.order() + cdef int k = n / 2 # maximum size of a subset + + sig_on() + + # We use a stack of bitsets. For that, we need 3 bitsets per level with at + # most n/2 + 1 levels, so 3 * (n / 2) + 3 bitsets. We also need another + # bitset that we create at the same time, so overall 3 * (n / 2) + 4 bitsets + cdef binary_matrix_t stack + binary_matrix_init(stack, 3 * (n / 2) + 4, n) + + cdef bitset_t candidates = stack.rows[3 * (n / 2) + 3] + cdef bitset_t left # vertices not yet explored + cdef bitset_t current # vertices in the current subset + cdef bitset_t boundary # union of neighbors of vertices in current subset + + cdef int l = 0 + cdef int p = n + cdef int q = 0 + cdef int c, b, v + + # We initialize the first level + for v in range(3): + bitset_clear(stack.rows[v]) + bitset_complement(stack.rows[0], stack.rows[0]) + + while l >= 0: + + # We take the values at the top of the stack + left = stack.rows[l] + current = stack.rows[l + 1] + boundary = stack.rows[l + 2] + + if bitset_isempty(current): + bitset_copy(candidates, left) + else: + bitset_and(candidates, left, boundary) + + if bitset_isempty(candidates): + # We decrease l to pop the stack + l -= 3 + + # If the current set if non empty, we update the lower bound + c = bitset_len(current) + if c: + bitset_difference(boundary, boundary, current) + b = bitset_len(boundary) + if b * q < p * c: + p = b + q = c + + else: + # Choose a candidate vertex v + v = bitset_first(candidates) + bitset_discard(left, v) + + # Since we have not modified l, the bitsets for iterating without v + # in the subset current are now at the top of the stack, with + # correct values + + if bitset_len(current) < k: + # We continue with v in the subset current + l += 3 + bitset_copy(stack.rows[l], left) + bitset_copy(stack.rows[l + 1], current) + bitset_add(stack.rows[l + 1], v) + bitset_union(stack.rows[l + 2], boundary, DG.rows[v]) + + binary_matrix_free(stack) + binary_matrix_free(DG) + sig_off() + + return QQ((p, q)) diff --git a/src/sage/graphs/line_graph.pyx b/src/sage/graphs/line_graph.pyx index 714e2531272..262b293c529 100644 --- a/src/sage/graphs/line_graph.pyx +++ b/src/sage/graphs/line_graph.pyx @@ -10,9 +10,9 @@ amounts to the following functions : :widths: 30, 70 :delim: | - :meth:`line_graph` | Computes the line graph of a given graph + :meth:`line_graph` | Return the line graph of a given graph :meth:`is_line_graph` | Check whether a graph is a line graph - :meth:`root_graph` | Computes the root graph corresponding to the given graph + :meth:`root_graph` | Return the root graph corresponding to the given graph Author: @@ -127,9 +127,10 @@ Functions --------- """ + def is_line_graph(g, certificate=False): r""" - Tests wether the graph is a line graph. + Check whether the graph `g` is a line graph. INPUT: @@ -203,6 +204,12 @@ def is_line_graph(g, certificate=False): sage: new_g = gl.is_line_graph(certificate=True)[1] sage: g.line_graph().is_isomorphic(gl) True + + Verify that :trac:`29740` is fixed:: + + sage: g = Graph('O{e[{}^~z`MDZBZBkXzE^') + sage: g.is_line_graph() + False """ g._scream_if_not_simple() @@ -219,31 +226,38 @@ def is_line_graph(g, certificate=False): if g.is_connected(): try: + # To check whether g is a line graph, we try to construct its root + # graph R, isom = root_graph(g) if certificate: return True, R, isom else: return True - except ValueError: - # g is not a line graph - if certificate: - return False, get_certificate(g) - else: - return False + except ValueError as VE: + if str(VE) == "this graph is not a line graph !": + # g is not a line graph + if certificate: + return False, get_certificate(g) + else: + return False + raise VE - # g is not connected. + # g is not connected, so we apply the above procedure to each connected + # component from sage.graphs.graph import Graph R = Graph() for gg in g.connected_components_subgraphs(): try: RR, isom = root_graph(gg) R += RR - except ValueError: - # gg is not a line graph - if certificate: - return False, get_certificate(gg) - else: - return False + except ValueError as VE: + if str(VE) == "this graph is not a line graph !": + # gg is not a line graph + if certificate: + return False, get_certificate(gg) + else: + return False + raise VE if certificate: _, isom = g.is_isomorphic(R.line_graph(labels=False), certificate=True) @@ -252,9 +266,9 @@ def is_line_graph(g, certificate=False): return True -def line_graph(self, labels=True): +def line_graph(g, labels=True): """ - Returns the line graph of the (di)graph. + Return the line graph of the (di)graph ``g``. INPUT: @@ -341,15 +355,15 @@ def line_graph(self, labels=True): cdef dict conflicts = {} cdef list elist = [] - self._scream_if_not_simple() - if self._directed: + g._scream_if_not_simple() + if g._directed: from sage.graphs.digraph import DiGraph G = DiGraph() - G.add_vertices(self.edge_iterator(labels=labels)) - for v in self: + G.add_vertices(g.edge_iterator(labels=labels)) + for v in g: # Connect appropriate incident edges of the vertex v - G.add_edges((e, f) for e in self.incoming_edge_iterator(v, labels=labels) - for f in self.outgoing_edge_iterator(v, labels=labels)) + G.add_edges((e, f) for e in g.incoming_edge_iterator(v, labels=labels) + for f in g.outgoing_edge_iterator(v, labels=labels)) return G else: from sage.graphs.all import Graph @@ -365,7 +379,7 @@ def line_graph(self, labels=True): # 1) List of vertices in the line graph - for e in self.edge_iterator(labels=labels): + for e in g.edge_iterator(labels=labels): if hash(e[0]) < hash(e[1]): elist.append(e) elif hash(e[0]) > hash(e[1]): @@ -379,11 +393,11 @@ def line_graph(self, labels=True): G.add_vertices(elist) # 2) adjacencies in the line graph - for v in self: + for v in g: elist = [] # Add the edge to the list, according to hashes, as previously - for e in self.edge_iterator(v, labels=labels): + for e in g.edge_iterator(v, labels=labels): if hash(e[0]) < hash(e[1]): elist.append(e) elif hash(e[0]) > hash(e[1]): @@ -402,7 +416,7 @@ def line_graph(self, labels=True): def root_graph(g, verbose=False): r""" - Computes the root graph corresponding to the given graph + Return the root graph corresponding to the given graph ``g`` See the documentation of :mod:`sage.graphs.line_graph` to know how it works. @@ -413,16 +427,10 @@ def root_graph(g, verbose=False): - ``verbose`` -- boolean (default: ``False``); display some information about what is happening inside of the algorithm. - .. NOTE:: - - It is best to use this code through - :meth:`~sage.graphs.graph.Graph.is_line_graph`, which first checks that - the graph is indeed a line graph, and deals with the disconnected - case. But if you are sure of yourself, dig in ! - .. WARNING:: - * This code assumes that the graph is connected. + This code assumes that `g` is a line graph, and is a connected, + undirected graph without multiple edges. TESTS: @@ -442,13 +450,19 @@ def root_graph(g, verbose=False): sage: root_graph(graphs.PetersenGraph()) Traceback (most recent call last): ... - ValueError: This graph is not a line graph ! + ValueError: this graph is not a line graph ! + + sage: root_graph(Graph('O{e[{}^~z`MDZBZBkXzE^')) + Traceback (most recent call last): + ... + ValueError: this graph is not a line graph ! Small corner-cases:: sage: from sage.graphs.line_graph import root_graph sage: root_graph(graphs.CompleteGraph(3)) - (Complete bipartite graph of order 1+3: Graph on 4 vertices, {0: (0, 1), 1: (0, 2), 2: (0, 3)}) + (Complete bipartite graph of order 1+3: Graph on 4 vertices, + {0: (0, 1), 1: (0, 2), 2: (0, 3)}) sage: G, D = root_graph(graphs.OctahedralGraph()); G Complete graph: Graph on 4 vertices sage: G, D = root_graph(graphs.DiamondGraph()); G @@ -464,6 +478,8 @@ def root_graph(g, verbose=False): raise ValueError("g cannot have multiple edges !") if not g.is_connected(): raise ValueError("g is not connected !") + # is_line_graph expects a particular error message when g is not a line graph + not_line_graph = "this graph is not a line graph !" # Complete Graph ? if g.is_clique(): @@ -497,10 +513,6 @@ def root_graph(g, verbose=False): # From now on we can assume (thanks to Beineke) that no edge belongs to two # even triangles at once. - error_message = ("It looks like there is a problem somewhere. You" - "found a bug here ! Please report it on sage-devel," - "our google group !") - # Better to work on integers... Everything takes more time otherwise. G = g.relabel(inplace=False) @@ -546,7 +558,7 @@ def root_graph(g, verbose=False): # We now associate the clique to all the vertices it contains. for v in S: if len(v_cliques[v]) == 2: - raise ValueError("This graph is not a line graph !") + raise ValueError(not_line_graph) v_cliques[v].append(tuple(S)) if verbose: @@ -566,7 +578,7 @@ def root_graph(g, verbose=False): # If edge xy does not appear in any of the cliques associated with y if all(x not in C for C in v_cliques[y]): if len(v_cliques[y]) >= 2 or len(v_cliques[x]) >= 2: - raise ValueError("This graph is not a line graph !") + raise ValueError("this graph is not a line graph !") v_cliques[x].append((x, y)) v_cliques[y].append((x, y)) @@ -610,19 +622,14 @@ def root_graph(g, verbose=False): # We now build R R.add_edges(vertex_to_map.values()) - # Even if whatever is written above is complete nonsense, here we make sure - # that we do not return gibberish. Is the line graph of R isomorphic to the - # input ? If so, we return R, and the isomorphism. Else, we panic and - # scream. - # - # It's actually "just to make sure twice". This can be removed later if it - # turns out to be too costly. + # If g is a line graph, then it is isomorphic to the line graph of the graph + # R that we have constructed, so we return R (and the isomorphism). is_isom, isom = g.is_isomorphic(R.line_graph(labels=False), certificate=True) + if is_isom: + return R, isom + else: + raise ValueError(not_line_graph) - if not is_isom: - raise Exception(error_message) - - return R, isom diff --git a/src/sage/graphs/linearextensions.py b/src/sage/graphs/linearextensions.py deleted file mode 100644 index ae94b513de6..00000000000 --- a/src/sage/graphs/linearextensions.py +++ /dev/null @@ -1,195 +0,0 @@ -""" -Linear Extensions of Directed Acyclic Graphs - -A linear extension of a directed acyclic graph is a total (linear) ordering on -the vertices that is compatible with the graph in the following sense: -if there is a path from x to y in the graph, the x appears before y in the -linear extension. - -The algorithm implemented in this module is from "Generating Linear Extensions -Fast" by Preusse and Ruskey, which can be found at -http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.52.3057 . The algorithm -generates the extensions in constant amortized time (CAT) -- a constant amount -of time per extension generated, or linear in the number of extensions -generated. - -EXAMPLES: - -Here we generate the 5 linear extensions of the following directed -acyclic graph:: - - sage: from sage.graphs.linearextensions import LinearExtensions - sage: D = DiGraph({ 0:[1,2], 1:[3], 2:[3,4] }) - sage: D.is_directed_acyclic() - True - sage: LinearExtensions(D).list() - doctest:...: DeprecationWarning: LinearExtensions is deprecated; use FinitePoset.linear_extensions or DiGraph.topological_sort_generator instead - See https://trac.sagemath.org/25864 for details. - [[0, 1, 2, 3, 4], - [0, 1, 2, 4, 3], - [0, 2, 1, 3, 4], - [0, 2, 1, 4, 3], - [0, 2, 4, 1, 3]] - -Notice how all of the total orders are compatible with the ordering -induced from the graph. - -We can also get at the linear extensions directly from the graph. From -the graph, the linear extensions are known as topological sorts :: - - sage: list(D.topological_sort_generator()) - [[0, 1, 2, 3, 4], - [0, 2, 1, 3, 4], - [0, 2, 1, 4, 3], - [0, 2, 4, 1, 3], - [0, 1, 2, 4, 3]] - -""" -#***************************************************************************** -# Copyright (C) 2008 Mike Hansen -# -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** -from sage.combinat.combinat import CombinatorialClass - -class LinearExtensionsOld(CombinatorialClass): - def __init__(self, dag): - r""" - Creates an object representing the class of all linear extensions - of the directed acyclic graph \code{dag}. - - EXAMPLES:: - - sage: from sage.graphs.linearextensions import LinearExtensions - sage: D = DiGraph({ 0:[1,2], 1:[3], 2:[3,4] }) - sage: l = LinearExtensions(D) - doctest:...: DeprecationWarning: LinearExtensions is deprecated; use FinitePoset.linear_extensions or DiGraph.topological_sort_generator instead - See https://trac.sagemath.org/25864 for details. - - sage: l == loads(dumps(l)) - True - - TESTS:: - - sage: list(LinearExtensions(DiGraph({ }))) - [[]] - - sage: LinearExtensions(DiGraph({ 0:[1], 1:[0] })) - Traceback (most recent call last): - ... - ValueError: The graph is not directed acyclic - - """ - from sage.combinat.posets.posets import Poset - self._dag = Poset(dag) # this returns a copy - - def _repr_(self): - """ - TESTS:: - - sage: from sage.graphs.linearextensions import LinearExtensions - sage: D = DiGraph({ 0:[1,2], 1:[3], 2:[3,4] }) - sage: LinearExtensions(D) - doctest:...: DeprecationWarning: LinearExtensions is deprecated; use FinitePoset.linear_extensions or DiGraph.topological_sort_generator instead - See https://trac.sagemath.org/25864 for details. - Linear extensions of Finite poset containing 5 elements - - """ - return "Linear extensions of %s"%self._dag - - def list(self): - """ - Returns a list of the linear extensions of the directed acyclic graph. - - EXAMPLES:: - - sage: from sage.graphs.linearextensions import LinearExtensions - sage: D = DiGraph({ 0:[1,2], 1:[3], 2:[3,4] }) - sage: LinearExtensions(D).list() - doctest:...: DeprecationWarning: LinearExtensions is deprecated; use FinitePoset.linear_extensions or DiGraph.topological_sort_generator instead - See https://trac.sagemath.org/25864 for details. - [[0, 1, 2, 3, 4], - [0, 1, 2, 4, 3], - [0, 2, 1, 3, 4], - [0, 2, 1, 4, 3], - [0, 2, 4, 1, 3]] - - TESTS:: - - sage: D = DiGraph({ "a":["b","c"], "b":["d"], "c":["d","e"] }) - sage: LinearExtensions(D).list() - [['a', 'b', 'c', 'd', 'e'], - ['a', 'b', 'c', 'e', 'd'], - ['a', 'c', 'b', 'd', 'e'], - ['a', 'c', 'b', 'e', 'd'], - ['a', 'c', 'e', 'b', 'd']] - - sage: D = DiGraph({ 4:[3,2], 3:[1], 2:[1,0] }) - sage: LinearExtensions(D).list() - [[4, 2, 0, 3, 1], - [4, 2, 3, 0, 1], - [4, 2, 3, 1, 0], - [4, 3, 2, 0, 1], - [4, 3, 2, 1, 0]] - - """ - from sage.combinat.combinat_cython import linear_extension_iterator - elts = list(self._dag) - return sorted([[elts[i] for i in e] for e in linear_extension_iterator(self._dag._hasse_diagram)]) - -def LinearExtensions(dag): - r""" - ``LinearExtensions`` is deprecated; use - :meth:`sage.combinat.posets.FinitePoset.linear_extensions` or :meth:`sage.graphs.digraph.DiGraph.topological_sort_generator` instead. - - EXAMPLES:: - - sage: D = DiGraph({ 0:[1,2], 1:[3], 2:[3,4] }) - sage: Poset(D).linear_extensions().list() - [[0, 1, 2, 3, 4], - [0, 2, 1, 3, 4], - [0, 2, 1, 4, 3], - [0, 2, 4, 1, 3], - [0, 1, 2, 4, 3]] - - sage: D.topological_sort_generator().list() - [[0, 1, 2, 3, 4], - [0, 2, 1, 3, 4], - [0, 2, 1, 4, 3], - [0, 2, 4, 1, 3], - [0, 1, 2, 4, 3]] - - sage: D = DiGraph({ "a":["b","c"], "b":["d"], "c":["d","e"] }) - sage: Poset(D).linear_extensions().list() - [['a', 'b', 'c', 'd', 'e'], - ['a', 'c', 'b', 'd', 'e'], - ['a', 'c', 'b', 'e', 'd'], - ['a', 'c', 'e', 'b', 'd'], - ['a', 'b', 'c', 'e', 'd']] - - sage: D.topological_sort_generator().list() - [['a', 'b', 'c', 'd', 'e'], - ['a', 'c', 'b', 'd', 'e'], - ['a', 'c', 'b', 'e', 'd'], - ['a', 'c', 'e', 'b', 'd'], - ['a', 'b', 'c', 'e', 'd']] - - TESTS:: - - sage: from sage.graphs.linearextensions import LinearExtensions - sage: D = DiGraph({ 0:[1,2], 1:[3], 2:[3,4] }) - sage: LinearExtensions(D).list() - doctest:...: DeprecationWarning: LinearExtensions is deprecated; use FinitePoset.linear_extensions or DiGraph.topological_sort_generator instead - See https://trac.sagemath.org/25864 for details. - [[0, 1, 2, 3, 4], - [0, 1, 2, 4, 3], - [0, 2, 1, 3, 4], - [0, 2, 1, 4, 3], - [0, 2, 4, 1, 3]] - - """ - from sage.misc.superseded import deprecation - deprecation(25864, "LinearExtensions is deprecated; use FinitePoset.linear_extensions or DiGraph.topological_sort_generator instead") - - return LinearExtensionsOld(dag) diff --git a/src/sage/graphs/mcqd.pyx b/src/sage/graphs/mcqd.pyx index 0ed4ac68959..c903ab9f9eb 100644 --- a/src/sage/graphs/mcqd.pyx +++ b/src/sage/graphs/mcqd.pyx @@ -1,3 +1,5 @@ +# distutils: language = c++ +# sage_setup: distribution = sage-mcqd from sage.ext.memory_allocator cimport MemoryAllocator diff --git a/src/sage/graphs/path_enumeration.pyx b/src/sage/graphs/path_enumeration.pyx index 5b252b06285..e2ba97db95e 100644 --- a/src/sage/graphs/path_enumeration.pyx +++ b/src/sage/graphs/path_enumeration.pyx @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # cython: binding=True +# distutils: language = c++ r""" Path Enumeration @@ -617,7 +618,7 @@ def yen_k_shortest_simple_paths(self, source, target, weight_function=None, and `m` is the number of edges and `k` is the number of shortest paths needed to find. - See [Yen1970]_ and the :wikipedia:`Yen's_algorithm` for more details on the + See [Yen1970]_ and the :wikipedia:`Yen%27s_algorithm` for more details on the algorithm. EXAMPLES:: @@ -1445,14 +1446,6 @@ def _all_paths_iterator(self, vertex, ending_vertices=None, sage: g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True) sage: pi = g._all_paths_iterator('a') - sage: for _ in range(5): # py2 - ....: print(next(pi)) # py2 - ['a', 'a'] - ['a', 'b'] - ['a', 'a', 'a'] - ['a', 'a', 'b'] - ['a', 'b', 'c'] - sage: pi = g._all_paths_iterator('a') sage: [len(next(pi)) - 1 for _ in range(5)] [1, 1, 2, 2, 2] @@ -1481,26 +1474,10 @@ def _all_paths_iterator(self, vertex, ending_vertices=None, It is possible to specify the allowed ending vertices:: - sage: pi = g._all_paths_iterator('a', ending_vertices=['c']) - sage: for _ in range(5): # py2 - ....: print(next(pi)) # py2 - ['a', 'b', 'c'] - ['a', 'a', 'b', 'c'] - ['a', 'a', 'a', 'b', 'c'] - ['a', 'b', 'c', 'd', 'c'] - ['a', 'a', 'a', 'a', 'b', 'c'] sage: pi = g._all_paths_iterator('a', ending_vertices=['c']) sage: [len(next(pi)) - 1 for _ in range(5)] [2, 3, 4, 4, 5] sage: pi = g._all_paths_iterator('a', ending_vertices=['a', 'b']) - sage: for _ in range(5): # py2 - ....: print(next(pi)) # py2 - ['a', 'a'] - ['a', 'b'] - ['a', 'a', 'a'] - ['a', 'a', 'b'] - ['a', 'a', 'a', 'a'] - sage: pi = g._all_paths_iterator('a', ending_vertices=['a', 'b']) sage: [len(next(pi)) - 1 for _ in range(5)] [1, 1, 2, 2, 3] @@ -1715,29 +1692,11 @@ def all_paths_iterator(self, starting_vertices=None, ending_vertices=None, sage: g = DiGraph({'a': ['a', 'b'], 'b': ['c'], 'c': ['d'], 'd': ['c']}, loops=True) sage: pi = g.all_paths_iterator() - sage: for _ in range(7): # py2 - ....: print(next(pi)) # py2 - ['d', 'c'] - ['b', 'c'] - ['c', 'd'] - ['a', 'a'] - ['a', 'b'] - ['a', 'a', 'a'] - ['a', 'a', 'b'] - sage: pi = g.all_paths_iterator() sage: [len(next(pi)) - 1 for _ in range(7)] [1, 1, 1, 1, 1, 2, 2] It is possible to precise the allowed starting and/or ending vertices:: - sage: pi = g.all_paths_iterator(starting_vertices=['a']) - sage: for _ in range(5): # py2 - ....: print(next(pi)) # py2 - ['a', 'a'] - ['a', 'b'] - ['a', 'a', 'a'] - ['a', 'a', 'b'] - ['a', 'b', 'c'] sage: pi = g.all_paths_iterator(starting_vertices=['a']) sage: [len(next(pi)) - 1 for _ in range(5)] [1, 1, 2, 2, 2] diff --git a/src/sage/graphs/planarity.pyx b/src/sage/graphs/planarity.pyx index c0aa813c95b..f761281c0ea 100644 --- a/src/sage/graphs/planarity.pyx +++ b/src/sage/graphs/planarity.pyx @@ -1,3 +1,4 @@ +# distutils: libraries = planarity """ Wrapper for Boyer's (C) planarity algorithm """ 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/graphs/spanning_tree.pyx b/src/sage/graphs/spanning_tree.pyx index 1572f35bac4..25d016f991b 100644 --- a/src/sage/graphs/spanning_tree.pyx +++ b/src/sage/graphs/spanning_tree.pyx @@ -227,7 +227,7 @@ cpdef kruskal(G, wfunction=None, bint check=False): If the input graph is a tree, then return its edges:: sage: T = graphs.RandomTree(randint(1, 50)) # long time - sage: sorted(T.edge_iterator()) == kruskal(T, check=True) # long time + sage: sorted(T.edge_iterator()) == sorted(kruskal(T, check=True)) # long time True If the input is not a Graph:: @@ -286,6 +286,7 @@ def kruskal_iterator(G, wfunction=None, bint check=False): weighted=G.weighted(), weight_function=wfunction) + def kruskal_iterator_from_edges(edges, union_find, weighted=False, weight_function=None): """ Return an iterator implementation of Kruskal algorithm on list of edges. @@ -342,6 +343,7 @@ def kruskal_iterator_from_edges(edges, union_find, weighted=False, weight_functi if union_find.number_of_subsets() == 1: return + def filter_kruskal(G, threshold=10000, weight_function=None, bint check=False): """ Minimum spanning tree using Filter Kruskal algorithm. @@ -401,7 +403,7 @@ def filter_kruskal(G, threshold=10000, weight_function=None, bint check=False): .. SEEALSO:: - :meth:`sage.graphs.generic_graph.GenericGraph.min_spanning_tree` - - :wikipedia:`Kruskal's_algorithm` + - :wikipedia:`Kruskal%27s_algorithm` - :func:`kruskal` - :func:`filter_kruskal_iterator` @@ -418,6 +420,7 @@ def filter_kruskal(G, threshold=10000, weight_function=None, bint check=False): """ return list(filter_kruskal_iterator(G, threshold=threshold, weight_function=weight_function, check=check)) + def filter_kruskal_iterator(G, threshold=10000, weight_function=None, bint check=False): r""" Return an iterator implementation of Filter Kruskal's algorithm. @@ -429,14 +432,14 @@ def filter_kruskal_iterator(G, threshold=10000, weight_function=None, bint check .. SEEALSO:: - :meth:`sage.graphs.generic_graph.GenericGraph.min_spanning_tree` - - :wikipedia:`Kruskal's_algorithm` + - :wikipedia:`Kruskal%27s_algorithm` - :func:`kruskal` - :func:`filter_kruskal` EXAMPLES: The edges of a minimum spanning tree of ``G``, if one exists, otherwise - returns the empty list. + returns the empty list. :: sage: from sage.graphs.spanning_tree import filter_kruskal_iterator sage: G = Graph({1:{2:28, 6:10}, 2:{3:16, 7:14}, 3:{4:12}, 4:{5:22, 7:18}, 5:{6:25, 7:24}}) @@ -617,7 +620,7 @@ cpdef boruvka(G, wfunction=None, bint check=False, bint by_weight=True): connected, and has at least one vertex. Otherwise, you should set ``check=True`` to perform some sanity checks and preprocessing on the input graph. - + - ``by_weight`` -- boolean (default: ``False``); whether to find MST by using weights of edges provided. Default: ``by_weight=True``. If ``wfunction`` is given, MST is calculated using the weights of edges as @@ -666,7 +669,7 @@ cpdef boruvka(G, wfunction=None, bint check=False, bint by_weight=True): [] TESTS: - + If the input graph is a tree, then return its edges:: sage: T = graphs.RandomTree(randint(1, 10)) @@ -791,6 +794,7 @@ cpdef boruvka(G, wfunction=None, bint check=False, bint by_weight=True): return T + @cython.binding(True) def random_spanning_tree(self, output_as_graph=False): r""" diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 7a3d0761e1e..29fdf2995e9 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -232,12 +232,12 @@ def is_orthogonal_array_block_graph(int v,int k,int l,int mu): m, n = latin_squares_graph_parameters(v,k,l,mu) except Exception: return - if orthogonal_array(m,n,existence=True): + if orthogonal_array(m,n,existence=True) is True: from sage.graphs.generators.intersection import OrthogonalArrayBlockGraph return (lambda m,n : OrthogonalArrayBlockGraph(m, n), m,n) - elif n>2 and skew_hadamard_matrix(n+1, existence=True): - if m==(n+1)//2: + elif n>2 and skew_hadamard_matrix(n+1, existence=True) is True: + if m==(n+1)/2: from sage.graphs.generators.families import SquaredSkewHadamardMatrixGraph as G elif m==(n-1)//2: from sage.graphs.generators.families import PasechnikGraph as G @@ -318,10 +318,11 @@ def is_steiner(int v,int k,int l,int mu): return m = int(sqrt(mu)) n = (k*(m-1))//m+m - if (v == (n*(n-1))//(m*(m-1)) and - k == m*(n-m)//(m-1) and - l == (m-1)**2 + (n-1)//(m-1)-2 and - balanced_incomplete_block_design(n,m,existence=True)): + + if (v == (n*(n-1))/(m*(m-1)) and + k == m*(n-m)/(m-1) and + l == (m-1)**2 + (n-1)/(m-1)-2 and + balanced_incomplete_block_design(n,m,existence=True) is True): from sage.graphs.generators.intersection import IntersectionGraph return (lambda n, m: IntersectionGraph([frozenset(b) for b in balanced_incomplete_block_design(n, m)]), n, m) @@ -526,8 +527,8 @@ def is_goethals_seidel(int v,int k,int l,int mu): if (v == v_bibd*(r_bibd+1) and 2*k == v+r_bibd-1 and 4*l == -2*v + 6*k -v_bibd -k_bibd and - hadamard_matrix(r_bibd+1, existence=True) and - balanced_incomplete_block_design(v_bibd, k_bibd, existence = True)): + hadamard_matrix(r_bibd+1, existence=True) is True and + balanced_incomplete_block_design(v_bibd, k_bibd, existence = True) is True): from sage.graphs.generators.families import GoethalsSeidelGraph return [GoethalsSeidelGraph, k_bibd, r_bibd] @@ -1142,7 +1143,7 @@ def is_RSHCD(int v,int k,int l,int mu): (64, 27, 10, 12) """ - if SRG_from_RSHCD(v,k,l,mu,existence=True): + if SRG_from_RSHCD(v,k,l,mu,existence=True) is True: return [SRG_from_RSHCD,v,k,l,mu] def SRG_from_RSHCD(v,k,l,mu, existence=False,check=True): @@ -1202,10 +1203,10 @@ def SRG_from_RSHCD(v,k,l,mu, existence=False,check=True): t = abs(a//2) if (e**2 == 1 and - k == (n-1-a+e)//2 and - l == (n-2*a)//4 - (1-e) and - mu== (n-2*a)//4 and - regular_symmetric_hadamard_matrix_with_constant_diagonal(n,sgn(a)*e,existence=True)): + k == (n-1-a+e)/2 and + l == (n-2*a)/4 - (1-e) and + mu== (n-2*a)/4 and + regular_symmetric_hadamard_matrix_with_constant_diagonal(n,sgn(a)*e,existence=True) is True): if existence: return True from sage.matrix.constructor import identity_matrix as I @@ -1458,9 +1459,7 @@ def is_twograph_descendant_of_srg(int v, int k0, int l, int mu): sage: from sage.graphs.strongly_regular_db import is_twograph_descendant_of_srg sage: t = is_twograph_descendant_of_srg(27, 10, 1, 5); t (.la at... - sage: g = t[0](*t[1:]); g # py2 - descendant of complement(Johnson graph with parameters 8,2) at {5, 7}: Graph on 27 vertices - sage: g = t[0](*t[1:]); g # py3 + sage: g = t[0](*t[1:]); g descendant of complement(Johnson graph with parameters 8,2) at {0, 1}: Graph on 27 vertices sage: g.is_strongly_regular(parameters=True) (27, 10, 1, 5) @@ -1482,13 +1481,19 @@ def is_twograph_descendant_of_srg(int v, int k0, int l, int mu): for kf in [(-D+b)//4, (D+b)//4]: k = int(kf) if k == kf and \ - strongly_regular_graph(v+1, k, l - 2*mu + k , k - mu, existence=True): - def la(vv): - from sage.combinat.designs.twographs import twograph_descendant - g = strongly_regular_graph(vv, k, l - 2*mu + k) - return twograph_descendant(g, next(g.vertex_iterator()), - name=True) - return la, v + 1 + strongly_regular_graph(v+1, k, l - 2*mu + k , k - mu, existence=True) is True: + try: + g = strongly_regular_graph_lazy(v+1, k, l - 2*mu + k) # Sage might not know how to build g + def la(*gr): + from sage.combinat.designs.twographs import twograph_descendant + gg = g[0](*gr) + if (gg.name() is None) or (gg.name() == ''): + gg = Graph(gg, name=str((v+1, k, l - 2*mu + k , k - mu))+"-strongly regular graph") + return twograph_descendant(gg, next(gg.vertex_iterator()), + name=True) + return (la, *g[1:]) + except RuntimeError: + pass return @@ -1582,7 +1587,7 @@ def is_switch_skewhad(int v, int k, int l, int mu): if int(r) == 2*n-1 and \ v == (4*n-1)**2 + 1 and \ k == (4*n-1)*(2*n-1) and \ - skew_hadamard_matrix(4*n, existence=True): + skew_hadamard_matrix(4*n, existence=True) is True: return (SwitchedSquaredSkewHadamardMatrixGraph, n) def is_switch_OA_srg(int v, int k, int l, int mu): @@ -1636,7 +1641,7 @@ def is_switch_OA_srg(int v, int k, int l, int mu): if (k % n or l != c*c-1 or k != 1+(c-1)*(c+1)+(n-c)*(n-c-1) or - not orthogonal_array(c+1,n,existence=True,resolvable=True)): + not orthogonal_array(c+1,n,existence=True,resolvable=True) is True): return None def switch_OA_srg(c, n): @@ -2796,11 +2801,21 @@ def strongly_regular_graph(int v,int k,int l,int mu=-1,bint existence=False,bint TESTS: - Check that all of our constructions are correct:: + Check that :trac:`26513` is fixed:: + + sage: graphs.strongly_regular_graph(539, 288, 162, 144) + descendant of (540, 264, 138, 120)-strongly regular graph at ... 539 vertices + sage: graphs.strongly_regular_graph(539, 250, 105, 125) + descendant of (540, 275, 130, 150)-strongly regular graph at ... 539 vertices + sage: graphs.strongly_regular_graph(209, 100, 45, 50) + descendant of complement(merging of S_7 on Circulant(6,[1,4])s) at ... 209 vertices + + + Check that all of our constructions are correct - you will need gap_packages spkg installed:: sage: from sage.graphs.strongly_regular_db import apparently_feasible_parameters sage: for p in sorted(apparently_feasible_parameters(1300)): # not tested - ....: if graphs.strongly_regular_graph(*p,existence=True): # not tested + ....: if graphs.strongly_regular_graph(*p,existence=True) is True: # not tested ....: try: # not tested ....: _ = graphs.strongly_regular_graph(*p) # not tested ....: print(p, "built successfully") # not tested @@ -2819,6 +2834,51 @@ def strongly_regular_graph(int v,int k,int l,int mu=-1,bint existence=False,bint sage: graphs.strongly_regular_graph(6,3,0) Multipartite Graph with set sizes [3, 3]: Graph on 6 vertices """ + if mu == -1: + mu = k*(k-l-1)//(v-k-1) + g = strongly_regular_graph_lazy(v, k, l, mu=mu, existence=existence) + if existence is True: + return g + G = g[0](*g[1:]) + if check and (v,k,l,mu) != G.is_strongly_regular(parameters=True): + params = (v,k,l,mu) + raise RuntimeError(f"Sage built an incorrect {params}-SRG.") + return G + +def strongly_regular_graph_lazy(int v,int k,int l,int mu=-1,bint existence=False): + r""" + return a promise to build an `(v,k,l,mu)`-srg + + Return a promise to build an `(v,k,l,mu)`-srg as a tuple `t`, with `t[0]` a + function to evaluate on `*t[1:]`. + + Input as in :func:`~sage.graphs.strongly_regular_graphs_db.strongly_regular_graph`, + although without `check`. + + TESTS:: + + sage: from sage.graphs.strongly_regular_db import strongly_regular_graph_lazy + sage: g,p=strongly_regular_graph_lazy(10,6,3); g,p + (. at ...>, 5) + sage: g(p) + Johnson graph with parameters 5,2: Graph on 10 vertices + sage: g,p=strongly_regular_graph_lazy(10,3,0,1); g,p + (. at...>, + (5,)) + sage: g(p) + complement(Johnson graph with parameters 5,2): Graph on 10 vertices + sage: g,p=strongly_regular_graph_lazy(12,3,2); g,p + (. at...>, + (3, 4)) + sage: g(p) + complement(Multipartite Graph with set sizes [4, 4, 4]): Graph on 12 vertices + sage: g=strongly_regular_graph_lazy(539,250,105); g + (.la at...>, + 5, + 11) + sage: g[0](*g[1:]) + descendant of (540, 275, 130, 150)-strongly regular graph at 0: Graph on 539 vertices + """ load_brouwer_database() if mu == -1: mu = k*(k-l-1)//(v-k-1) @@ -2831,20 +2891,15 @@ def strongly_regular_graph(int v,int k,int l,int mu=-1,bint existence=False,bint return False raise ValueError(f"There exists no {params}-strongly regular graph") - def check_srg(G): - if check and (v,k,l,mu) != G.is_strongly_regular(parameters=True): - raise RuntimeError(f"Sage built an incorrect {params}-SRG.") - return G - if _small_srg_database is None: _build_small_srg_database() if params in _small_srg_database: val = _small_srg_database[params] - return True if existence else check_srg(val[0](*val[1:])) + return True if existence else (val[0], *val[1:]) if params_complement in _small_srg_database: val = _small_srg_database[params_complement] - return True if existence else check_srg(val[0](*val[1:]).complement()) + return True if existence else (lambda *t: val[0](*t).complement(), *val[1:]) test_functions = [is_complete_multipartite, # must be 1st, to prevent 0-divisions is_paley, is_johnson, @@ -2873,12 +2928,12 @@ def strongly_regular_graph(int v,int k,int l,int mu=-1,bint existence=False,bint if existence: return True ans = f(*params) - return check_srg(ans[0](*ans[1:])) + return (ans[0],*ans[1:]) if f(*params_complement): if existence: return True ans = f(*params_complement) - return check_srg(ans[0](*ans[1:]).complement()) + return (lambda t: ans[0](*t).complement(), ans[1:]) # From now on, we have no idea how to build the graph. # @@ -2951,14 +3006,14 @@ def apparently_feasible_parameters(int n): (16, 9, 4, 6), (16, 10, 6, 6), (17, 8, 3, 4)} - sage: all(graphs.strongly_regular_graph(*x,existence=True) for x in small_feasible) + sage: all(graphs.strongly_regular_graph(*x,existence=True) is True for x in small_feasible) True But that becomes wrong for `v<60` (because of the non-existence of a `(49,16,3,6)`-strongly regular graph):: sage: small_feasible = apparently_feasible_parameters(60) - sage: all(graphs.strongly_regular_graph(*x,existence=True) for x in small_feasible) + sage: all(graphs.strongly_regular_graph(*x,existence=True) is True for x in small_feasible) False """ @@ -3191,7 +3246,7 @@ def _check_database(): for params,dic in sorted(saved_database.items()): sage_answer = strongly_regular_graph(*params,existence=True) if dic['status'] == 'open': - if sage_answer: + if sage_answer is True: print("Sage can build a {}, Brouwer's database cannot".format(params)) assert sage_answer is not False elif dic['status'] == 'exists': diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index 5116f71f75b..42d8147b722 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -112,9 +112,7 @@ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None): For a Chordal Graph, a reversed Lex BFS is a Perfect Elimination Order:: sage: g = graphs.PathGraph(3).lexicographic_product(graphs.CompleteGraph(2)) - sage: g.lex_BFS(reverse=True) # py2 - [(2, 0), (2, 1), (1, 1), (1, 0), (0, 0), (0, 1)] - sage: g.lex_BFS(reverse=True) # py3 + sage: g.lex_BFS(reverse=True) [(2, 1), (2, 0), (1, 1), (1, 0), (0, 1), (0, 0)] And the vertices at the end of the tree of discovery are, for chordal @@ -1290,7 +1288,7 @@ def is_valid_lex_M_order(G, alpha, F): clique. The ordering `\alpha` is a perfect elimination ordering of `H`, so `H` is chordal. See [RTL76]_ for more details. - INPUTS: + INPUT: - ``G`` -- a Graph diff --git a/src/sage/graphs/tutte_polynomial.py b/src/sage/graphs/tutte_polynomial.py index 994ed721492..377d6b253b8 100644 --- a/src/sage/graphs/tutte_polynomial.py +++ b/src/sage/graphs/tutte_polynomial.py @@ -32,8 +32,6 @@ --------- """ -from six import itervalues - from contextlib import contextmanager from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc_c import prod @@ -626,7 +624,7 @@ def recursive_tp(graph=None): uG = underlying_graph(G) em = edge_multiplicities(G) - d = list(itervalues(em)) + d = list(em.values()) def yy(start, end): return sum(y**i for i in range(start, end+1)) @@ -671,7 +669,7 @@ def yy(start, end): for d_i in d[:-2]) return result - # Theorem 3 from Haggard, Pearce, and Royle, adapted to multi-eaars + # Theorem 3 from Haggard, Pearce, and Royle, adapted to multi-ears ear = Ear.find_ear(uG) if ear is not None: if (ear.is_cycle and ear.vertices == G.vertices()): diff --git a/src/sage/graphs/views.pyx b/src/sage/graphs/views.pyx index 22e82619b1e..0642dfe5643 100644 --- a/src/sage/graphs/views.pyx +++ b/src/sage/graphs/views.pyx @@ -496,6 +496,13 @@ cdef class EdgesView: False sage: G == E False + + Check that :trac:`29180` is fixed:: + + sage: G = graphs.CycleGraph(4) + sage: E = graphs.EmptyGraph() + sage: G.edges() == E.edges() + False """ if not isinstance(right, EdgesView): return NotImplemented @@ -507,6 +514,9 @@ cdef class EdgesView: self._ignore_direction != other._ignore_direction or self._labels != other._labels): return False + # Check that self and other have the same number of edges + if len(self) != len(other): + return False # Check that the same edges are reported in the same order return all(es == eo for es, eo in zip(self, other)) @@ -601,6 +611,7 @@ cdef class EdgesView: elif i < 0: return list(self)[i] else: + i = int(i) # For Python < 3.7 where islice doesn't support non-int try: return next(islice(self, i, i + 1, 1)) except StopIteration: @@ -684,7 +695,7 @@ cdef class EdgesView: sage: E * 1.5 Traceback (most recent call last): ... - TypeError: can't multiply sequence by non-int of type 'sage.rings.real_mpfr.RealLiteral' + TypeError: can...t multiply sequence by non-int of type 'sage.rings.real_mpfr.RealLiteral' """ if isinstance(left, EdgesView): return list(left) * right diff --git a/src/sage/groups/abelian_gps/abelian_aut.py b/src/sage/groups/abelian_gps/abelian_aut.py index f45738c6dbe..a4ad9f41524 100644 --- a/src/sage/groups/abelian_gps/abelian_aut.py +++ b/src/sage/groups/abelian_gps/abelian_aut.py @@ -83,7 +83,7 @@ from sage.libs.gap.libgap import libgap from sage.matrix.matrix_space import MatrixSpace from sage.rings.all import ZZ -from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.unique_representation import CachedRepresentation class AbelianGroupAutomorphism(ElementLibGAP): @@ -207,7 +207,7 @@ def matrix(self): m.set_immutable() return m -class AbelianGroupAutomorphismGroup_gap(UniqueRepresentation, +class AbelianGroupAutomorphismGroup_gap(CachedRepresentation, GroupMixinLibGAP, Group, ParentLibGAP): diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index e8671002697..7fd67cdc138 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -203,8 +203,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from six.moves import range -import six from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ @@ -540,7 +538,7 @@ def __init__(self, generator_orders, names): sage: A.category() Category of infinite commutative groups """ - assert isinstance(names, (six.string_types, tuple)) + assert isinstance(names, (str, tuple)) assert isinstance(generator_orders, tuple) assert all(isinstance(order,Integer) for order in generator_orders) self._gens_orders = generator_orders @@ -1147,7 +1145,7 @@ def permutation_group(self): TypeError: Abelian group must be finite """ # GAP does not support infinite permutation groups - if not self.is_finite(): + if not self.is_finite(): raise TypeError('Abelian group must be finite') from sage.groups.perm_gps.permgroup import PermutationGroup s = 'Image(IsomorphismPermGroup(%s))'%self._gap_init_() @@ -1362,7 +1360,7 @@ def subgroups(self, check=False): """ if not self.is_finite(): raise ValueError("group must be finite") - from sage.misc.misc import verbose + from sage.misc.verbose import verbose if self.is_trivial(): return [self] diff --git a/src/sage/groups/additive_abelian/additive_abelian_group.py b/src/sage/groups/additive_abelian/additive_abelian_group.py index 498ec739a32..3dcedeb7e36 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_group.py +++ b/src/sage/groups/additive_abelian/additive_abelian_group.py @@ -136,7 +136,6 @@ def cover_and_relations_from_invariants(invs): [0 2 0] [0 0 3]) """ - from six.moves import range n = len(invs) A = ZZ**n B = A.span([A.gen(i) * invs[i] for i in range(n)]) @@ -166,7 +165,6 @@ def _hermite_lift(self): sage: v._hermite_lift() (1, 0) """ - from six.moves import range y = self.lift() H = self.parent().W().basis_matrix() pivot_rows = H.pivot_rows() @@ -466,7 +464,7 @@ def permutation_group(self): TypeError: Additive Abelian group must be finite """ # GAP does not support infinite permutation groups - if not self.is_finite(): + if not self.is_finite(): raise TypeError('Additive Abelian group must be finite') from sage.groups.perm_gps.permgroup import PermutationGroup s = 'Image(IsomorphismPermGroup(AbelianGroup(%s)))'%(list(self.invariants()),) diff --git a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py index 1112deb130e..b9a847c3b20 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py +++ b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py @@ -29,6 +29,7 @@ We check that ridiculous operations are being avoided:: + sage: from sage.misc.verbose import set_verbose sage: set_verbose(2, 'additive_abelian_wrapper.py') sage: 300001 * M.0 verbose 1 (...: additive_abelian_wrapper.py, _discrete_exp) Calling discrete exp on (1, 0, 0) @@ -51,7 +52,6 @@ from . import additive_abelian_group as addgp from sage.rings.all import ZZ -from sage.misc.misc import verbose from sage.categories.morphism import Morphism from sage.structure.element import parent @@ -223,7 +223,7 @@ def _discrete_exp(self, v): sage: v.parent() is QQbar True """ - from six.moves import range + from sage.misc.verbose import verbose v = self.V()(v) verbose("Calling discrete exp on %s" % v) # DUMB IMPLEMENTATION! diff --git a/src/sage/groups/artin.py b/src/sage/groups/artin.py index f7d1ff086af..68465b38213 100644 --- a/src/sage/groups/artin.py +++ b/src/sage/groups/artin.py @@ -22,7 +22,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import division, absolute_import, print_function -import six from sage.misc.cachefunc import cached_method from sage.groups.free_group import FreeGroup @@ -403,7 +402,7 @@ def __classcall_private__(cls, coxeter_data, names=None): coxeter_data = CoxeterMatrix(coxeter_data) if names is None: names = 's' - if isinstance(names, six.string_types): + if isinstance(names, str): if ',' in names: names = [x.strip() for x in names.split(',')] else: diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index c7a87c68cd4..5e020504800 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -65,7 +65,6 @@ # https://www.gnu.org/licenses/ ############################################################################## -import six from sage.rings.integer import Integer from sage.rings.integer_ring import IntegerRing from sage.misc.lazy_attribute import lazy_attribute @@ -2246,7 +2245,7 @@ def BraidGroup(n=None, names='s'): n = None # derive n from counting names if n is None: - if isinstance(names, six.string_types): + if isinstance(names, str): n = len(names.split(',')) else: names = list(names) diff --git a/src/sage/groups/cubic_braid.py b/src/sage/groups/cubic_braid.py index f2d25b030c3..12137eb841b 100644 --- a/src/sage/groups/cubic_braid.py +++ b/src/sage/groups/cubic_braid.py @@ -75,7 +75,7 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # **************************************************************************** @@ -476,14 +476,12 @@ def find_root(domain): if not(root_list): domain = min_pol.splitting_field(min_pol_root_bur.variable_name()) min_pol = min_pol_root_bur.change_ring(domain) - else: - domain = domain root_list = min_pol.roots() for root in root_list: - if root[0] == 0: + if root[0] == 0: continue - root_bur = root[0] - if root[1] == 1: + root_bur = root[0] + if root[1] == 1: break return root_bur @@ -519,20 +517,11 @@ def find_root(domain): domain = root_bur.parent() else: # domain is not None - if characteristic is None: - characteristic = domain.characteristic() - elif characteristic != domain.characteristic(): - raise ValueError('characteristic of domain does not match given characteristic') root_bur = find_root(domain) - - else: # root_bur is not!= None + else: # root_bur is not None if domain is None: domain = root_bur.parent() - if characteristic is None: - characteristic = domain.characteristic() - elif characteristic != domain.characteristic(): - raise ValueError('characteristic of domain does not match given characteristic') if 1 not in domain: raise ValueError('root_bur must belong to a domain containing 1') @@ -725,8 +714,7 @@ def __classcall_private__(cls, n=None, names='c', cbg_type=None): n = None # derive n from counting names if n is None: - import six - if isinstance(names, six.string_types): + if isinstance(names, str): n = len(names.split(',')) else: names = list(names) @@ -923,7 +911,10 @@ def _test_matrix_group(self, **options): sage: CBG2._test_matrix_group() """ tester = self._tester(**options) - F3 = GF(3); r63 = F3(2); F4 = GF(4); r64 = F4.gen() + F3 = GF(3) + r63 = F3(2) + F4 = GF(4) + r64 = F4.gen() MatDEF = self.as_matrix_group() self._internal_test_attached_group(MatDEF, tester) @@ -1272,7 +1263,8 @@ def transvec2mat(v, bas=bas, bform=bform, fact=a): # now 1 + 2*cos(\pi/6)*i\theta = 1 + sqrt(3)*(-sqrt(3)/2 + I/2) = 1- 3/2 + sqrt(3)I/2 = z12^4 = - ~z12^2 # finally: Coxeter's Realization is the unitary Burau representation of Squier for s = ~z12 # ----------------------------------------------------------------------------------------------- - UCF = UniversalCyclotomicField(); z12 = UCF.gen(12) + UCF = UniversalCyclotomicField() + z12 = UCF.gen(12) classical_group = self.as_matrix_group(root_bur=~z12, domain=UCF, reduced='unitary') self._classical_group = classical_group self._classical_base_group = classical_group @@ -1474,7 +1466,7 @@ def as_matrix_group(self, root_bur=None, domain=None, characteristic=None, var=' # ------------------------------------------------------------------------------- unitary = False - if type(reduced) == str: + if isinstance(reduced, str): if reduced == 'unitary': unitary = True @@ -1483,10 +1475,10 @@ def as_matrix_group(self, root_bur=None, domain=None, characteristic=None, var=' bur_mat = braid_gen.burau_matrix(root_bur=root_bur, domain=domain, characteristic=characteristic, var=var, reduced=reduced) if unitary: - bur_mat, bur_mat_ad, herm_form = bur_mat + bur_mat, bur_mat_ad, herm_form = bur_mat if domain is None: - domain = bur_mat.base_ring() + domain = bur_mat.base_ring() gen_list.append(bur_mat) @@ -1668,11 +1660,11 @@ def as_classical_group(self, embedded=False): # of one strand more (self.strands() +1) generated by the first self.strands() -1 # generators # ---------------------------------------------------------------------------------------- - return self._classical_embedding + return self._classical_embedding elif self._classical_group is not None: - return self._classical_group + return self._classical_group else: - raise ValueError("no classical embedding defined") + raise ValueError("no classical embedding defined") # ---------------------------------------------------------------------------------- diff --git a/src/sage/groups/fqf_orthogonal.py b/src/sage/groups/fqf_orthogonal.py new file mode 100644 index 00000000000..4294048425f --- /dev/null +++ b/src/sage/groups/fqf_orthogonal.py @@ -0,0 +1,576 @@ +r""" +Orthogonal Groups of Torsion Quadratic Forms + +The orthogonal group of a torsion quadratic module `T` +consists of all linear self-maps of `T` which preserve +the torsion quadratic form. + + +EXAMPLES:: + + sage: L = IntegralLattice("A2").twist(2) + sage: T = L.discriminant_group() + sage: Oq = T.orthogonal_group() + +The isometries act on elements of their domain:: + + sage: g = Oq(matrix(ZZ, 2, [0, 3, 1, 2])) + sage: T.gen(0) * g + (0, 3) + +Isometries are represented with respect to +the Smith form generators of `T`:: + + sage: L = IntegralLattice("A2").twist(2).direct_sum(IntegralLattice('U')) + sage: T = L.discriminant_group().normal_form() + sage: OT = T.orthogonal_group() + sage: g = matrix(2, 2, [1, 3, 1, 2]) + sage: g = OT(g) + sage: g + [1 3] + [1 2] + sage: sf = T.smith_form_gens() + sage: matrix([s * g for s in T.smith_form_gens()]) + [1 3] + [1 2] + +AUTHORS: + +- Simon Brandhorst (2020-01-08): initial version +""" + +# **************************************************************************** +# Copyright (C) 2020 Simon Brandhorst +# +# 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.libs.gap.libgap import libgap +from sage.groups.abelian_gps.abelian_aut import AbelianGroupAutomorphismGroup_subgroup, AbelianGroupAutomorphism, AbelianGroupAutomorphismGroup_gap +from sage.modules.torsion_quadratic_module import TorsionQuadraticModule +from sage.rings.all import ZZ +from sage.matrix.all import matrix +from sage.categories.action import Action + + +class FqfIsometry(AbelianGroupAutomorphism): + r""" + Isometry of a finite quadratic/bilinear form. + + INPUT: + + - ``parent`` -- the parent :class:`~FqfOrthogonalGroup` + - ``x`` -- a libgap element + - ``check`` -- bool (default: ``True``) + + EXAMPLES:: + + sage: q = matrix.diagonal([2/3]) + sage: q = TorsionQuadraticForm(q) + sage: G = q.orthogonal_group() + sage: g = G(matrix(ZZ, 1, [2])) + sage: g + [2] + """ + + def _repr_(self): + r""" + Return the string represenation of ``self``. + + EXAMPLES:: + + sage: q = matrix.diagonal([2/3, 4/3]) + sage: q = TorsionQuadraticForm(q) + sage: G = q.orthogonal_group() + sage: g = G.one() + sage: g + [1 0] + [0 1] + """ + return str(self.matrix()) + + def __call__(self, x): + r""" + Return the image of ``x`` under ``self``. + + EXAMPLES:: + + sage: q = matrix.diagonal([2/3, 4/3]) + sage: q = TorsionQuadraticForm(q) + sage: G = q.orthogonal_group() + sage: g = G(matrix(ZZ, 2, [1, 0, 0, 2])) + sage: g + [1 0] + [0 2] + sage: x = q.0 + sage: g(x) + (1, 0) + """ + if x in self.parent().invariant_form(): + return x * self + else: + return AbelianGroupAutomorphism.__call__(self, x) + +class FqfOrthogonalGroup(AbelianGroupAutomorphismGroup_subgroup): + r""" + Return a group of isometries of this torsion quadratic form. + + Do not call this class directly instead use + :meth:`sage.modules.torsion_quadratic_module.orthogonal_group`. + + INPUT: + + - ``T`` -- a non degenerate torsion quadratic module. + + EXAMPLES:: + + sage: q = matrix.diagonal(QQ, [2/3, 2/3, 4/3]) + sage: T = TorsionQuadraticForm(q) + sage: T + Finite quadratic module over Integer Ring with invariants (3, 3, 3) + Gram matrix of the quadratic form with values in Q/2Z: + [4/3 0 0] + [ 0 2/3 0] + [ 0 0 2/3] + sage: T.orthogonal_group() + Group of isometries of + Finite quadratic module over Integer Ring with invariants (3, 3, 3) + Gram matrix of the quadratic form with values in Q/2Z: + [4/3 0 0] + [ 0 2/3 0] + [ 0 0 2/3] + generated by 2 elements + sage: q = matrix.diagonal(QQ, [3/2, 1/4, 1/4]) + sage: T = TorsionQuadraticForm(q) + sage: T.orthogonal_group().order() + 8 + + Action on an invariant subquotient:: + + sage: T = TorsionQuadraticForm(matrix.diagonal([2/9] + [2/27])) + sage: S1 = 3 * T + sage: S2 = 9 * T + sage: Q = S1/S2 + sage: G = T.orthogonal_group() + sage: g = G(matrix(ZZ, 2, [8, 0, 0, 1])) + sage: Q.1 * g + (0, 2) + """ + Element = FqfIsometry + + def __init__(self, ambient, gens, fqf, check=False): + r""" + TESTS:: + + sage: q = matrix.diagonal(QQ, [2/3, 2/3, 4/3]) + sage: T = TorsionQuadraticForm(q) + sage: Oq = T.orthogonal_group() + sage: TestSuite(Oq).run() + """ + # We act on the smith form generators + # because they are independent + if not isinstance(fqf, TorsionQuadraticModule): + raise TypeError("input must be a torsion quadratic module") + if not isinstance(ambient, AbelianGroupAutomorphismGroup_gap): + raise TypeError("input must be a torsion quadratic module") + if not fqf.invariants() == ambient.domain().gens_orders(): + raise ValueError("invariants of the abelian groups do not match") + gens = [ambient(g) for g in gens] + self._invariant_form = fqf + AbelianGroupAutomorphismGroup_subgroup.__init__(self, ambient, gens) + if check and any(not self._preserves_form(g) for g in self.gens()): + raise ValueError("a generator does not preserve the quadratic form") + + def invariant_form(self): + r""" + Return the torsion quadratic form left invariant. + + EXAMPLES:: + + sage: q = matrix.diagonal(QQ, [2/3]) + sage: T = TorsionQuadraticForm(q) + sage: Oq = T.orthogonal_group() + sage: Oq.invariant_form() is T + True + """ + return self._invariant_form + + def _element_constructor_(self, x, check=False): + r""" + Construct an element from ``x`` and handle conversions. + + INPUT: + + - ``x`` -- something that converts in can be: + + * a libgap element + * an integer matrix in the covering matrix ring + * a class:`sage.modules.fg_pid.fgp_morphism.FGP_Morphism` + defining an automorphism -- the domain of ``x`` must have + invariants equal to ``self.domain().gens_orders()`` + * something that acts on the invariant form module + + EXAMPLES:: + + sage: L = IntegralLattice("A2").twist(2).direct_sum(IntegralLattice("A2")) + sage: q = L.discriminant_group() + sage: OL = L.orthogonal_group() + sage: Oq = q.orthogonal_group() + sage: f = matrix(ZZ, 2, [0, 1, -1, -1]) + sage: f = matrix.block_diagonal([f, f]) + sage: f = OL(f) + sage: fbar = Oq(f) + sage: fbar + [4 3] + [3 1] + sage: fbar == Oq(f.matrix()) + True + + TESTS:: + + sage: all(x*f==x*fbar for x in q.gens()) + True + sage: L = IntegralLattice("A2").twist(3) + sage: q = L.discriminant_group() + sage: OL = L.orthogonal_group() + sage: Oq = q.orthogonal_group() + sage: assert Oq(OL.0) == Oq(OL.0.matrix()) + sage: assert Oq(Oq.0.matrix()) == Oq.0 + """ + from sage.libs.gap.element import GapElement + if not isinstance(x, GapElement): + try: + # see if x is a matrix preserving + # the inner product of W + q = self.invariant_form() + W = q.W() + if x.ncols() == W.degree(): + # equip x with an action + x = W.orthogonal_group([x]).gen(0) + except (AttributeError, TypeError): + pass + try: + # if there is an action try that + gen = self.invariant_form().smith_form_gens() + x = matrix(ZZ, [(g*x).vector() for g in gen]) + except TypeError: + pass + f = AbelianGroupAutomorphismGroup_subgroup._element_constructor_(self, x, check=True) + if check: + # double check that the form is preserved + # this is expensive + if not self._preserves_form(f): + raise ValueError("not an isometry") + return f + + def _preserves_form(self, f): + r""" + Return if ``f`` preserves the form. + + INPUT: + + Something that acts on the domain. + + EXAMPLES:: + + sage: T = TorsionQuadraticForm(matrix([1/4])) + sage: G = T.orthogonal_group() + sage: g = G.gen(0) + sage: G._preserves_form(g) + True + """ + g = self.invariant_form().smith_form_gens() + gf = tuple(h*f for h in g) + n = len(g) + for i in range(n): + if gf[i].q() != g[i].q(): + return False + for j in range(i+1, n): + if g[i].b(g[j]) != gf[i].b(gf[j]): + return False + return True + + def _get_action_(self, S, op, self_on_left): + r""" + Provide the coercion system with an action. + + EXAMPLES:: + + sage: q = matrix.diagonal([2/3, 4/3]) + sage: q = TorsionQuadraticForm(q) + sage: G = q.orthogonal_group() + sage: G._get_action_(q, operator.mul, False) + Right action by Group of isometries of + Finite quadratic module over Integer Ring with invariants (3, 3) + Gram matrix of the quadratic form with values in Q/2Z: + [4/3 0] + [ 0 2/3] + generated by 2 elements on Finite quadratic module over Integer Ring with invariants (3, 3) + Gram matrix of the quadratic form with values in Q/2Z: + [4/3 0] + [ 0 2/3] + """ + import operator + if op == operator.mul and not self_on_left: + T = self.invariant_form() + if S == T: + return ActionOnFqf(self, S) + try: + if S.is_submodule(T): + # check if the submodule is invariant + if all(T(s)*g in S for s in S.gens() for g in self.gens()): + return ActionOnFqf(self, S, on_subquotient=True) + elif S.V().is_submodule(T.V()) and T.W().is_submodule(S.W()): # is a subquotient + Q1 = S.V()/T.W() + Q2 = S.W()/T.W() + if ( + all(T(q) * g in Q1 for q in Q1.gens() for g in self.gens()) and + all(T(q) * g in Q2 for q in Q2.gens() for g in self.gens()) + ): + return ActionOnFqf(self, S, on_subquotient=True) + except AttributeError: + pass + try: + return AbelianGroupAutomorphismGroup_subgroup._get_action_(self, S, op, self_on_left) + except AttributeError: + pass + + def _subgroup_constructor(self, libgap_subgroup): + r""" + Create a subgroup from the input. + + See :class:`~sage.groups.libgap_wrapper`. Override this in derived + classes. + + EXAMPLES:: + + sage: q = TorsionQuadraticForm(matrix.diagonal([2/3, 2/3])) + sage: G = q.orthogonal_group() + sage: G.subgroup(G.gens()[:1]) # indirect doctest + Group of isometries of + Finite quadratic module over Integer Ring with invariants (3, 3) + Gram matrix of the quadratic form with values in Q/2Z: + [2/3 0] + [ 0 2/3] + generated by 1 elements + """ + generators = libgap_subgroup.GeneratorsOfGroup() + generators = tuple(self(g, check=False) for g in generators) + return FqfOrthogonalGroup(self, generators, self.invariant_form(), check=False) + + def _repr_(self): + r""" + The string representation of ``self``. + + EXAMPLES:: + + sage: q = TorsionQuadraticForm(matrix.diagonal([2/3,2/3])) + sage: q.orthogonal_group() + Group of isometries of + Finite quadratic module over Integer Ring with invariants (3, 3) + Gram matrix of the quadratic form with values in Q/2Z: + [2/3 0] + [ 0 2/3] + generated by 2 elements + """ + return "Group of isometries of \n%s\ngenerated by %s elements"%(self.invariant_form(), len(self.gens())) + +class ActionOnFqf(Action): + r""" + Action on a finite quadratic module. + + INPUT: + + - ``orthogonal_grp`` -- an instance of :class:`GroupOfIsometries` + - ``fqf`` -- a torsion quadratic module + - ``on_subquotient`` -- bool (default: ``False``) + - ``is_left`` -- bool (default: ``False``) + + EXAMPLES:: + + sage: q = matrix.diagonal([2/3, 4/3]) + sage: q = TorsionQuadraticForm(q) + sage: G = q.orthogonal_group() + sage: g = G(matrix.diagonal([2, 2])) + sage: g + [2 0] + [0 2] + sage: x = q.0 + sage: x * g + (2, 0) + """ + def __init__(self, orthogonal_grp, fqf, on_subquotient=False, is_left=False): + r""" + Initialize the action + + TESTS:: + + sage: from sage.groups.fqf_orthogonal import ActionOnFqf + sage: q = matrix.diagonal([2/3, 4/3]) + sage: q = TorsionQuadraticForm(q) + sage: G = q.orthogonal_group() + sage: A = ActionOnFqf(G, q, is_left=True) + Traceback (most recent call last): + ... + ValueError: the action is from the right + """ + import operator + self._on_subquotient = on_subquotient + if is_left: + raise ValueError("the action is from the right") + Action.__init__(self, orthogonal_grp, fqf, is_left, operator.mul) + + def _act_(self, g, a): + r""" + This defines the group action. + + INPUT: + + - ``a`` -- an element of the invariant submodule + - ``g`` -- an element of the acting group + + OUTPUT: + + - an element of the invariant submodule + + EXAMPLES:: + + sage: from sage.groups.fqf_orthogonal import ActionOnFqf + sage: q = matrix.diagonal([2/3, 4/3]) + sage: q = TorsionQuadraticForm(q) + sage: g1 = 2 * matrix.identity(2) + sage: g2 = matrix(ZZ, 2, [1, 0, 0, 2]) + sage: G = q.orthogonal_group(gens=[g1, g2]) + sage: A = ActionOnFqf(G, q) + sage: A + Right action by Group of isometries of + Finite quadratic module over Integer Ring with invariants (3, 3) + Gram matrix of the quadratic form with values in Q/2Z: + [4/3 0] + [ 0 2/3] + generated by 2 elements on Finite quadratic module over Integer Ring with invariants (3, 3) + Gram matrix of the quadratic form with values in Q/2Z: + [4/3 0] + [ 0 2/3] + sage: x = q.an_element() + sage: g = G.an_element() + sage: A(x, g).parent() + Finite quadratic module over Integer Ring with invariants (3, 3) + Gram matrix of the quadratic form with values in Q/2Z: + [4/3 0] + [ 0 2/3] + sage: q = TorsionQuadraticForm(matrix.diagonal([2/3, 2/3, 6/8, 1/4])) + sage: G = q.orthogonal_group() + sage: q2 = q.primary_part(2) + sage: g = G(matrix.diagonal([1, 7])) + sage: q2.gen(1) * g + (0, 3) + """ + if self.is_left(): + pass + # this would be a left action but... we do not allow it. + # v = (a.vector()*g.matrix().inverse()) + # P = a.parent() + # return P.linear_combination_of_smith_form_gens(v) + elif self._on_subquotient: + S = a.parent() + T = g.parent().invariant_form() + return S(T(a)*g) + else: + v = (a.vector()*g.matrix()) + P = a.parent() + return P.linear_combination_of_smith_form_gens(v) + +def _isom_fqf(A, B=None): + r""" + Return isometries from `A` to `B`. + + INPUT: + + - ``A`` -- a torsion quadratic module + - ``B`` -- (default: ``None``) a torsion quadratic module + + OUTPUT: + + A list of generators of the orthogonal group of A. + If ``B`` is given returns instead a single isometry of `A` and `B` or + raises an ``ValueError`` if `A` and `B` are not isometric. + + EXAMPLES:: + + sage: q = matrix.diagonal(QQ, 3*[2/3]) + sage: q = TorsionQuadraticForm(q) + sage: gens = sage.groups.fqf_orthogonal._isom_fqf(q, q) + sage: q1 = q.submodule_with_gens(gens) + sage: q1.gram_matrix_quadratic() == q.gram_matrix_quadratic() + True + + TESTS:: + + sage: for p in primes_first_n(7)[1:]: # long time + ....: q = matrix.diagonal(QQ, 3 * [2/p]) # long time + ....: q = TorsionQuadraticForm(q) # long time + ....: assert q.orthogonal_group().order()==GO(3, p).order() # long time + """ + def orbits(G, L): + r""" + Return the orbits of `L` under `G`. + + INPUT: + + - ``G`` -- an fqf_orthognal group + - ``L`` -- a list of tuples of elements of the domain of ``G`` + + A list of orbit representatives of `L` + """ + D = G.invariant_form() + A = G.domain() + L = libgap([[A(g).gap() for g in f] for f in L]) + orb = G.gap().Orbits(L,libgap.OnTuples) + orb = [g[0] for g in orb] + orb = [[D.linear_combination_of_smith_form_gens(A(g).exponents()) for g in f] for f in orb] + return orb + + if B is None: + B = A + automorphisms = True + else: + automorphisms = False + if A.invariants() != B.invariants(): + raise ValueError("torsion quadratic modules are not isometric") + n = len(A.smith_form_gens()) + # separating the different primes here would speed things up + b_cand = [[b for b in B if b.q()==a.q() and b.order() == a.order()] for a in A.smith_form_gens()] + + G = B.orthogonal_group(tuple([])) + ambient = G.ambient() + waiting = [[]] + while len(waiting) > 0: + # f is an i-partial isometry + f = waiting.pop() + i = len(f) + if i == n: + # f is a full isometry + if not automorphisms: + return f + g = ambient(matrix(f)) + if not g in G: + G = B.orthogonal_group(tuple(ambient(s.matrix()) for s in G.gens())+(g,)) + waiting = orbits(G, waiting) + continue + # extend f to an i+1 - partial isometry in all possible ways + a = A.smith_form_gens()[i] + card = ZZ.prod(A.smith_form_gen(k).order() for k in range(i+1)) + for b in b_cand[i]: + if all(b.b(f[k])==a.b(A.smith_form_gens()[k]) for k in range(i)): + fnew = f + [b] + # check that the elements of fnew are independent + if B.submodule(fnew).cardinality() == card: + waiting.append(fnew) + if not automorphisms: + raise ValueError("torsion quadratic modules are not isometric") + gens = G.gap().SmallGeneratingSet() + return [G(g).matrix() for g in gens] diff --git a/src/sage/groups/free_group.py b/src/sage/groups/free_group.py index a056ad4c455..198e17baabc 100644 --- a/src/sage/groups/free_group.py +++ b/src/sage/groups/free_group.py @@ -60,7 +60,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -import six from sage.categories.groups import Groups from sage.groups.group import Group from sage.groups.libgap_wrapper import ParentLibGAP, ElementLibGAP @@ -506,7 +505,6 @@ def syllables(self): """ g = self.gap().UnderlyingElement() k = g.NumberSyllables().sage() - gen = self.parent().gen exponent_syllable = libgap.eval('ExponentSyllable') generator_syllable = libgap.eval('GeneratorSyllable') result = [] @@ -669,7 +667,7 @@ def FreeGroup(n=None, names='x', index_set=None, abelian=False, **kwds): n = None # derive n from counting names if n is None: - if isinstance(names, six.string_types): + if isinstance(names, str): n = len(names.split(',')) else: names = list(names) diff --git a/src/sage/groups/generic.py b/src/sage/groups/generic.py index feb3c7b935a..c52e7b0863a 100644 --- a/src/sage/groups/generic.py +++ b/src/sage/groups/generic.py @@ -476,7 +476,6 @@ def bsgs(a, b, bounds, operation='*', identity=None, inverse=None, op=None): c = op(inverse(b),multiple(a,lb,operation=operation)) if ran < 30: # use simple search for small ranges - i = lb d = c # for i,d in multiples(a,ran,c,indexed=True,operation=operation): for i0 in range(ran): @@ -589,7 +588,6 @@ def discrete_log_rho(a, base, ord=None, operation='*', hash_function=hash): - Yann Laigle-Chapuy (2009-09-05) """ - from six.moves import range from sage.rings.integer import Integer from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from operator import mul, add, pow @@ -892,7 +890,6 @@ def discrete_log_lambda(a, base, bounds, operation='*', hash_function=hash): -- Yann Laigle-Chapuy (2009-01-25) """ - from six.moves import range from sage.rings.integer import Integer from operator import mul, add, pow diff --git a/src/sage/groups/indexed_free_group.py b/src/sage/groups/indexed_free_group.py index c15c2e104fc..91982ce4457 100644 --- a/src/sage/groups/indexed_free_group.py +++ b/src/sage/groups/indexed_free_group.py @@ -18,7 +18,6 @@ # # https://www.gnu.org/licenses/ ############################################################################## -from six import integer_types from sage.categories.groups import Groups from sage.categories.poor_man_map import PoorManMap @@ -30,7 +29,7 @@ from sage.rings.integer import Integer from sage.rings.infinity import infinity from sage.sets.family import Family -from six import iteritems + class IndexedGroup(IndexedMonoid): """ @@ -371,7 +370,7 @@ def _element_constructor_(self, x=None): d[k] = v x = d if isinstance(x, dict): - x = {k: v for k, v in iteritems(x) if v != 0} + x = {k: v for k, v in x.items() if v != 0} return IndexedGroup._element_constructor_(self, x) @cached_method @@ -478,11 +477,11 @@ def __pow__(self, n): sage: x^-3 F[0]^-3*F[1]^-6*F[3]^-3*F[4]^3 """ - if not isinstance(n, integer_types + (Integer,)): + if not isinstance(n, (int, Integer)): raise TypeError("Argument n (= {}) must be an integer".format(n)) if n == 1: return self if n == 0: return self.parent().one() - return self.__class__(self.parent(), {k:v*n for k,v in iteritems(self._monomial)}) + return self.__class__(self.parent(), {k:v*n for k,v in self._monomial.items()}) diff --git a/src/sage/groups/libgap_wrapper.pyx b/src/sage/groups/libgap_wrapper.pyx index d4ad92b6be4..d06bebe881b 100644 --- a/src/sage/groups/libgap_wrapper.pyx +++ b/src/sage/groups/libgap_wrapper.pyx @@ -253,6 +253,14 @@ class ParentLibGAP(SageObject): sage: G.subgroup(subgroup_gens) Subgroup with 8 generators of Matrix group over Rational Field with 48 generators + TESTS: + + Check that :trac:`19010` is fixed:: + + sage: G = WeylGroup(['B',3]) + sage: H = G.subgroup([G[14], G[17]]) + sage: all(g*h in G and h*g in G for g in G for h in H) + True """ generators = [ g if isinstance(g, GapElement) else self(g).gap() for g in generators ] diff --git a/src/sage/groups/lie_gps/nilpotent_lie_group.py b/src/sage/groups/lie_gps/nilpotent_lie_group.py index 78001d2e9be..5b6541a96fb 100644 --- a/src/sage/groups/lie_gps/nilpotent_lie_group.py +++ b/src/sage/groups/lie_gps/nilpotent_lie_group.py @@ -24,7 +24,7 @@ from sage.manifolds.structure import(DifferentialStructure, RealDifferentialStructure) from sage.misc.cachefunc import cached_method -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.modules.free_module_element import vector from sage.rings.real_mpfr import RealField_class from sage.structure.element import MultiplicativeGroupElement diff --git a/src/sage/groups/matrix_gps/coxeter_group.py b/src/sage/groups/matrix_gps/coxeter_group.py index ddb102db427..ec8db50992e 100644 --- a/src/sage/groups/matrix_gps/coxeter_group.py +++ b/src/sage/groups/matrix_gps/coxeter_group.py @@ -18,7 +18,6 @@ # # http://www.gnu.org/licenses/ ############################################################################## -from six.moves import range from sage.structure.unique_representation import UniqueRepresentation from sage.categories.coxeter_groups import CoxeterGroups diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py index d356c9cfc14..2467582fb0a 100644 --- a/src/sage/groups/matrix_gps/finitely_generated.py +++ b/src/sage/groups/matrix_gps/finitely_generated.py @@ -48,7 +48,7 @@ - Volker Braun (2013-1) port to new Parent, libGAP. - Sebastian Oehms (2018-07): Added _permutation_group_element_ (Trac #25706) -- Sebastian Oehms (2019-01): Revision of :trac:`25706` (:trac:`26903`and :trac:`27143`). +- Sebastian Oehms (2019-01): Revision of :trac:`25706` (:trac:`26903` and :trac:`27143`). """ ############################################################################## @@ -65,7 +65,6 @@ from sage.rings.all import ZZ from sage.rings.all import QQbar -from sage.interfaces.gap import gap from sage.structure.element import is_Matrix from sage.matrix.matrix_space import MatrixSpace, is_MatrixSpace from sage.matrix.all import matrix @@ -79,8 +78,8 @@ from sage.rings.number_field.number_field import CyclotomicField from sage.combinat.integer_vector import IntegerVectors -from sage.groups.matrix_gps.matrix_group import ( - MatrixGroup_generic, MatrixGroup_gap ) +from sage.groups.matrix_gps.matrix_group import (MatrixGroup_generic, + MatrixGroup_gap) from sage.groups.matrix_gps.group_element import is_MatrixGroupElement @@ -128,7 +127,7 @@ def normalize_square_matrices(matrices): degree = ZZ(len(m)) else: degree, rem = ZZ(len(m)).sqrtrem() - if rem!=0: + if rem != 0: raise ValueError('list of plain numbers must have square integer length') deg.append(degree) gens.append(matrix(degree, degree, m)) @@ -309,6 +308,7 @@ def MatrixGroup(*gens, **kwds): # ################################################################### + class FinitelyGeneratedMatrixGroup_generic(MatrixGroup_generic): """ TESTS:: @@ -479,6 +479,7 @@ def _test_matrix_generators(self, **options): # ################################################################### + class FinitelyGeneratedMatrixGroup_gap(MatrixGroup_gap): """ Matrix group generated by a finite number of matrices. @@ -553,7 +554,7 @@ def as_permutation_group(self, algorithm=None, seed=None): A finite subgroup of GL(12,Z) as a permutation group:: - sage: imf=libgap.function_factory('ImfMatrixGroup') + sage: imf = libgap.function_factory('ImfMatrixGroup') sage: GG = imf( 12, 3 ) sage: G = MatrixGroup(GG.GeneratorsOfGroup()) sage: G.cardinality() @@ -624,7 +625,7 @@ def as_permutation_group(self, algorithm=None, seed=None): sage: PG = MG.as_permutation_group() sage: mg = MG.an_element() sage: PG(mg) - (1,2,6,19,35,33)(3,9,26,14,31,23)(4,13,5)(7,22,17)(8,24,12)(10,16,32,27,20,28)(11,30,18)(15,25,36,34,29,21) + (1,2,6,19,35,33)(3,9,26,14,31,23)(4,13,5)(7,22,17)(8,24,12)(10,16,32,27,20,28)(11,30,18)(15,25,36,34,29,21) """ # Note that the output of IsomorphismPermGroup() depends on # memory locations and will change if you change the order of @@ -635,12 +636,11 @@ def as_permutation_group(self, algorithm=None, seed=None): if seed is not None: from sage.libs.gap.libgap import libgap libgap.set_seed(ZZ(seed)) - iso=self._libgap_().IsomorphismPermGroup() + iso = self._libgap_().IsomorphismPermGroup() if algorithm == "smaller": - iso=iso.Image().SmallerDegreePermutationRepresentation() - return PermutationGroup(iso.Image().GeneratorsOfGroup().sage(), \ - canonicalize=False) - + iso = iso.Image().SmallerDegreePermutationRepresentation() + return PermutationGroup(iso.Image().GeneratorsOfGroup().sage(), + canonicalize=False) def module_composition_factors(self, algorithm=None): r""" @@ -651,8 +651,8 @@ def module_composition_factors(self, algorithm=None): EXAMPLES:: - sage: F=GF(3);MS=MatrixSpace(F,4,4) - sage: M=MS(0) + sage: F = GF(3); MS = MatrixSpace(F,4,4) + sage: M = MS(0) sage: M[0,1]=1;M[1,2]=1;M[2,3]=1;M[3,0]=1 sage: G = MatrixGroup([M]) sage: G.module_composition_factors() @@ -669,34 +669,25 @@ def module_composition_factors(self, algorithm=None): more verbose version. For more on MeatAxe notation, see - http://www.gap-system.org/Manuals/doc/ref/chap69.html + https://www.gap-system.org/Manuals/doc/ref/chap69.html """ - from sage.misc.sage_eval import sage_eval + from sage.libs.gap.libgap import libgap F = self.base_ring() - if not(F.is_finite()): + if not F.is_finite(): raise NotImplementedError("Base ring must be finite.") - q = F.cardinality() - gens = self.gens() n = self.degree() MS = MatrixSpace(F, n, n) - mats = [] # initializing list of mats by which the gens act on self - for g in gens: - p = MS(g.matrix()) - m = p.rows() - mats.append(m) - mats_str = str(gap([[list(r) for r in ma] for ma in mats])) - gap.eval("M:=GModuleByMats("+mats_str+", GF("+str(q)+"))") - gap.eval("MCFs := MTX.CompositionFactors( M )") - N = eval(gap.eval("Length(MCFs)")) + mats = [MS(g.matrix()) for g in self.gens()] + # initializing list of mats by which the gens act on self + mats_gap = libgap(mats) + M = mats_gap.GModuleByMats(F) + compo = libgap.function_factory('MTX.CompositionFactors') + MCFs = compo(M) if algorithm == "verbose": - print(gap.eval('MCFs') + "\n") - L = [] - for i in range(1, N + 1): - gap.eval("MCF := MCFs[%s]" % i) - L.append(tuple([sage_eval(gap.eval("MCF.field")), - eval(gap.eval("MCF.dimension")), - sage_eval(gap.eval("MCF.IsIrreducible")) ])) - return sorted(L) + print(str(MCFs) + "\n") + return sorted((MCF['field'].sage(), + MCF['dimension'].sage(), + MCF['IsIrreducible'].sage()) for MCF in MCFs) def invariant_generators(self): r""" @@ -767,21 +758,22 @@ def invariant_generators(self): from sage.interfaces.singular import singular gens = self.gens() singular.LIB("finvar.lib") - n = self.degree() #len((gens[0].matrix()).rows()) + n = self.degree() # len((gens[0].matrix()).rows()) F = self.base_ring() q = F.characteristic() - ## test if the field is admissible - if F.gen()==1: # we got the rationals or GF(prime) + # test if the field is admissible + if F.gen() == 1: # we got the rationals or GF(prime) FieldStr = str(F.characteristic()) - elif hasattr(F,'polynomial'): # we got an algebraic extension - if len(F.gens())>1: + elif hasattr(F,'polynomial'): # we got an algebraic extension + if len(F.gens()) > 1: raise NotImplementedError("can only deal with finite fields and (simple algebraic extensions of) the rationals") - FieldStr = '(%d,%s)'%(F.characteristic(),str(F.gen())) - else: # we have a transcendental extension - FieldStr = '(%d,%s)'%(F.characteristic(),','.join([str(p) for p in F.gens()])) + FieldStr = '(%d,%s)' % (F.characteristic(), str(F.gen())) + else: # we have a transcendental extension + FieldStr = '(%d,%s)' % (F.characteristic(), + ','.join(str(p) for p in F.gens())) - ## Setting Singular's variable names - ## We need to make sure that field generator and variables get different names. + # Setting Singular's variable names + # We need to make sure that field generator and variables get different names. if str(F.gen())[0] == 'x': VarStr = 'y' else: @@ -805,7 +797,8 @@ def invariant_generators(self): elements if elements is not None: ReyName = 't'+singular._next_var_name() - singular.eval('matrix %s[%d][%d]'%(ReyName,self.cardinality(),n)) + singular.eval('matrix %s[%d][%d]' % (ReyName, + self.cardinality(), n)) for i in range(1,self.cardinality()+1): M = Matrix(F, elements[i-1]) D = [{} for foobar in range(self.degree())] @@ -814,28 +807,26 @@ def invariant_generators(self): for row in range(self.degree()): for t in D[row].items(): singular.eval('%s[%d,%d]=%s[%d,%d]+(%s)*var(%d)' - %(ReyName,i,row+1,ReyName,i,row+1, repr(t[1]),t[0]+1)) + % (ReyName,i,row+1,ReyName,i,row+1, repr(t[1]),t[0]+1)) IRName = 't'+singular._next_var_name() - singular.eval('matrix %s = invariant_algebra_reynolds(%s)'%(IRName,ReyName)) + singular.eval('matrix %s = invariant_algebra_reynolds(%s)' % (IRName,ReyName)) else: ReyName = 't'+singular._next_var_name() - singular.eval('list %s=group_reynolds((%s))'%(ReyName,Lgens)) + singular.eval('list %s=group_reynolds((%s))' % (ReyName, Lgens)) IRName = 't'+singular._next_var_name() - singular.eval('matrix %s = invariant_algebra_reynolds(%s[1])'%(IRName,ReyName)) + singular.eval('matrix %s = invariant_algebra_reynolds(%s[1])' % (IRName, ReyName)) - OUT = [singular.eval(IRName+'[1,%d]'%(j)) + OUT = [singular.eval(IRName+'[1,%d]' % (j)) for j in range(1, 1+int(singular('ncols('+IRName+')')))] return [PR(gen) for gen in OUT] if self.cardinality() % q == 0: PName = 't' + singular._next_var_name() SName = 't' + singular._next_var_name() - singular.eval('matrix %s,%s=invariant_ring(%s)'%(PName,SName,Lgens)) - OUT = [ - singular.eval(PName+'[1,%d]'%(j)) - for j in range(1,1+singular('ncols('+PName+')')) - ] + [ - singular.eval(SName+'[1,%d]'%(j)) for j in range(2,1+singular('ncols('+SName+')')) - ] + singular.eval('matrix %s,%s=invariant_ring(%s)' % (PName, SName, Lgens)) + OUT = [singular.eval(PName+'[1,%d]' % (j)) + for j in range(1,1+singular('ncols('+PName+')'))] + OUT += [singular.eval(SName+'[1,%d]' % (j)) + for j in range(2,1+singular('ncols('+SName+')'))] return [PR(gen) for gen in OUT] def molien_series(self, chi=None, return_series=True, prec=20, variable='t'): @@ -972,7 +963,7 @@ def molien_series(self, chi=None, return_series=True, prec=20, variable='t'): if R.characteristic() == 0: P = PolynomialRing(R, variable) t = P.gen() - #it is possible the character is over a larger cyclotomic field + # it is possible the character is over a larger cyclotomic field K = chi.values()[0].parent() if K.degree() != 1: if R.degree() != 1: @@ -986,32 +977,32 @@ def molien_series(self, chi=None, return_series=True, prec=20, variable='t'): mol += L(chi(g)) / (M.identity_matrix()-t*g.matrix()).det().change_ring(L) elif R.characteristic().divides(N): raise NotImplementedError("characteristic cannot divide group order") - else: #char p>0 - #find primitive Nth roots of unity over base ring and QQ + else: # char p>0 + # find primitive Nth roots of unity over base ring and QQ F = cyclotomic_polynomial(N).change_ring(R) w = F.roots(ring=R.algebraic_closure(), multiplicities=False)[0] - #don't need to extend further in this case since the order of - #the roots of unity in the character divide the order of the group + # don't need to extend further in this case since the order of + # the roots of unity in the character divide the order of the group L = CyclotomicField(N, 'v') v = L.gen() - #construct Molien series + # construct Molien series P = PolynomialRing(L, variable) t = P.gen() mol = P(0) for g in self: - #construct Phi + # construct Phi phi = L(chi(g)) for e in g.matrix().eigenvalues(): - #find power such that w**n = e + # find power such that w**n = e n = 1 while w**n != e and n < N+1: n += 1 - #raise v to that power + # raise v to that power phi *= (1-t*v**n) mol += P(1)/phi - #We know the coefficients will be integers + # We know the coefficients will be integers mol = mol.numerator().change_ring(ZZ) / mol.denominator().change_ring(ZZ) - #divide by group order + # divide by group order mol /= N if return_series: PS = PowerSeriesRing(ZZ, variable, default_prec=prec) @@ -1168,14 +1159,14 @@ def reynolds_operator(self, poly, chi=None): raise TypeError("number of variables in polynomial must match size of matrices") R = FractionField(poly.base_ring()) C = FractionField(self.base_ring()) - if chi is None: #then this is the trivial character + if chi is None: # then this is the trivial character if R.characteristic() == 0: - #non-modular case + # non-modular case if C == QQbar or R == QQbar: L = QQbar elif not C.is_absolute() or not R.is_absolute(): raise NotImplementedError("only implemented for absolute fields") - else: #create the compositum + else: # create the compositum if C.absolute_degree() == 1: L = R elif R.absolute_degree() == 1: @@ -1199,10 +1190,10 @@ def reynolds_operator(self, poly, chi=None): F += poly(*g.matrix()*vector(poly.parent().gens())) F /= self.order() return F - #non-trivial character case + # non-trivial character case K = chi.values()[0].parent() if R.characteristic() == 0: - #extend base_ring to compositum + # extend base_ring to compositum if C == QQbar or K == QQbar or R == QQbar: L = QQbar elif not C.is_absolute() or not K.is_absolute() or not R.is_absolute(): @@ -1217,13 +1208,13 @@ def reynolds_operator(self, poly, chi=None): # all are QQ L = R elif l == 1: - #only one is an extension + # only one is an extension L = fields[0] elif l == 2: - #only two are extensions + # only two are extensions L = fields[0].composite_fields(fields[1])[0] else: - #all three are extensions + # all three are extensions L1 = fields[0].composite_fields(fields[1])[0] L = L1.composite_fields(fields[2])[0] else: diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index 90249e2adcf..2c90da94514 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -48,7 +48,6 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** - from __future__ import absolute_import from sage.categories.groups import Groups @@ -188,7 +187,7 @@ def subgroup(self, generators, check=True): OUTPUT: The subgroup generated by ``generators`` as an instance of FinitelyGeneratedMatrixGroup_gap - EAMPLES:: + EXAMPLES:: sage: UCF = UniversalCyclotomicField() sage: G = GL(3, UCF) @@ -338,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/cubegroup.py b/src/sage/groups/perm_gps/cubegroup.py index 78602206c24..72c2c484c4d 100644 --- a/src/sage/groups/perm_gps/cubegroup.py +++ b/src/sage/groups/perm_gps/cubegroup.py @@ -92,7 +92,6 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** from __future__ import print_function -from six.moves import range from sage.groups.perm_gps.permgroup import PermutationGroup_generic import random diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 8512b011221..334d90ac328 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -136,8 +136,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import -from six.moves import range -from six import integer_types from functools import wraps @@ -158,7 +156,7 @@ from sage.categories.all import FiniteEnumeratedSets from sage.groups.conjugacy_classes import ConjugacyClassGAP from sage.structure.richcmp import (richcmp_method, - richcmp, rich_to_bool, op_EQ) + richcmp, rich_to_bool, op_EQ, op_NE) def load_hap(): @@ -450,7 +448,7 @@ def __init__(self, gens=None, gap_group=None, canonicalize=True, #Here we need to check if all of the points are integers #to make the domain contain all integers up to the max. #This is needed for backward compatibility - if all(isinstance(p, (Integer,) + integer_types) for p in domain): + if all(isinstance(p, (int, Integer)) for p in domain): domain = list(range(min([1] + domain), max([1] + domain)+1)) if domain not in FiniteEnumeratedSets(): @@ -692,6 +690,18 @@ def __richcmp__(self, right, op): True sage: H3 < H1 # since H3 is a subgroup of H1 True + + TESTS: + + Check that :trac:`29624` is fixed:: + + sage: G = SymmetricGroup(2) + sage: H = PermutationGroup([(1,2)]) + sage: not G == H + False + sage: G != H + False + """ if not isinstance(right, PermutationGroup_generic): return NotImplemented @@ -701,7 +711,7 @@ def __richcmp__(self, right, op): gSelf = self._libgap_() gRight = right._libgap_() - if op == op_EQ: + if op in [op_EQ,op_NE]: return gSelf._richcmp_(gRight, op) if gSelf.IsSubgroup(gRight): @@ -775,7 +785,7 @@ def _element_constructor_(self, x, check=True): ....: if elt in G2: ....: assert G1(G2(elt)) == elt """ - if isinstance(x, integer_types + (Integer,)) and x == 1: + if isinstance(x, (int, Integer)) and x == 1: return self.identity() if isinstance(x, PermutationGroupElement): @@ -4671,6 +4681,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/groups/perm_gps/permgroup_element.pxd b/src/sage/groups/perm_gps/permgroup_element.pxd index 0f9a6e0ac9c..a2ac8f20eaa 100644 --- a/src/sage/groups/perm_gps/permgroup_element.pxd +++ b/src/sage/groups/perm_gps/permgroup_element.pxd @@ -1,5 +1,6 @@ from sage.structure.element cimport MultiplicativeGroupElement, MonoidElement, Element from sage.structure.list_clone cimport ClonableIntArray +from sage.rings.polynomial.polydict cimport ETuple from sage.libs.gap.element cimport GapElement cdef class PermutationGroupElement(MultiplicativeGroupElement): @@ -24,3 +25,4 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): cdef public __custom_name cpdef list _act_on_list_on_position(self, list x) cpdef ClonableIntArray _act_on_array_on_position(self, ClonableIntArray x) + cpdef ETuple _act_on_etuple_on_position(self, ETuple x) diff --git a/src/sage/groups/perm_gps/permgroup_element.pyx b/src/sage/groups/perm_gps/permgroup_element.pyx index 90d014cb134..9f843d84f2f 100644 --- a/src/sage/groups/perm_gps/permgroup_element.pyx +++ b/src/sage/groups/perm_gps/permgroup_element.pyx @@ -105,6 +105,8 @@ import random import sage.groups.old as group +from libc.stdlib cimport qsort + from cysignals.memory cimport sig_malloc, sig_calloc, sig_realloc, sig_free from cpython.list cimport * @@ -139,7 +141,8 @@ cdef arith_llong arith = arith_llong() cdef extern from *: long long LLONG_MAX -#import permgroup_named +cdef int etuple_index_cmp(const void * a, const void * b) nogil: + return (( a)[0] > ( b)[0]) - (( a)[0] < ( b)[0]) def make_permgroup_element(G, x): """ @@ -1132,24 +1135,65 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): y.set_immutable() return y + cpdef ETuple _act_on_etuple_on_position(self, ETuple x): + r""" + Return the right action of this permutation on the ETuple ``x``. + + EXAMPLES:: + + sage: from sage.rings.polynomial.polydict import ETuple + sage: S = SymmetricGroup(6) + sage: e = ETuple([1,2,3,4,5,6]) + sage: S("(1,4)")._act_on_etuple_on_position(e) + (4, 2, 3, 1, 5, 6) + sage: S("(1,2,3,4,5,6)")._act_on_etuple_on_position(e) + (6, 1, 2, 3, 4, 5) + + sage: e = ETuple([1,2,0,0,0,6]) + sage: S("(1,4)")._act_on_etuple_on_position(e) + (0, 2, 0, 1, 0, 6) + sage: S("(1,2,3,4,5,6)")._act_on_etuple_on_position(e) + (6, 1, 2, 0, 0, 0) + + It is indeed a right action:: + + sage: p, q = S('(1,2,3,4,5,6)'), S('(1,2)(3,4)(5,6)') + sage: e = ETuple([10..15]) + sage: right = lambda x, p: p._act_on_etuple_on_position(x) + sage: right(e, p * q) == right(right(e, p), q) + True + """ + cdef size_t ind + cdef ETuple result = ETuple.__new__(ETuple) + + result._length = x._length + result._nonzero = x._nonzero + result._data = sig_malloc(sizeof(int)*result._nonzero*2) + for ind in range(x._nonzero): + result._data[2*ind] = self.perm[x._data[2*ind]] # index + result._data[2*ind + 1] = x._data[2*ind+1] # exponent + qsort(result._data, result._nonzero, 2 * sizeof(int), etuple_index_cmp) + return result + cpdef _act_on_(self, x, bint self_on_left): """ - Return the right action of self on left. + Return the result of the action of ``self`` on ``x``. - For example, if f=left is a polynomial, then this function returns - f(sigma\*x), which is image of f under the right action of sigma on + For example, if ``x=f(z)`` is a polynomial, then this function returns + f(sigma\*z), which is the image of f under the right action of sigma on the indeterminates. This is a right action since the image of - f(sigma\*x) under tau is f(sigma\*tau\*x). + f(sigma\*z) under tau is f(sigma\*tau\*z). - Additionally, if ``left`` is a matrix, then sigma acts on the matrix - by permuting the rows. + Additionally, if ``x`` is a matrix, then sigma acts on the matrix + by permuting the columns when acting from the right and by permuting + the rows when acting from the left. INPUT: + - ``x`` -- element of space on which permutations act - - ``left`` - element of space on which permutations - act from the right - + - ``self_on_left`` -- if ``True``, this permutation acts on ``x`` from + the left, otherwise from the right EXAMPLES:: @@ -1167,13 +1211,18 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): 2*x^2 - y^2 + z^2 + u^2 sage: M = matrix(ZZ,[[1,0,0,0,0],[0,2,0,0,0],[0,0,3,0,0],[0,0,0,4,0],[0,0,0,0,5]]) - sage: M*sigma + sage: sigma * M [0 2 0 0 0] [0 0 3 0 0] [1 0 0 0 0] [0 0 0 0 5] [0 0 0 4 0] - + sage: (M * sigma) * tau == M * (sigma * tau) + True + sage: (M * sigma) * tau == (M * sigma.matrix()) * tau.matrix() + True + sage: (tau * sigma) * M == tau * (sigma * M) + True """ if not self_on_left: left = x @@ -1192,7 +1241,11 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): left.parent())) return left(tuple(sigma_x)) elif is_Matrix(left): - return left.with_permuted_rows(self) + return left.with_permuted_columns(~self) + else: + if is_Matrix(x): + return x.with_permuted_rows(self) + def __mul__(left, right): r""" diff --git a/src/sage/groups/perm_gps/permgroup_named.py b/src/sage/groups/perm_gps/permgroup_named.py index aa8a6a1502b..309744c65ec 100644 --- a/src/sage/groups/perm_gps/permgroup_named.py +++ b/src/sage/groups/perm_gps/permgroup_named.py @@ -86,7 +86,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from six.moves import range import os @@ -2089,7 +2088,7 @@ def cardinality(self): try: return Integer(libgap.NrTransitiveGroups(libgap(self._degree))) except RuntimeError: - from sage.misc.misc import verbose + from sage.misc.verbose import verbose verbose("Error: TransitiveGroups should come with GAP.", level=0) except TypeError: raise NotImplementedError("Only the transitive groups of degree at most 31 are available in GAP's database") @@ -2494,7 +2493,7 @@ def cardinality(self): try: return Integer(libgap.NrPrimitiveGroups(self._degree)) except RuntimeError: - from sage.misc.misc import verbose + from sage.misc.verbose import verbose verbose("Error: PrimitiveGroups should be in GAP already.", level=0) diff --git a/src/sage/groups/perm_gps/symgp_conjugacy_class.py b/src/sage/groups/perm_gps/symgp_conjugacy_class.py index 8ff2e4bacec..918e80ab905 100644 --- a/src/sage/groups/perm_gps/symgp_conjugacy_class.py +++ b/src/sage/groups/perm_gps/symgp_conjugacy_class.py @@ -6,7 +6,6 @@ - Vincent Delecroix, Travis Scrimshaw (2014-11-23) """ from __future__ import print_function -from six.moves import range from sage.groups.conjugacy_classes import ConjugacyClass, ConjugacyClassGAP from sage.groups.perm_gps.permgroup_element import PermutationGroupElement @@ -295,7 +294,7 @@ def conjugacy_class_iterator(part, S=None): r""" Return an iterator over the conjugacy class associated to the partition ``part``. - + The elements are given as a list of tuples, each tuple being a cycle. INPUT: diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index 76c093475cf..0ce579b07e6 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Right-Angled Artin Groups @@ -24,7 +25,6 @@ #***************************************************************************** from __future__ import division, absolute_import, print_function -import six from sage.libs.gap.element import GapElement @@ -37,6 +37,13 @@ from sage.combinat.root_system.coxeter_matrix import CoxeterMatrix from sage.combinat.root_system.coxeter_group import CoxeterGroup +from sage.combinat.free_module import CombinatorialFreeModule +from sage.categories.fields import Fields +from sage.categories.algebras_with_basis import AlgebrasWithBasis +from sage.algebras.clifford_algebra import CliffordAlgebraElement +from sage.typeset.ascii_art import ascii_art +from sage.typeset.unicode_art import unicode_art + class RightAngledArtinGroup(ArtinGroup): r""" The right-angled Artin group defined by a graph `G`. @@ -161,7 +168,7 @@ def __classcall_private__(cls, G, names=None): raise ValueError("the graph must not be empty") if names is None: names = 'v' - if isinstance(names, six.string_types): + if isinstance(names, str): if ',' in names: names = [x.strip() for x in names.split(',')] else: @@ -295,8 +302,6 @@ def _element_constructor_(self, x): 1 """ if isinstance(x, RightAngledArtinGroup.Element): - if x.parent() is self: - return x raise ValueError("there is no coercion from {} into {}".format(x.parent(), self)) if x == 1: return self.one() @@ -371,6 +376,23 @@ def _normal_form(self, word): pos += len(comm_set) return tuple(w) + def cohomology(self, F=None): + """ + Return the cohomology ring of ``self`` over the field ``F``. + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: A.cohomology() + Cohomology ring of Right-angled Artin group of Cycle graph + with coefficients in Rational Field + """ + if F is None: + from sage.rings.rational_field import QQ + F = QQ + return CohomologyRAAG(F, self) + class Element(ArtinGroupElement): """ An element of a right-angled Artin group (RAAG). @@ -606,3 +628,281 @@ def _richcmp_(self, other, op): """ return richcmp(self._data, other._data, op) +class CohomologyRAAG(CombinatorialFreeModule): + r""" + The cohomology ring of a right-angled Artin group. + + The cohomology ring of a right-angled Artin group `A`, defined by + the graph `G`, with coefficients in a field `F` is isomorphic to + the exterior algebra of `F^N`, where `N` is the number of vertices + in `G`, modulo the quadratic relations `e_i \wedge e_j = 0` if and + only if `(i, j)` is an edge in `G`. This algebra is sometimes also + known as the Cartier-Foata algebra. + + REFERENCES: + + - [CQ2019]_ + """ + def __init__(self, R, A): + """ + Initialize ``self``. + + TESTS:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: H = A.cohomology() + sage: TestSuite(H).run() + """ + if R not in Fields(): + raise NotImplementedError("only implemented with coefficients in a field") + self._group = A + + names = tuple(['e' + name[1:] for name in A.variable_names()]) + from sage.graphs.independent_sets import IndependentSets + from sage.sets.finite_enumerated_set import FiniteEnumeratedSet + indices = [tuple(ind_set) for ind_set in IndependentSets(A._graph)] + indices = FiniteEnumeratedSet(indices) + cat = AlgebrasWithBasis(R.category()).Super().Graded().FiniteDimensional() + CombinatorialFreeModule.__init__(self, R, indices, category=cat, prefix='H') + self._assign_names(names) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: A.cohomology() + Cohomology ring of Right-angled Artin group of Cycle graph + with coefficients in Rational Field + """ + return "Cohomology ring of {} with coefficients in {}".format(self._group, self.base_ring()) + + def _repr_term(self, m): + """ + Return a string representation of the basis element indexed by + ``m``. + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: H = A.cohomology() + sage: H._repr_term((0,1,3)) + 'e0*e1*e3' + sage: w,x,y,z = H.algebra_generators() + sage: y*w + x*z + -e0*e2 + e1*e3 + """ + if not m: + return '1' + return '*'.join('e' + str(i) for i in m) + + def _ascii_art_term(self, m): + r""" + Return ascii art for the basis element indexed by ``m``. + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: H = A.cohomology() + sage: H._ascii_art_term((0,1,3)) + e0/\e1/\e3 + sage: w,x,y,z = H.algebra_generators() + sage: ascii_art(y*w + 2*x*z) + -e0/\e2 + 2*e1/\e3 + """ + if not m: + return ascii_art('1') + wedge = '/\\' + return ascii_art(*['e' + str(i) for i in m], sep=wedge) + + def _unicode_art_term(self, m): + """ + Return unicode art for the basis element indexed by ``m``. + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: H = A.cohomology() + sage: H._unicode_art_term((0,1,3)) + e0∧e1∧e3 + sage: w,x,y,z = H.algebra_generators() + sage: unicode_art(y*w + x*z) + -e0∧e2 + e1∧e3 + """ + if not m: + return unicode_art('1') + import unicodedata + wedge = unicodedata.lookup('LOGICAL AND') + return unicode_art(*['e' + str(i) for i in m], sep=wedge) + + def _latex_term(self, m): + r""" + Return a `\LaTeX` representation of the basis element indexed + by ``m``. + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: H = A.cohomology() + sage: H._latex_term((0,1,3)) + 'e_{0} \\wedge e_{1} \\wedge e_{3}' + """ + if not m: + return '1' + from sage.misc.latex import latex + return " \\wedge ".join('e_{{{}}}'.format(latex(i)) for i in m) + + def gen(self, i): + """ + Return the ``i``-th standard generator of the algebra ``self``. + + This corresponds to the ``i``-th vertex in the graph + (under a fixed ordering of the vertices). + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: H = A.cohomology() + sage: H.gen(0) + e0 + sage: H.gen(1) + e1 + """ + return self._from_dict({(i,): self.base_ring().one()}, remove_zeros=False) + + @cached_method + def one_basis(self): + """ + Return the basis element indexing `1` of ``self``. + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: H = A.cohomology() + sage: H.one_basis() + () + """ + return () + + @cached_method + def algebra_generators(self): + """ + Return the algebra generators of ``self``. + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: H = A.cohomology() + sage: H.algebra_generators() + Finite family {0: e0, 1: e1, 2: e2, 3: e3} + """ + V = self._group._graph.vertices() + d = {x: self.gen(i) for i,x in enumerate(V)} + from sage.sets.family import Family + return Family(V, lambda x: d[x]) + + def gens(self): + r""" + Return the generators of ``self`` (as an algebra). + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: H = A.cohomology() + sage: H.gens() + (e0, e1, e2, e3) + """ + return tuple(self.algebra_generators()) + + def ngens(self): + """ + Return the number of algebra generators of ``self``. + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: H = A.cohomology() + sage: H.ngens() + 4 + """ + return self._group._graph.num_verts() + + def degree_on_basis(self, I): + """ + Return the degree on the basis element ``clique``. + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: H = A.cohomology() + sage: sorted([H.degree_on_basis(I) for I in H.basis().keys()]) + [0, 1, 1, 1, 1, 2, 2] + """ + return len(I) + + class Element(CliffordAlgebraElement): + """ + An element in the cohomology ring of a right-angled Artin group. + """ + def _mul_(self, other): + """ + Return ``self`` multiplied by ``other``. + + EXAMPLES:: + + sage: C4 = graphs.CycleGraph(4) + sage: A = groups.misc.RightAngledArtin(C4) + sage: H = A.cohomology() + sage: b = sum(H.basis()) + sage: b * b + 2*e0*e2 + 2*e1*e3 + 2*e0 + 2*e1 + 2*e2 + 2*e3 + 1 + """ + zero = self.parent().base_ring().zero() + I = self.parent()._indices + d = {} + + for ml,cl in self: + for mr,cr in other: + # Create the next term + t = list(mr) + for i in reversed(ml): + pos = 0 + for j in t: + if i == j: + pos = None + break + if i < j: + break + pos += 1 + cr = -cr + if pos is None: + t = None + break + t.insert(pos, i) + + if t is None: # The next term is 0, move along + continue + + t = tuple(t) + if t not in I: # not an independent set, so this term is also 0 + continue + d[t] = d.get(t, zero) + cl * cr + if d[t] == zero: + del d[t] + + return self.__class__(self.parent(), d) + diff --git a/src/sage/homology/algebraic_topological_model.py b/src/sage/homology/algebraic_topological_model.py index 97452fabc12..d635daaa82e 100644 --- a/src/sage/homology/algebraic_topological_model.py +++ b/src/sage/homology/algebraic_topological_model.py @@ -23,7 +23,6 @@ # # http://www.gnu.org/licenses/ ######################################################################## -from six import iteritems # TODO: cythonize this. @@ -221,7 +220,7 @@ def algebraic_topological_model(K, base_ring=None): c_bar = c_vec bdry_c = diff * c_vec # Apply phi to bdry_c and subtract from c_bar. - for (idx, coord) in iteritems(bdry_c): + for (idx, coord) in bdry_c.items(): try: c_bar -= coord * phi_dict[dim-1][idx] except KeyError: @@ -232,7 +231,7 @@ def algebraic_topological_model(K, base_ring=None): # Evaluate pi(bdry(c_bar)). pi_bdry_c_bar = zero - for (idx, coeff) in iteritems(bdry_c_bar): + for (idx, coeff) in bdry_c_bar.items(): try: pi_bdry_c_bar += coeff * pi_dict[dim-1][idx] except KeyError: @@ -312,7 +311,7 @@ def algebraic_topological_model(K, base_ring=None): # First pi: if idx in pi_dict[n]: column = vector(base_ring, M_rows) - for (entry, coeff) in iteritems(pi_dict[n][idx]): + for (entry, coeff) in pi_dict[n][idx].items(): # Translate from cells in n_cells to cells in gens[n]. column[gens[n].index(n_cells[entry])] = coeff else: @@ -524,7 +523,7 @@ def conditionally_sparse(m): # Take any u in gens so that lambda_i = != 0. # u_idx will be the index of the corresponding cell. (u_idx, lambda_i) = pi_bdry_c_bar.leading_item() - for (u_idx, lambda_i) in iteritems(pi_bdry_c_bar): + for (u_idx, lambda_i) in pi_bdry_c_bar.items(): if u_idx not in to_be_deleted: break # This element/column needs to be deleted from gens and diff --git a/src/sage/homology/cell_complex.py b/src/sage/homology/cell_complex.py index bd582b87498..7c00e37435b 100644 --- a/src/sage/homology/cell_complex.py +++ b/src/sage/homology/cell_complex.py @@ -44,7 +44,6 @@ # http://www.gnu.org/licenses/ ######################################################################## from __future__ import absolute_import -from six.moves import range from sage.structure.sage_object import SageObject from sage.rings.integer_ring import ZZ diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 51c379fe6f0..a782c58bb8d 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -47,7 +47,6 @@ # # http://www.gnu.org/licenses/ ######################################################################## -from six import iteritems from copy import copy @@ -394,7 +393,7 @@ def _repr_(self): return 'Trivial chain' if n == 1: - deg, vec = next(iteritems(self._vec)) + deg, vec = next(iter(self._vec.items())) return 'Chain({0}:{1})'.format(deg, vec) return 'Chain with {0} nonzero terms over {1}'.format( @@ -510,7 +509,7 @@ def is_cycle(self): True """ chain_complex = self.parent() - for d, v in iteritems(self._vec): + for d, v in self._vec.items(): dv = chain_complex.differential(d) * v if not dv.is_zero(): return False @@ -540,7 +539,7 @@ def is_boundary(self): True """ chain_complex = self.parent() - for d, v in iteritems(self._vec): + for d, v in self._vec.items(): d = chain_complex.differential(d - chain_complex.degree_of_differential()).transpose() if v not in d.image(): return False @@ -584,7 +583,7 @@ def _lmul_(self, scalar): True """ vectors = dict() - for d, v in iteritems(self._vec): + for d, v in self._vec.items(): v = scalar * v if not v.is_zero(): v.set_immutable() @@ -673,16 +672,16 @@ def __init__(self, grading_group, degree_of_differential, base_ring, differentia raise ValueError('grading_group must be either ZZ or multiplicative') # all differentials (excluding the 0x0 ones) must be specified to the constructor if any(dim+degree_of_differential not in differentials and d.nrows() != 0 - for dim, d in iteritems(differentials)): + for dim, d in differentials.items()): raise ValueError('invalid differentials') if any(dim-degree_of_differential not in differentials and d.ncols() != 0 - for dim, d in iteritems(differentials)): + for dim, d in differentials.items()): raise ValueError('invalid differentials') self._grading_group = grading_group self._degree_of_differential = degree_of_differential self._diff = differentials - from sage.categories.all import ChainComplexes + from sage.categories.chain_complexes import ChainComplexes category = ChainComplexes(base_ring) super(ChainComplex_class, self).__init__(base=base_ring, category=category) @@ -708,7 +707,7 @@ def _element_constructor_(self, vectors, check=True): if isinstance(vectors, Chain_class): vectors = vectors._vec data = dict() - for degree, vec in iteritems(vectors): + for degree, vec in vectors.items(): if not is_Vector(vec): vec = vector(self.base_ring(), vec) vec.set_immutable() @@ -823,7 +822,7 @@ def nonzero_degrees(self): sage: D.nonzero_degrees() (0, 1, 2, 3, 6, 7) """ - return tuple(sorted(n for n, d in iteritems(self._diff) + return tuple(sorted(n for n, d in self._diff.items() if d.ncols())) @cached_method @@ -1083,13 +1082,13 @@ def __eq__(self, other): return False R = self.base_ring() equal = True - for d, mat in iteritems(self.differential()): + for d, mat in self.differential().items(): if d not in other.differential(): equal = equal and mat.ncols() == 0 and mat.nrows() == 0 else: equal = (equal and other.differential()[d].change_ring(R) == mat.change_ring(R)) - for d, mat in iteritems(other.differential()): + for d, mat in other.differential().items(): if d not in self.differential(): equal = equal and mat.ncols() == 0 and mat.nrows() == 0 return equal @@ -1467,7 +1466,7 @@ def betti(self, deg=None, base_ring=None): H = self.homology(deg, base_ring=base_ring) if isinstance(H, dict): return {deg: homology_group.dimension() - for deg, homology_group in iteritems(H)} + for deg, homology_group in H.items()} else: return H.dimension() @@ -2000,10 +1999,8 @@ def cartesian_product(self, *factors, **kwds): R = self.base_ring() zero = matrix(R, []) subdivide = kwds.get('subdivide', False) - ret = self - diffs = [D.differential() for D in factors] - keys = reduce(lambda X,d: X.union(d.keys()), diffs, set()) + keys = reduce(lambda X, d: X.union(d.keys()), diffs, set()) ret = {k: matrix.block_diagonal([d.get(k, zero) for d in diffs], subdivide=subdivide) for k in keys} diff --git a/src/sage/homology/chain_complex_morphism.py b/src/sage/homology/chain_complex_morphism.py index 885777d276e..fea32a9f6bf 100644 --- a/src/sage/homology/chain_complex_morphism.py +++ b/src/sage/homology/chain_complex_morphism.py @@ -54,7 +54,7 @@ from sage.matrix.constructor import block_diagonal_matrix, zero_matrix from sage.categories.morphism import Morphism from sage.categories.homset import Hom -from sage.categories.category_types import ChainComplexes +from sage.categories.chain_complexes import ChainComplexes def is_ChainComplexMorphism(x): diff --git a/src/sage/homology/cubical_complex.py b/src/sage/homology/cubical_complex.py index 6c8e0aa1e0e..6871c9a086a 100644 --- a/src/sage/homology/cubical_complex.py +++ b/src/sage/homology/cubical_complex.py @@ -66,7 +66,6 @@ page instead. """ from __future__ import print_function, absolute_import -from six.moves import zip from copy import copy from sage.homology.cell_complex import GenericCellComplex diff --git a/src/sage/homology/delta_complex.py b/src/sage/homology/delta_complex.py index c0ad86af2c1..3889450c9ec 100644 --- a/src/sage/homology/delta_complex.py +++ b/src/sage/homology/delta_complex.py @@ -49,8 +49,6 @@ page instead. """ from __future__ import absolute_import -from six.moves import range -from six import integer_types from copy import copy from sage.homology.cell_complex import GenericCellComplex @@ -297,7 +295,7 @@ def store_bdry(simplex, faces): new_data[dim] = s dim += 1 elif isinstance(data, dict): - if all(isinstance(a, (Integer,) + integer_types) for a in data): + if all(isinstance(a, (int, Integer)) for a in data): # a dictionary indexed by integers new_data = data if -1 not in new_data: diff --git a/src/sage/homology/examples.py b/src/sage/homology/examples.py index 4242b7b05a5..fb466af33dc 100644 --- a/src/sage/homology/examples.py +++ b/src/sage/homology/examples.py @@ -62,7 +62,6 @@ sage: simplicial_complexes.MatchingComplex(6).homology() {0: 0, 1: Z^16, 2: 0} """ -from six import iteritems from sage.homology.simplicial_complex import SimplicialComplex from sage.structure.unique_representation import UniqueRepresentation @@ -1409,7 +1408,7 @@ def RandomTwoSphere(n): graph = RandomTriangulation(n) graph = graph.relabel(inplace=False) - triangles = [(u, v, w) for u, L in iteritems(graph._embedding) + triangles = [(u, v, w) for u, L in graph._embedding.items() for v, w in zip(L, L[1:] + [L[0]]) if u < v and u < w] return SimplicialComplex(triangles, maximality_check=False) diff --git a/src/sage/homology/hochschild_complex.py b/src/sage/homology/hochschild_complex.py index 29048a8ae26..78f38739dc5 100644 --- a/src/sage/homology/hochschild_complex.py +++ b/src/sage/homology/hochschild_complex.py @@ -17,7 +17,7 @@ from sage.structure.parent import Parent from sage.structure.element import ModuleElement, parent from sage.structure.richcmp import richcmp -from sage.categories.category_types import ChainComplexes +from sage.categories.chain_complexes import ChainComplexes from sage.categories.tensor import tensor from sage.combinat.free_module import CombinatorialFreeModule from sage.homology.chain_complex import ChainComplex, Chain_class diff --git a/src/sage/homology/homology_group.py b/src/sage/homology/homology_group.py index 2b6ac8036fc..adfac7717a0 100644 --- a/src/sage/homology/homology_group.py +++ b/src/sage/homology/homology_group.py @@ -16,7 +16,6 @@ # # http://www.gnu.org/licenses/ ######################################################################## -from six.moves import range from sage.modules.free_module import VectorSpace from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup_fixed_gens @@ -142,7 +141,7 @@ def HomologyGroup(n, base_ring, invfac=None): fixed degree. INPUT: - + - ``n`` -- integer; the number of generators - ``base_ring`` -- ring; the base ring over which the homology is computed diff --git a/src/sage/homology/koszul_complex.py b/src/sage/homology/koszul_complex.py index a52b1251ff0..83cbe9dd243 100644 --- a/src/sage/homology/koszul_complex.py +++ b/src/sage/homology/koszul_complex.py @@ -11,7 +11,6 @@ # # http://www.gnu.org/licenses/ ######################################################################## -from six.moves import range from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent diff --git a/src/sage/homology/matrix_utils.py b/src/sage/homology/matrix_utils.py index 222634e64c8..341cefb55eb 100644 --- a/src/sage/homology/matrix_utils.py +++ b/src/sage/homology/matrix_utils.py @@ -16,7 +16,6 @@ # http://www.gnu.org/licenses/ ######################################################################## from __future__ import print_function -from six.moves import range # TODO: this module is a clear candidate for cythonizing. Need to # evaluate speed benefits. diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/homology/simplicial_complex.py index bd29635767a..1c9dd8da8ea 100644 --- a/src/sage/homology/simplicial_complex.py +++ b/src/sage/homology/simplicial_complex.py @@ -149,8 +149,6 @@ True """ from __future__ import print_function, absolute_import -from six.moves import range -from six import integer_types from operator import index as PyNumber_Index # possible future directions for SimplicialComplex: @@ -775,7 +773,7 @@ def __lt__(self, other): try: return sorted(self) < sorted(other) except TypeError: - return sorted(map(str,self)) < sorted(map(str, other)) + return sorted(map(str, self)) < sorted(map(str, other)) def __hash__(self): """ @@ -4225,7 +4223,7 @@ def fixed_complex(self, G): G = self.automorphism_group().subgroup(gens) invariant_f = [list(u) for u in self.face_iterator() - if all(sorted([sigma(j) for j in u]) == sorted(list(u)) + if all(sorted(sigma(j) for j in u) == sorted(u) for sigma in gens)] new_verts = [min(o) for o in G.orbits() if o in invariant_f] return SimplicialComplex([[s for s in f if s in new_verts] @@ -4286,7 +4284,7 @@ def _is_numeric(self): sage: s._is_numeric() False """ - return all(isinstance(v, integer_types + (Integer,)) + return all(isinstance(v, (int, Integer)) for v in self.vertices()) # @cached_method when we switch to immutable SimplicialComplex diff --git a/src/sage/homology/simplicial_set.py b/src/sage/homology/simplicial_set.py index 69654587aa1..7f6403299cb 100644 --- a/src/sage/homology/simplicial_set.py +++ b/src/sage/homology/simplicial_set.py @@ -251,7 +251,6 @@ # http://www.gnu.org/licenses/ # #***************************************************************************** -from six.moves import range import copy @@ -1549,7 +1548,7 @@ def all_n_simplices(self, n): d = sigma.dimension() ans.update([sigma.apply_degeneracies(*_) for _ in all_degeneracies(d, n-d)]) - return sorted(list(ans)) + return sorted(ans) def _map_from_empty_set(self): """ @@ -3533,7 +3532,7 @@ def _facets_(self): for dim in range(self.dimension(), 0, -1): for sigma in self.n_cells(dim): faces.update([tau.nondegenerate() for tau in self.faces(sigma)]) - return sorted(list(set(self.nondegenerate_simplices()).difference(faces))) + return sorted(set(self.nondegenerate_simplices()).difference(faces)) def f_vector(self): """ diff --git a/src/sage/homology/simplicial_set_morphism.py b/src/sage/homology/simplicial_set_morphism.py index a851222294e..bbac4531136 100644 --- a/src/sage/homology/simplicial_set_morphism.py +++ b/src/sage/homology/simplicial_set_morphism.py @@ -29,7 +29,6 @@ # http://www.gnu.org/licenses/ # #***************************************************************************** -from six.moves import range import itertools diff --git a/src/sage/interacts/debugger.py b/src/sage/interacts/debugger.py index dd9bd706918..e8aa9f6788c 100644 --- a/src/sage/interacts/debugger.py +++ b/src/sage/interacts/debugger.py @@ -9,7 +9,6 @@ - William Stein (2012) """ from __future__ import print_function -from six.moves import range from sage.misc.superseded import deprecation deprecation(27531, "sage.interacts.debugger is deprecated because it is meant for the deprecated Sage Notebook") diff --git a/src/sage/interacts/test_jupyter.rst b/src/sage/interacts/test_jupyter.rst index ed2bcb59a5d..3f307d2c97c 100644 --- a/src/sage/interacts/test_jupyter.rst +++ b/src/sage/interacts/test_jupyter.rst @@ -281,9 +281,7 @@ Test all interacts from the Sage interact library:: Interactive function with 2 widgets n: IntSlider(value=1000, description=u'Number of Tosses', max=10000, min=2, step=100) interval: IntRangeSlider(value=(0, 0), description=u'Plotting range (y)', max=1) - doctest:...: UserWarning: Attempting to set identical bottom==top results - in singular transformations; automatically expanding. - bottom=0.0, top=0.0 + doctest:...: UserWarning: Attempting to set identical bottom == top == 0.0 results in singular transformations; automatically expanding. Test matrix control (see :trac:`27735`):: diff --git a/src/sage/interfaces/axiom.py b/src/sage/interfaces/axiom.py index 3e48a26ce29..e9f3419d9a9 100644 --- a/src/sage/interfaces/axiom.py +++ b/src/sage/interfaces/axiom.py @@ -173,21 +173,21 @@ # Distributed under the terms of the GNU General Public License (GPL) # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ########################################################################### -from __future__ import print_function -from __future__ import absolute_import +from __future__ import print_function, absolute_import import os import re from .expect import Expect, ExpectElement, FunctionElement, ExpectFunction -from sage.misc.all import verbose from sage.env import DOT_SAGE from pexpect import EOF from sage.misc.multireplace import multiple_replace from sage.interfaces.tab_completion import ExtraTabCompletion from sage.docs.instancedoc import instancedoc +from sage.structure.richcmp import rich_to_bool + # The Axiom commands ")what thing det" ")show Matrix" and ")display # op det" commands, gives a list of all identifiers that begin in @@ -421,6 +421,7 @@ def _eval_line(self, line, reformat=True, allow_use_file=False, 4 Type: PositiveInteger """ + from sage.misc.verbose import verbose if not wait_for_prompt: return Expect._eval_line(self, line) line = line.rstrip().rstrip(';') @@ -569,7 +570,7 @@ def __call__(self, x): P = self.parent() return P('%s(%s)'%(self.name(), x)) - def _cmp_(self, other): + def _richcmp_(self, other, op): """ EXAMPLES:: @@ -604,18 +605,13 @@ def _cmp_(self, other): """ P = self.parent() if 'true' in P.eval("(%s = %s) :: Boolean"%(self.name(),other.name())): - return 0 + return rich_to_bool(op, 0) elif 'true' in P.eval("(%s < %s) :: Boolean"%(self.name(), other.name())): - return -1 + return rich_to_bool(op, -1) elif 'true' in P.eval("(%s > %s) :: Boolean"%(self.name(),other.name())): - return 1 + return rich_to_bool(op, 1) - # everything is supposed to be comparable in Python, so we define - # the comparison thus when no comparable in interfaced system. - if (hash(self) < hash(other)): - return -1 - else: - return 1 + return NotImplemented def type(self): """ diff --git a/src/sage/interfaces/ecm.py b/src/sage/interfaces/ecm.py index 3420714f8c6..ee5236f2a2d 100644 --- a/src/sage/interfaces/ecm.py +++ b/src/sage/interfaces/ecm.py @@ -23,6 +23,19 @@ Output from ecm is non-deterministic. Doctests should set the random seed, but currently there is no facility to do so. + +TESTS: + +Check that the issues from :trac:`27199` are fixed:: + + sage: n = 16262093986406371 + sage: ecm = ECM() + sage: ecm.factor(n, B1=10) + [1009, 1009, 1733, 3023, 3049] + + sage: n = 1308301 * (10^499 + 153) + sage: ECM(B1=600).one_curve(n, c=1, sigma=10) + [1308301, 100...00153] """ ############################################################################### @@ -37,10 +50,8 @@ ############################################################################### from __future__ import print_function -from six import iteritems, PY2 - -import subprocess import re +import subprocess from sage.structure.sage_object import SageObject from sage.rings.integer_ring import ZZ @@ -174,7 +185,7 @@ def __init__(self, B1=10, B2=None, **kwds): def _make_cmd(self, B1, B2, kwds): ecm = ['ecm'] options = [] - for x, v in iteritems(kwds): + for x, v in kwds.items(): if v is False: continue options.append('-{0}'.format(x)) @@ -208,16 +219,11 @@ def _run_ecm(self, cmd, n): """ from subprocess import Popen, PIPE - if PY2: - enc_kwds = {} - else: - # Under normal usage this program only returns ASCII; anything - # else mixed is garbage and an error - # So just accept latin-1 without encoding errors, and let the - # output parser deal with the rest - enc_kwds = {'encoding': 'latin-1'} - - p = Popen(cmd, stdout=PIPE, stdin=PIPE, stderr=PIPE, **enc_kwds) + # Under normal usage this program only returns ASCII; anything + # else mixed is garbage and an error + # So just accept latin-1 without encoding errors, and let the + # output parser deal with the rest + p = Popen(cmd, stdout=PIPE, stdin=PIPE, stderr=PIPE, encoding='latin-1') out, err = p.communicate(input=str(n)) if err != '': raise ValueError(err) @@ -404,15 +410,15 @@ def _parse_output(self, n, out): if m is not None: factor = m.group('factor') primality = m.group('primality') - assert primality in ['prime', 'composite'] - result += [(ZZ(factor), primality == 'prime')] + assert primality in ['prime', 'composite', 'probable prime'] + result += [(ZZ(factor), primality != 'composite')] continue # cofactor on the next line m = self._found_cofactor_re.match(line) if m is not None: cofactor = m.group('cofactor') primality = m.group('primality') - assert primality in ['Prime', 'Composite'] - result += [(ZZ(cofactor), primality == 'Prime')] + assert primality in ['Prime', 'Composite', 'Probable prime'] + result += [(ZZ(cofactor), primality != 'Composite')] # assert len(result) == 2 return result raise ValueError('failed to parse ECM output') @@ -476,8 +482,9 @@ def one_curve(self, n, factor_digits=None, B1=2000, algorithm="ECM", **kwds): try: factors = self._parse_output(n, out) return [factors[0][0], factors[1][0]] - except ValueError: - # output does not end in factorization + except (ValueError, IndexError): + # output does not end in factorization (ValueError) + # or factors has only one element above (IndexError) return [ZZ(1), n] def _find_factor(self, n, factor_digits, B1, **kwds): @@ -656,7 +663,7 @@ def factor(self, n, factor_digits=None, B1=2000, proof=False, **kwds): # Step 3: Call find_factor until a factorization is found n_factorization = [n] while len(n_factorization) == 1: - n_factorization = self.find_factor(n) + n_factorization = self.find_factor(n,B1=B1) factors.extend(n_factorization) return sorted(probable_prime_factors) diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index 571c6c2807e..cce13898753 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -39,8 +39,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six import string_types -from six import reraise as raise_ import io import os @@ -171,7 +169,7 @@ def __init__(self, name, prompt, command=None, env={}, server=None, self.__init_code = init_code #Handle the log file - if isinstance(logfile, string_types): + if isinstance(logfile, str): self.__logfile = None self.__logfilename = logfile else: @@ -976,11 +974,11 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if except (TypeError, RuntimeError): pass return self._eval_line(line,allow_use_file=allow_use_file, wait_for_prompt=wait_for_prompt, restart_if_needed=False) - raise_(RuntimeError, RuntimeError("%s\nError evaluating %s in %s" % (msg, line, self)), sys.exc_info()[2]) + raise RuntimeError("%s\nError evaluating %s in %s" % (msg, line, self)) if line: try: - if isinstance(wait_for_prompt, string_types): + if isinstance(wait_for_prompt, str): E.expect(str_to_bytes(wait_for_prompt)) else: E.expect(self._prompt) @@ -1369,7 +1367,7 @@ def eval(self, code, strip=True, synchronize=False, locals=None, allow_use_file= except AttributeError: pass - if not isinstance(code, string_types): + if not isinstance(code, str): raise TypeError('input code must be a string.') #Remove extra whitespace @@ -1460,7 +1458,7 @@ def __init__(self, parent, value, is_name=False, name=None): # idea: Joe Wetherell -- try to find out if the output # is too long and if so get it using file, otherwise # don't. - if isinstance(value, string_types) and parent._eval_using_file_cutoff and \ + if isinstance(value, str) and parent._eval_using_file_cutoff and \ parent._eval_using_file_cutoff < len(value): self._get_using_file = True @@ -1473,7 +1471,7 @@ def __init__(self, parent, value, is_name=False, name=None): # coercion to work properly. except (RuntimeError, ValueError) as x: self._session_number = -1 - raise_(TypeError, TypeError(*x.args), sys.exc_info()[2]) + raise TypeError(*x.args) except BaseException: self._session_number = -1 raise diff --git a/src/sage/interfaces/four_ti_2.py b/src/sage/interfaces/four_ti_2.py index a336d54b197..067535f2d40 100644 --- a/src/sage/interfaces/four_ti_2.py +++ b/src/sage/interfaces/four_ti_2.py @@ -34,7 +34,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six import iteritems from sage.rings.integer_ring import ZZ import os @@ -254,7 +253,7 @@ def _process_input(self, kwds): if project is None: project = self.temp_project() - for ext, value in iteritems(kwds): + for ext, value in kwds.items(): if value is None: continue if ext == "project" or ext == "self": diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 336cc50a155..a35d640b64e 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -1011,15 +1011,6 @@ def __bool__(self): __nonzero__ = __bool__ - def __long__(self): - """ - TESTS:: - - sage: long(fricas('1')) # optional - fricas - 1L - """ - return long(self.sage()) - def __float__(self): """ TESTS:: diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 2b1cb8aef46..5f24b102da6 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -176,8 +176,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import, print_function -import six -from six import string_types from .expect import Expect, ExpectElement, FunctionElement, ExpectFunction from .gap_workspace import gap_workspace_file, prepare_workspace_dir @@ -624,10 +622,7 @@ def _execute_line(self, line, wait_for_prompt=True, expect_eof=False): current_outputs.append(b'@') elif x == 2: #special char c = ord(E.after[1:2]) - ord(b'A') + 1 - if six.PY2: - s = chr(c) - else: - s = bytes([c]) + s = bytes([c]) current_outputs.append(s) elif x == 3: # garbage collection info, ignore pass @@ -769,7 +764,7 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if if not len(normal): return '' - if isinstance(wait_for_prompt, string_types) and normal.ends_with(wait_for_prompt): + if isinstance(wait_for_prompt, str) and normal.ends_with(wait_for_prompt): n = len(wait_for_prompt) elif normal.endswith(bytes_to_str(self._prompt)): n = len(self._prompt) diff --git a/src/sage/interfaces/gfan.py b/src/sage/interfaces/gfan.py index 226f2cad576..3b4c8b33f29 100644 --- a/src/sage/interfaces/gfan.py +++ b/src/sage/interfaces/gfan.py @@ -41,8 +41,6 @@ #***************************************************************************** from __future__ import print_function -import six - from subprocess import Popen, PIPE @@ -63,13 +61,8 @@ def __call__(self, I, cmd='', verbose=False, format=True): print("gfan command:\n%s" % cmd) print("gfan input:\n%s" % I) - if six.PY2: - enc_kwargs = {} - else: - enc_kwargs = {'encoding': 'latin-1'} - gfan_processes = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, - **enc_kwargs) + encoding='latin-1') ans, err = gfan_processes.communicate(input=I) # sometimes, gfan outputs stuff to stderr even though everything is fine diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index 1df5d6f72db..54b523b36ef 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -231,6 +231,7 @@ from sage.env import DOT_SAGE from sage.misc.pager import pager from sage.docs.instancedoc import instancedoc +from sage.structure.richcmp import rich_to_bool COMMANDS_CACHE = '%s/giac_commandlist_cache.sobj'%DOT_SAGE @@ -579,7 +580,6 @@ def cputime(self, t=None): else: return float(self('time() - %s'%float(t))) - def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if_needed=False): """ EXAMPLES:: @@ -587,16 +587,23 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if sage: giac._eval_line('2+2') '4' - sage: A=matrix([range(280)]) - sage: GA=giac(A) + sage: A = matrix([range(280)]) + sage: GA = giac(A) + + TESTS:: + + sage: h='int(1/x*((-2*x^(1/3)+1)^(1/4))^3,x)' + sage: giac(h) + 12*(...) """ with gc_disabled(): z = Expect._eval_line(self, line, allow_use_file=allow_use_file, wait_for_prompt=wait_for_prompt) if z.lower().find("error") != -1: raise RuntimeError("An error occurred running a Giac command:\nINPUT:\n%s\nOUTPUT:\n%s"%(line, z)) - return z - + lines = (line for line in z.splitlines() + if not line.startswith('Evaluation time:')) + return "\n".join(lines) def eval(self, code, strip=True, **kwds): r""" @@ -868,7 +875,7 @@ def __hash__(self): """ return hash(giac.eval('string(%s);'%self.name())) - def _cmp_(self, other): + def _richcmp_(self, other, op): """ Compare equality between self and other, using giac. @@ -909,30 +916,26 @@ def _cmp_(self, other): P = self.parent() if P.eval("evalb(%s %s %s)"%(self.name(), P._equality_symbol(), other.name())) == P._true_symbol(): - return 0 + return rich_to_bool(op, 0) # (to be tested with giac). Maple does not allow comparing objects of different types and # it raises an error in this case. # We catch the error, and return True for < try: if P.eval("evalb(%s %s %s)"%(self.name(), P._lessthan_symbol(), other.name())) == P._true_symbol(): - return -1 + return rich_to_bool(op, -1) except RuntimeError as e: msg = str(e) if 'is not valid' in msg and 'to < or <=' in msg: if (hash(str(self)) < hash(str(other))): - return -1 + return rich_to_bool(op, -1) else: - return 1 + return rich_to_bool(op, 1) else: raise RuntimeError(e) if P.eval("evalb(%s %s %s)"%(self.name(), P._greaterthan_symbol(), other.name())) == P._true_symbol(): - return 1 - # everything is supposed to be comparable in Python, so we define - # the comparison thus when no comparable in interfaced system. - if (hash(self) < hash(other)): - return -1 - else: - return 1 + return rich_to_bool(op, 1) + + return NotImplemented def _tab_completion(self): """ diff --git a/src/sage/interfaces/gp.py b/src/sage/interfaces/gp.py index 304a2d418e3..1f2eac46594 100644 --- a/src/sage/interfaces/gp.py +++ b/src/sage/interfaces/gp.py @@ -142,7 +142,7 @@ from __future__ import absolute_import from .expect import Expect, ExpectElement, ExpectFunction, FunctionElement -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.interfaces.tab_completion import ExtraTabCompletion from sage.libs.pari.all import pari import sage.rings.complex_field @@ -934,17 +934,6 @@ def is_string(self): """ return repr(self.type()) == 't_STR' - def __long__(self): - """ - Return Python long. - - EXAMPLES:: - - sage: long(gp(10)) - 10L - """ - return long(str(self)) - def __float__(self): """ Return Python float. diff --git a/src/sage/interfaces/interface.py b/src/sage/interfaces/interface.py index 5ced772cee6..7fa17eaf21c 100644 --- a/src/sage/interfaces/interface.py +++ b/src/sage/interfaces/interface.py @@ -35,16 +35,16 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from six import iteritems, integer_types, string_types import operator from sage.structure.sage_object import SageObject from sage.structure.parent_base import ParentWithBase from sage.structure.element import Element, parent +from sage.structure.richcmp import rich_to_bool import sage.misc.sage_eval from sage.misc.fast_methods import WithEqualityById @@ -292,7 +292,7 @@ def __call__(self, x, name=None): except (NotImplementedError, TypeError): pass - if isinstance(x, string_types): + if isinstance(x, str): return cls(self, x, name=name) try: # Special methods do not and should not have an option to @@ -335,7 +335,7 @@ def _coerce_from_special_method(self, x): def _coerce_impl(self, x, use_special=True): if isinstance(x, bool): return self(self._true_symbol() if x else self._false_symbol()) - elif isinstance(x, integer_types): + elif isinstance(x, int): import sage.rings.all return self(sage.rings.all.Integer(x)) elif isinstance(x, float): @@ -555,7 +555,7 @@ def _convert_args_kwds(self, args=None, kwds=None): for i, arg in enumerate(args): if not isinstance(arg, InterfaceElement) or arg.parent() is not self: args[i] = self(arg) - for key, value in iteritems(kwds): + for key, value in kwds.items(): if not isinstance(value, InterfaceElement) or value.parent() is not self: kwds[key] = self(value) @@ -589,9 +589,9 @@ def function_call(self, function, args=None, kwds=None): EXAMPLES:: sage: maxima.quad_qags(x, x, 0, 1, epsrel=1e-4) - [0.5,0.55511151231257...e-14,21,0] + [0.5,5.5511151231257...e-15,21,0] sage: maxima.function_call('quad_qags', [x, x, 0, 1], {'epsrel':'1e-4'}) - [0.5,0.55511151231257...e-14,21,0] + [0.5,5.5511151231257...e-15,21,0] """ args, kwds = self._convert_args_kwds(args, kwds) self._check_valid_function_name(function) @@ -892,7 +892,7 @@ def __hash__(self): """ return hash('%s' % self) - def _cmp_(self, other): + def _richcmp_(self, other, op): """ Comparison of interface elements. @@ -933,26 +933,21 @@ def _cmp_(self, other): try: if P.eval("%s %s %s"%(self.name(), P._equality_symbol(), other.name())) == P._true_symbol(): - return 0 + return rich_to_bool(op, 0) except RuntimeError: pass try: if P.eval("%s %s %s"%(self.name(), P._lessthan_symbol(), other.name())) == P._true_symbol(): - return -1 + return rich_to_bool(op, -1) except RuntimeError: pass try: if P.eval("%s %s %s"%(self.name(), P._greaterthan_symbol(), other.name())) == P._true_symbol(): - return 1 + return rich_to_bool(op, 1) except Exception: pass - # everything is supposed to be comparable in Python, so we define - # the comparison thus when no comparison is available in interfaced system. - if hash(self) < hash(other): - return -1 - else: - return 1 + return NotImplemented def is_string(self): """ @@ -1135,7 +1130,7 @@ def __repr__(self): except ValueError as msg: return '(invalid {} object -- {})'.format(self.parent() or type(self), msg) cr = getattr(self, '_cached_repr', None) - if isinstance(cr, string_types): + if isinstance(cr, str): s = cr else: s = self._repr_() @@ -1324,16 +1319,6 @@ def __bool__(self): __nonzero__ = __bool__ - def __long__(self): - """ - EXAMPLES:: - - sage: m = maxima('1') - sage: long(m) - 1L - """ - return long(repr(self)) - def __float__(self): """ EXAMPLES:: @@ -1404,7 +1389,7 @@ def name(self, new_name=None): 's5' """ if new_name is not None: - if not isinstance(new_name, string_types): + if not isinstance(new_name, str): raise TypeError("new_name must be a string") p = self.parent() p.set(new_name, self._name) diff --git a/src/sage/interfaces/jmoldata.py b/src/sage/interfaces/jmoldata.py index 8f291fb954b..40984bdc33d 100644 --- a/src/sage/interfaces/jmoldata.py +++ b/src/sage/interfaces/jmoldata.py @@ -67,7 +67,7 @@ def is_jvm_available(self): except (subprocess.CalledProcessError, OSError): return False - java_version_number = int(re.sub(r'.*version "(0\.|1\.)?(\d*)[\s\S]*', r'\2', version)) + java_version_number = int(re.sub(r'.*version "(0\.|1\.)?(\d*)[\s\S]*', r'\2', version, flags=re.S)) return java_version_number >= 7 def export_image(self, diff --git a/src/sage/interfaces/kash.py b/src/sage/interfaces/kash.py index 8b8901d62f8..0ebd83f2ed2 100644 --- a/src/sage/interfaces/kash.py +++ b/src/sage/interfaces/kash.py @@ -4,18 +4,14 @@ Sage provides an interface to the KASH computer algebra system, which is a *free* (as in beer!) but *closed source* program for algebraic number theory that shares much common code with Magma. To -use KASH, you must install the appropriate optional Sage package by -typing something like "sage -i kash3-linux-2005.11.22" or "sage -i -kash3_osx-2005.11.22". For a list of optional packages type "sage --optional". If you type one of the above commands, the (about 16MB) -package will be downloaded automatically (you don't have to do -that). - -It is not enough to just have KASH installed on your computer. Note -that the KASH Sage package is currently only available for Linux -and OSX. If you need Windows support, contact the -`sage-support `_ -mailing list. +use KASH, you must first install it. Visit its web page: +http://page.math.tu-berlin.de/~kant/kash.html + +.. TODO:: + + Update the following sentence. + +It is not enough to just have KASH installed on your computer. The KASH interface offers three pieces of functionality: @@ -48,8 +44,8 @@ Tutorial -------- -The examples in this tutorial require that the optional kash -package be installed. +The examples in this tutorial require that kash +be installed. Basics ~~~~~~ @@ -251,9 +247,8 @@ [ 1, 2, 3, 5, 6, 5 ] The ``Apply`` command applies a function to each -element of a list. +element of a list:: -:: sage: L = kash([1,2,3,4]) # optional -- kash sage: L.Apply('i -> 3*i') # optional -- kash [ 3, 6, 9, 12 ] @@ -521,6 +516,7 @@ def _start(self): try: Expect._start(self) except RuntimeError: + # TODO: replace this error with something more accurate. from sage.misc.package import PackageNotFoundError raise PackageNotFoundError("kash") # Turn off the annoying timer. diff --git a/src/sage/interfaces/kenzo.py b/src/sage/interfaces/kenzo.py index 976983a06f8..43142421140 100644 --- a/src/sage/interfaces/kenzo.py +++ b/src/sage/interfaces/kenzo.py @@ -29,40 +29,91 @@ from sage.structure.sage_object import SageObject from sage.homology.homology_group import HomologyGroup from sage.rings.integer_ring import ZZ +from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup +from sage.groups.abelian_gps.abelian_group import AbelianGroup from sage.categories.commutative_additive_groups import CommutativeAdditiveGroups +from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup -from sage.libs.ecl import EclObject, ecl_eval, EclListIterator - +from sage.matrix.all import matrix +from sage.homology.chain_complex import ChainComplex +from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet -# Redirection of ECL and Maxima stdout to /dev/null -# This is also done in the Maxima library, but we -# also do it here for redundancy. -ecl_eval(r"""(defparameter *dev-null* (make-two-way-stream - (make-concatenated-stream) (make-broadcast-stream)))""") -ecl_eval("(setf original-standard-output *standard-output*)") -ecl_eval("(setf *standard-output* *dev-null*)") - -# Loading and initialization of Kenzo -# Note that it will load kenzo.fas file from $SAGE_LOCAL/lib/ecl/ -ecl_eval("(require :kenzo)") -ecl_eval("(in-package :cat)") -ecl_eval("(setf *HOMOLOGY-VERBOSE* nil)") +from sage.libs.ecl import EclObject, ecl_eval, EclListIterator +from sage.features.kenzo import Kenzo # defining the auxiliary functions as wrappers over the kenzo ones -chcm_mat = EclObject("chcm-mat") -homologie = EclObject("homologie") -sphere = EclObject("sphere") -crts_prdc = EclObject("crts-prdc") -moore = EclObject("moore") -k_z = EclObject("k-z") -k_z2 = EclObject("k-z2") -echcm = EclObject("echcm") -loop_space = EclObject("loop-space") -tnsr_prdc = EclObject("tnsr-prdc") -typep = EclObject("typep") -classifying_space = EclObject("classifying-space") -suspension = EclObject("suspension") +kenzo_names = ['add', + 'array-dimensions', + 'basis_aux1', + 'basis_aux1', + 'bicomplex-spectral-sequence', + 'build-finite-ss2', + 'build-mrph-aux', + 'change-sorc-trgt-aux', + 'chcm-mat', + 'chcm-mat2', + 'classifying-space', + 'cmps', + 'convertmatrice', + 'crts-prdc', + 'degr-aux', + 'dffr-aux', + 'dffr_aux1', + 'dgop', + 'dgop-int-ext', + 'dstr-change-sorc-trgt-aux', + 'echcm', + 'eilenberg-moore-spectral-sequence', + 'evaluation-aux1', + 'gmsm', + 'homologie', + 'homotopy-list', + 'idnt-mrph', + 'join', + 'k-z', + 'k-z2', + 'k-zp', + 'kabstractsimplex_aux1', + 'kchaincomplex_aux1', + 'kmorphismchaincomplex_aux1', + 'loop-space', + 'make-array-from-lists', + 'make-array-to-lists', + 'moore', + 'ncol', + 'nlig', + 'nreverse', + 'nth', + 'opps', + 'orgn_aux1', + 'sbtr', + 'serre-spectral-sequence-product', + 'serre-whitehead-spectral-sequence', + 'sfinitesimplicialset_aux1', + 'smash-product', + 'sorc-aux', + 'spectral-sequence-differential-matrix', + 'spectral-sequence-group', + 'sphere', + 'suspension', + 'tnsr-prdc', + 'trgt-aux', + 'wedge', + 'zero-mrph'] + + +# Now initialize Kenzo. For each string s in kenzo_names, the +# following defines __s__, a wrapper for a Kenzo function. For +# example __sphere__ is defined as EclObject("sphere"). Hyphens +# are replaced with underscores to get valid Python identifiers. +if Kenzo().is_present(): + ecl_eval("(require :kenzo)") + ecl_eval("(in-package :cat)") + ecl_eval("(setf *HOMOLOGY-VERBOSE* nil)") + for s in kenzo_names: + name = '__{}__'.format(s.replace('-', '_')) + exec('{} = EclObject("{}")'.format(name, s)) def Sphere(n): @@ -86,7 +137,7 @@ def Sphere(n): sage: [s2.homology(i) for i in range(8)] # optional - kenzo [Z, 0, Z, 0, 0, 0, 0, 0] """ - kenzosphere = sphere(n) + kenzosphere = __sphere__(n) return KenzoSimplicialSet(kenzosphere) @@ -100,10 +151,9 @@ def MooreSpace(m, n): INPUT: - - ``m`` - A positive integer. The order of the nontrivial homology group. - - - ``n`` - The dimension in which the homology is not trivial + - ``m`` -- A positive integer. The order of the nontrivial homology group. + - ``n`` -- The dimension in which the homology is not trivial OUTPUT: @@ -118,7 +168,7 @@ def MooreSpace(m, n): sage: [m24.homology(i) for i in range(8)] # optional - kenzo [Z, 0, 0, 0, C2, 0, 0, 0] """ - kenzomoore = moore(m, n) + kenzomoore = __moore__(m, n) return KenzoSimplicialSet(kenzomoore) @@ -136,7 +186,6 @@ def EilenbergMacLaneSpace(G, n): - ``n`` -- the dimension in which the homotopy is not trivial - OUTPUT: - A :class:`KenzoSimplicialGroup` @@ -151,14 +200,17 @@ def EilenbergMacLaneSpace(G, n): sage: [f3.homology(i) for i in range(8)] # optional - kenzo [Z, 0, 0, C2, 0, C2, C2, C2] """ - if G is ZZ: - kenzospace = k_z(n) + if G == ZZ: + kenzospace = __k_z__(n) return KenzoSimplicialGroup(kenzospace) - elif G in CommutativeAdditiveGroups() and G.cardinality() == 2: - kenzospace = k_z2(n) + elif G == AdditiveAbelianGroup([2]): + kenzospace = __k_z2__(n) + return KenzoSimplicialGroup(kenzospace) + elif G in CommutativeAdditiveGroups() and G.is_cyclic(): + kenzospace = __k_zp__(G.cardinality(), n) return KenzoSimplicialGroup(kenzospace) else: - raise NotImplementedError("Eilenberg-MacLane spaces are only supported over ZZ and ZZ_2") + raise NotImplementedError("Eilenberg-MacLane spaces are only supported over ZZ and ZZ_n") class KenzoObject(SageObject): @@ -178,8 +230,8 @@ def __init__(self, kenzo_object): TESTS:: sage: from sage.interfaces.kenzo import KenzoObject # optional -kenzo - sage: from sage.interfaces.kenzo import sphere # optional -kenzo - sage: ks = sphere(2) # optional -kenzo + sage: from sage.interfaces.kenzo import __sphere__ # optional -kenzo + sage: ks = __sphere__(2) # optional -kenzo sage: ks # optional -kenzo sage: s2 = KenzoObject(ks) # optional -kenzo @@ -208,13 +260,160 @@ def _repr_(self): return kenzo_string[6:-1] +class KenzoSpectralSequence(KenzoObject): + r""" + Wrapper around Kenzo spectral sequences + """ + + def group(self, p, i, j): + r""" + Return the ``i,j``'th group of the ``p`` page. + + INPUT: + + - ``p`` -- the page to take the group from. + + - ``i`` -- the column where the group is taken from. + + - ``j`` -- the row where the group is taken from. + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: S2 = Sphere(2) # optional - kenzo + sage: EMS = S2.em_spectral_sequence() # optional - kenzo + sage: EMS.group(0, -1, 2) # optional - kenzo + Additive abelian group isomorphic to Z + sage: EMS.group(0, -1, 3) # optional - kenzo + Trivial group + """ + invs = __spectral_sequence_group__(self._kenzo, p, i, j).python() + if not invs: + invs = [] + return AdditiveAbelianGroup(invs) + + def matrix(self, p, i, j): + r""" + Return the matrix that determines the differential from the + ``i,j``'th group of the ``p``'th page. + + INPUT: + + - ``p`` -- the page. + + - ``i`` -- the column of the differential domain. + + - ``j`` -- the row of the differential domain. + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: S3 = Sphere(3) # optional - kenzo + sage: L = S3.loop_space() # optional - kenzo + sage: EMS = L.em_spectral_sequence() # optional - kenzo + sage: EMS.table(1, -5, -2, 5, 8) # optional - kenzo + 0 Z Z + Z + Z Z + Z + Z + 0 0 0 0 + 0 0 Z Z + Z + 0 0 0 0 + sage: EMS.matrix(1, -2 ,8) # optional - kenzo + [ 3 -2 0] + [ 3 0 -3] + [ 0 2 -3] + """ + klist = __spectral_sequence_differential_matrix__(self._kenzo, p, i, j) + plist = klist.python() + if plist is None or plist == [None]: + i = len(self.group(p, i, j).invariants()) + j = len(self.group(p, i - p, j + p - 1).invariants()) + return matrix(i, j) + return matrix(plist) + + def differential(self, p, i, j): + r""" + Return the ``(p, i, j)`` differential morphism of the spectral sequence. + + INPUT: + + - ``p`` -- the page. + + - ``i`` -- the column of the differential domain. + + - ``j`` -- the row of the differential domain. + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: S3 = Sphere(3) # optional - kenzo + sage: L = S3.loop_space() # optional - kenzo + sage: EMS = L.em_spectral_sequence() # optional - kenzo + sage: EMS.table(1,-5,-2,5,8) # optional - kenzo + 0 Z Z + Z + Z Z + Z + Z + 0 0 0 0 + 0 0 Z Z + Z + 0 0 0 0 + sage: EMS.matrix(1, -3, 8) # optional - kenzo + [ 2 -2 2] + sage: EMS.differential(1, -3, 8) # optional - kenzo + Morphism from module over Integer Ring with invariants (0, 0, 0) to module with invariants (0,) that sends the generators to [(2), (-2), (2)] + """ + domain = self.group(p, i, j) + codomain = self.group(p, i - p, j + p - 1) + M = self.matrix(p, i, j) + images = [codomain(r) for r in M.columns()] + return domain.hom(images, codomain=codomain) + + def table(self, p, i1, i2, j1, j2): + r""" + Return a table printing the groups in the ``p`` page. + + INPUT: + + - ``p`` -- the page to print. + + -- ``i1`` -- the first column to print. + + -- ``i2`` -- the last column to print. + + -- ``j1`` -- the first row to print. + + -- ``j2`` -- the last row to print. + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: S2 = Sphere(2) # optional - kenzo + sage: EMS = S2.em_spectral_sequence() # optional - kenzo + sage: EMS.table(0, -2, 2, -2, 2) # optional - kenzo + 0 Z 0 0 0 + 0 0 0 0 0 + 0 0 Z 0 0 + 0 0 0 0 0 + 0 0 0 0 0 + """ + from sage.misc.table import table + groups = [] + for j in range(j2 - j1 + 1): + row = [] + for i in range(i1, i2 + 1): + group = self.group(p, i, j2 - j) + if group.invariants(): + row.append(group.short_name()) + else: + row.append('0') + groups.append(row) + return table(groups) + + class KenzoChainComplex(KenzoObject): r""" - Wrapper to Kenzo chain complexes. + Wrapper to Kenzo chain complexes. Kenzo simplicial sets are a particular case + of Kenzo chain complexes. """ def homology(self, n): r""" - Return the ``n``'th homology group of the kenzo chain complex + Return the ``n``'th homology group of the chain complex associated to this + kenzo object. INPUT: @@ -226,17 +425,17 @@ def homology(self, n): EXAMPLES:: - sage: from sage.interfaces.kenzo import Sphere # optional - kenzo - sage: s2 = Sphere(2) # optional - kenzo - sage: s2 # optional - kenzo - [K1 Simplicial-Set] - sage: s2.homology(2) # optional - kenzo - Z + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: s2 = Sphere(2) # optional - kenzo + sage: s2 # optional - kenzo + [K1 Simplicial-Set] + sage: s2.homology(2) # optional - kenzo + Z """ - echcm1 = echcm(self._kenzo) - m1 = chcm_mat(echcm1, n) - m2 = chcm_mat(echcm1, n+1) - homology = homologie(m1, m2) + echcm1 = __echcm__(self._kenzo) + m1 = __chcm_mat__(echcm1, n) + m2 = __chcm_mat__(echcm1, n + 1) + homology = __homologie__(m1, m2) lhomomology = [i for i in EclListIterator(homology)] res = [] for component in lhomomology: @@ -256,21 +455,202 @@ def tensor_product(self, other): - A :class:`KenzoChainComplex` - sage: from sage.interfaces.kenzo import Sphere # optional - kenzo - sage: s2 = Sphere(2) # optional - kenzo - sage: s3 = Sphere(3) # optional - kenzo - sage: p = s2.tensor_product(s3) # optional - kenzo - sage: type(p) # optional - kenzo - - sage: [p.homology(i) for i in range(8)] # optional - kenzo - [Z, 0, Z, Z, 0, Z, 0, 0] + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: s2 = Sphere(2) # optional - kenzo + sage: s3 = Sphere(3) # optional - kenzo + sage: p = s2.tensor_product(s3) # optional - kenzo + sage: type(p) # optional - kenzo + + sage: [p.homology(i) for i in range(8)] # optional - kenzo + [Z, 0, Z, Z, 0, Z, 0, 0] + """ + return KenzoChainComplex(__tnsr_prdc__(self._kenzo, other._kenzo)) + + def basis(self, dim): + r""" + Return the list of generators of the chain complex associated to the kenzo + object ``self`` in dimension ``dim``. + + INPUT: + + - ``dim`` -- An integer number + + OUTPUT: + + - A list of the form ['G"dim"G0', 'G"dim"G1', 'G"dim"G2', ...]. + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) + sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) + sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo + sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo + sage: kenzo_chcm # optional - kenzo + [K... Chain-Complex] + sage: for i in range(6): # optional - kenzo + ....: print("Basis in dimension %i: %s" % (i, kenzo_chcm.basis(i))) # optional - kenzo + Basis in dimension 0: ['G0G0', 'G0G1', 'G0G2'] + Basis in dimension 1: ['G1G0', 'G1G1'] + Basis in dimension 2: None + Basis in dimension 3: ['G3G0', 'G3G1'] + Basis in dimension 4: ['G4G0', 'G4G1'] + Basis in dimension 5: ['G5G0', 'G5G1', 'G5G2'] + + """ + return __basis_aux1__(self._kenzo, dim).python() + + def identity_morphism(self): + r""" + Return the identity morphism (degree 0) between ``self`` and itself. + + OUTPUT: + + - A :class:`KenzoChainComplexMorphism` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: s2 = Sphere(2) # optional - kenzo + sage: tp = s2.tensor_product(s2) # optional - kenzo + sage: idnt = tp.identity_morphism() # optional - kenzo + sage: type(idnt) # optional - kenzo + + """ + return KenzoChainComplexMorphism(__idnt_mrph__(self._kenzo)) + + def null_morphism(self, target=None, degree=None): + r""" + Return the null morphism between the chain complexes ``self`` and ``target`` + of degree ``degree``. + + INPUT: + + - ``target`` -- A KenzoChainComplex or None (default). + - ``degree`` -- An integer number or None (default). + + OUTPUT: + + - A :class:`KenzoChainComplexMorphism` representing the null morphism between + ``self`` and ``target`` of degree ``degree``. If ``target`` takes None value, + ``self`` is assumed as the target chain complex; if ``degree`` takes None value, + 0 is assumed as the degree of the null morphism. + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: s2 = Sphere(2) # optional - kenzo + sage: s3 = Sphere(3) # optional - kenzo + sage: tp22 = s2.tensor_product(s2) # optional - kenzo + sage: tp22 # optional - kenzo + [K... Chain-Complex] + sage: tp23 = s2.tensor_product(s3) # optional - kenzo + sage: tp23 # optional - kenzo + [K... Chain-Complex] + sage: null1 = tp22.null_morphism() # optional - kenzo + sage: null1 # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: null2 = tp22.null_morphism(target = tp23, degree = -3) # optional - kenzo + sage: null2 # optional - kenzo + [K... Morphism (degree -3): K... -> K...] + """ + if target is None: + target = self + if degree is None: + degree = 0 + if not isinstance(target, KenzoChainComplex): + raise ValueError("'target' parameter must be a KenzoChainComplex instance") + elif (not degree == 0) and (not degree.is_integer()): + raise ValueError("'degree' parameter must be an Integer number") + else: + return KenzoChainComplexMorphism(__zero_mrph__(self._kenzo, target._kenzo, degree)) + + def differential(self, dim=None, comb=None): + r""" + Return the differential of a combination. + + INPUT: + + - ``dim`` -- An integer number or None (default) + + - ``comb`` -- A list representing a formal sum of generators in the module + of dimension ``dim`` or None (default). For example, to represent + G7G12 + 3*G7G0 - 5*G7G3 we use the list [3, 'G7G0', -5, 'G7G3', 1, 'G7G12']. + Note that the generators must be in ascending order respect to the number + after the second G in their representation; the parameter + ``comb`` = [1, 'G7G12', 3, 'G7G0', -5, 'G7G3'] will produce an error in + Kenzo. + + OUTPUT: + + - If ``dim`` and ``comb`` are not None, it returns a Kenzo combination + representing the differential of the formal combination represented by + ``comb`` in the chain complex ``self`` in dimension ``dim``. On the other + hand, if `dim`` or ``comb`` (or both) take None value, the differential + :class:`KenzoMorphismChainComplex` of ``self`` is returned. + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) + sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) + sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) + sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo + sage: kenzo_chcm # optional - kenzo + [K... Chain-Complex] + sage: kenzo_chcm.basis(4) # optional - kenzo + ['G4G0', 'G4G1'] + sage: kenzo_chcm.differential(4, [1, 'G4G0']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 3} + <1 * G3G0> + <3 * G3G1> + ------------------------------------------------------------------------------ + + sage: kenzo_chcm.basis(5) # optional - kenzo + ['G5G0', 'G5G1', 'G5G2'] + sage: kenzo_chcm.differential(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 4} + <6 * G4G0> + <-3 * G4G1> + ------------------------------------------------------------------------------ + + """ + if dim is not None and comb is not None: + cmbn_list = pairing(comb) + return KenzoObject(__dffr_aux1__(self._kenzo, dim, cmbn_list)) + else: + return KenzoChainComplexMorphism(__dffr_aux__(self._kenzo)) + + def orgn(self): + r""" + Return the :orgn slot of Kenzo, which stores as a list the origin of the object + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: s2 = Sphere(2) # optional - kenzo + sage: l2 = s2.loop_space() # optional - kenzo + sage: l2.orgn() # optional - kenzo + '(LOOP-SPACE [K... Simplicial-Set])' + sage: A = l2.cartesian_product(s2) # optional - kenzo + sage: A.orgn() # optional - kenzo + '(CRTS-PRDC [K... Simplicial-Group] [K... Simplicial-Set])' """ - return KenzoChainComplex(tnsr_prdc(self._kenzo, other._kenzo)) + return str(__orgn_aux1__(self._kenzo)) class KenzoSimplicialSet(KenzoChainComplex): r""" Wrapper to Kenzo simplicial sets. + + In Kenzo, the homology of a simplicial set in computed from its associated + chain complex. Hence, this class inherits from `KenzoChainComplex`. """ def loop_space(self, n=1): @@ -297,7 +677,7 @@ def loop_space(self, n=1): sage: [l2.homology(i) for i in range(8)] # optional - kenzo [Z, Z, Z, Z, Z, Z, Z, Z] """ - return KenzoSimplicialGroup(loop_space(self._kenzo, n)) + return KenzoSimplicialGroup(__loop_space__(self._kenzo, n)) def cartesian_product(self, other): r""" @@ -322,7 +702,7 @@ def cartesian_product(self, other): sage: [p.homology(i) for i in range(6)] # optional - kenzo [Z, 0, Z, Z, 0, Z] """ - prod_kenzo = crts_prdc(self._kenzo, other._kenzo) + prod_kenzo = __crts_prdc__(self._kenzo, other._kenzo) return KenzoSimplicialSet(prod_kenzo) def suspension(self): @@ -343,7 +723,207 @@ def suspension(self): sage: [s.homology(i) for i in range(6)] # optional - kenzo [Z, 0, 0, 0, Z, 0] """ - return KenzoSimplicialSet(suspension(self._kenzo)) + return KenzoSimplicialSet(__suspension__(self._kenzo)) + + def homotopy_group(self, n): + """ + Return the n'th homotopy group of ``self`` + + INPUT: + + - ``n`` -- the dimension of the homotopy group to be computed + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: s2 = Sphere(2) # optional - kenzo + sage: p = s2.cartesian_product(s2) # optional - kenzo + sage: p.homotopy_group(3) # optional - kenzo + Multiplicative Abelian group isomorphic to Z x Z + + + .. WARNING:: + + This method assumes that the underlying space is simply connected. + You might get wrong answers if it is not. + """ + if n not in ZZ or n < 2: + raise ValueError("""homotopy groups can only be computed + for dimensions greater than 1""") + lgens = __homotopy_list__(self._kenzo, n).python() + if lgens is not None: + trgens = [0 if i == 1 else i for i in sorted(lgens)] + return AbelianGroup(trgens) + else: + return AbelianGroup([]) + + def em_spectral_sequence(self): + r""" + Return the Eilenberg-Moore spectral sequence of ``self``. + + OUTPUT: + + - A :class:`KenzoSpectralSequence` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: S2 = Sphere(2) # optional - kenzo + sage: EMS = S2.em_spectral_sequence() # optional - kenzo + sage: EMS.table(0, -2, 2, -2, 2) # optional - kenzo + 0 Z 0 0 0 + 0 0 0 0 0 + 0 0 Z 0 0 + 0 0 0 0 0 + 0 0 0 0 0 + + + .. WARNING:: + + This method assumes that the underlying space is simply connected. + You might get wrong answers if it is not. + """ + if self.homology(1).invariants(): + raise ValueError("""Eilenberg-Moore spectral sequence implemented + only for 1-reduced simplicial sets""") + return KenzoSpectralSequence(__eilenberg_moore_spectral_sequence__(self._kenzo)) + + def sw_spectral_sequence(self): + r""" + Return the Serre sequence of the first step of the Whitehead tower. + + OUTPUT: + + - A :class:`KenzoSpectralSequence` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: S3 = Sphere(3) # optional - kenzo + sage: E = S3.sw_spectral_sequence() # optional - kenzo + sage: T = E.table(0, 0, 4, 0, 4) # optional - kenzo + sage: T # optional - kenzo + Z 0 0 Z 0 + 0 0 0 0 0 + Z 0 0 Z 0 + 0 0 0 0 0 + Z 0 0 Z 0 + """ + if self.homology(1).invariants(): + raise ValueError("""Eilenberg-Moore spectral sequence implemented + only for 1-reduced simplicial sets""") + return KenzoSpectralSequence(__serre_whitehead_spectral_sequence__(self._kenzo)) + + def serre_spectral_sequence(self): + r""" + Return the spectral sequence of ``self``. + + The object self must be created as a cartesian product (twisted or not). + + OUTPUT: + + - A :class:`KenzoSpectralSequence` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere + sage: S2 = Sphere(2) # optional - kenzo + sage: S3 = Sphere(3) # optional - kenzo + sage: P = S2.cartesian_product(S3) # optional - kenzo + sage: E = P.serre_spectral_sequence() # optional - kenzo + sage: E.table(0, 0, 2, 0, 3) # optional - kenzo + Z 0 Z + 0 0 0 + 0 0 0 + Z 0 Z + + .. WARNING:: + + This method assumes that the underlying space is simply connected. + You might get wrong answers if it is not. + """ + if self.homology(1).invariants(): + raise ValueError("""Eilenberg-Moore spectral sequence implemented + only for 1-reduced simplicial sets""") + return KenzoSpectralSequence(__serre_spectral_sequence_product__(self._kenzo)) + + def wedge(self, other): + r""" + Return the wedge of ``self`` and ``other``. + + INPUT: + + - ``other`` -- the Kenzo simplicial set with which the wedge is made + + OUTPUT: + + - A :class:`KenzoSimplicialSet` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: s2 = Sphere(2) # optional - kenzo + sage: s3 = Sphere(3) # optional - kenzo + sage: w = s2.wedge(s3) # optional - kenzo + sage: type(w) # optional - kenzo + + sage: [w.homology(i) for i in range(6)] # optional - kenzo + [Z, 0, Z, Z, 0, 0] + """ + wedge_kenzo = __wedge__(self._kenzo, other._kenzo) + return KenzoSimplicialSet(wedge_kenzo) + + def join(self, other): + r""" + Return the join of ``self`` and ``other``. + + INPUT: + + - ``other`` -- the Kenzo simplicial set with which the join is made + + OUTPUT: + + - A :class:`KenzoSimplicialSet` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: s2 = Sphere(2) # optional - kenzo + sage: s3 = Sphere(3) # optional - kenzo + sage: j = s2.join(s3) # optional - kenzo + sage: type(j) # optional - kenzo + + sage: [j.homology(i) for i in range(6)] # optional - kenzo + [Z, 0, 0, 0, 0, 0] + """ + join_kenzo = __join__(self._kenzo, other._kenzo) + return KenzoSimplicialSet(join_kenzo) + + def smash_product(self, other): + r""" + Return the smash product of ``self`` and ``other``. + + INPUT: + + - ``other`` -- the Kenzo simplicial set with which the smash product is made + + OUTPUT: + + - A :class:`KenzoSimplicialSet` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: s2 = Sphere(2) # optional - kenzo + sage: s3 = Sphere(3) # optional - kenzo + sage: s = s2.smash_product(s3) # optional - kenzo + sage: type(s) # optional - kenzo + + sage: [s.homology(i) for i in range(6)] # optional - kenzo + [Z, 0, 0, 0, 0, Z] + """ + smash_kenzo = __smash_product__(self._kenzo, other._kenzo) + return KenzoSimplicialSet(smash_kenzo) class KenzoSimplicialGroup(KenzoSimplicialSet): @@ -370,4 +950,1058 @@ def classifying_space(self): sage: [c.homology(i) for i in range(8)] # optional - kenzo [Z, 0, 0, 0, C2, 0, 0, 0] """ - return KenzoSimplicialGroup(classifying_space(self._kenzo)) + return KenzoSimplicialGroup(__classifying_space__(self._kenzo)) + + +def k2s_matrix(kmatrix): + r""" + Convert an array of ECL to a matrix of Sage. + + INPUT: + + - ``kmatrix`` -- An array in ECL + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import k2s_matrix # optional - kenzo + sage: from sage.libs.ecl import EclObject + sage: M = EclObject("#2A((1 2 3) (3 2 1) (1 1 1))") + sage: k2s_matrix(M) # optional - kenzo + [1 2 3] + [3 2 1] + [1 1 1] + """ + dimensions = __array_dimensions__(kmatrix).python() + kmatrix_list = __make_array_to_lists__(kmatrix).python() + return matrix(dimensions[0], dimensions[1], kmatrix_list) + + +def s2k_matrix(smatrix): + r""" + Convert a matrix of Sage to an array of ECL. + + INPUT: + + - ``smatrix`` -- A matrix in Sage + + OUTPUT: + + - A :class:`EclObject` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import s2k_matrix # optional - kenzo + sage: A = Matrix([[1,2,3],[3,2,1],[1,1,1]]) + sage: s2k_matrix(A) # optional - kenzo + + """ + initcontents = [] + dimensions = smatrix.dimensions() + for i in smatrix.rows(): + initcontents.append(i.list()) + return __make_array_from_lists__(dimensions[0], dimensions[1], initcontents) + + +def s2k_dictmat(sdictmat): + r""" + Convert a dictionary in Sage, whose values are matrices, to an assoc list + in ECL. + + INPUT: + + - ``sdictmat`` -- A dictionary in Sage + + OUTPUT: + + - A :class:`EclObject` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import s2k_dictmat # optional - kenzo + sage: A = Matrix([[1,2,3],[3,2,1],[1,1,1]]) + sage: B = Matrix([[1,2],[2,1],[1,1]]) + sage: d = {1 : A, 2 : B} + sage: s2k_dictmat(d) # optional - kenzo + + + """ + rslt = EclObject([]) + for k in sdictmat.keys(): + rslt = EclObject(k).cons(s2k_matrix(sdictmat[k])).cons(rslt) + return rslt + + +def pairing(slist): + r""" + Convert a list of Sage (which has an even length) to an assoc list in ECL. + + INPUT: + + - ``slist`` -- A list in Sage + + OUTPUT: + + - A :class:`EclObject` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import pairing # optional - kenzo + sage: l = [1,2,3] + sage: pairing(l) # optional - kenzo + + """ + rslt = EclObject([]) + for k in range(len(slist) - 1, 0, -2): + rslt = EclObject(slist[k - 1]).cons(EclObject(slist[k])).cons(rslt) + return rslt + + +def KChainComplex(chain_complex): + r""" + Construct a KenzoChainComplex from a ChainComplex of degree = -1 in + Sage. + + INPUT: + + - ``chain_complex`` -- A ChainComplex of degree = -1 + + OUTPUT: + + - A KenzoChainComplex + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) + sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) + sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo + sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo + sage: kenzo_chcm # optional - kenzo + [K... Chain-Complex] + sage: kenzo_chcm.homology(5) # optional - kenzo + Z x Z + """ + d = chain_complex.differential() + chcm = s2k_dictmat(d) + str_orgn = str(d)[1:-1].replace(":", " ").replace(" ", ".").replace("\n", "").replace(",", "") + return KenzoChainComplex(__kchaincomplex_aux1__(chcm, str_orgn)) + + +def SChainComplex(kchaincomplex, start=0, end=15): + r""" + Convert the KenzoChainComplex ``kchcm`` (between dimensions ``start`` and + ``end``) to a ChainComplex. + + INPUT: + + - ``kchaincomplex`` -- A KenzoChainComplex + + - ``start`` -- An integer number (optional, default 0) + + - ``end`` -- An integer number greater than or equal to ``start`` (optional, default 15) + + OUTPUT: + + - A ChainComplex + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KChainComplex, SChainComplex # optional - kenzo + sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) + sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) + sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo + sage: SChainComplex(KChainComplex(sage_chcm)) == sage_chcm # optional - kenzo + True + + :: + + sage: from sage.interfaces.kenzo import SChainComplex, Sphere # optional - kenzo + sage: S4 = Sphere(4) # optional - kenzo + sage: C = SChainComplex(S4) # optional - kenzo + sage: C # optional - kenzo + Chain complex with at most 3 nonzero terms over Integer Ring + sage: C._ascii_art_() # optional - kenzo + 0 <-- C_4 <-- 0 ... 0 <-- C_0 <-- 0 + sage: [C.homology(i) for i in range(6)] # optional - kenzo + [Z, 0, 0, 0, Z, 0] + """ + matrices = {} + for i in range(start, end): + dffr_i = __chcm_mat2__(kchaincomplex._kenzo, i) + nlig = __nlig__(dffr_i).python() + ncol = __ncol__(dffr_i).python() + if ((nlig != 0) and (ncol != 0)): + matrices[i] = k2s_matrix(__convertmatrice__(dffr_i)) + else: + matrices[i] = matrix(nlig, ncol) + return ChainComplex(matrices, degree=-1) + + +def SAbstractSimplex(simplex, dim): + r""" + Convert an abstract simplex of Kenzo to an AbstractSimplex. + + INPUT: + + - ``simplex`` -- An abstract simplex of Kenzo. + + - ``dim``-- The dimension of ``simplex``. + + OUTPUT: + + - An AbstractSimplex. + + EXAMPLES:: + + sage: from sage.libs.ecl import EclObject, ecl_eval + sage: from sage.interfaces.kenzo import KenzoObject,\ + ....: SAbstractSimplex # optional - kenzo + sage: KAbSm = KenzoObject(ecl_eval("(ABSM 15 'K)")) # optional - kenzo + sage: SAbSm1 = SAbstractSimplex(KAbSm, 2) # optional - kenzo + sage: SAbSm2 = SAbstractSimplex(KAbSm, 7) # optional - kenzo + sage: SAbSm1.degeneracies() # optional - kenzo + [3, 2, 1, 0] + sage: SAbSm1.dimension() # optional - kenzo + 6 + sage: SAbSm2.dimension() # optional - kenzo + 11 + """ + degeneracies = __dgop_int_ext__(__dgop__(simplex._kenzo)).python() + if degeneracies is None: + degeneracies = [] + else: + degeneracies = tuple(degeneracies) + name = __gmsm__(simplex._kenzo).python() + return AbstractSimplex(dim, degeneracies, name=name) + + +def KAbstractSimplex(simplex): + r""" + Convert an AbstractSimplex in Sage to an abstract simplex of Kenzo. + + INPUT: + + - ``simplex`` -- An AbstractSimplex. + + OUTPUT: + + - An abstract simplex of Kenzo. + + EXAMPLES:: + + sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.interfaces.kenzo import KAbstractSimplex,\ + ....: SAbstractSimplex # optional - kenzo + sage: SAbSm = AbstractSimplex(1, (2,0,3,2,1), name = 'SAbSm') # optional - kenzo + sage: KAbSm = KAbstractSimplex(SAbSm) # optional - kenzo + sage: SAbSm2 = SAbstractSimplex(KAbSm, 1) # optional - kenzo + sage: SAbSm.degeneracies() == SAbSm2.degeneracies() # optional - kenzo + True + sage: SAbSm.dimension() == SAbSm2.dimension() # optional - kenzo + True + """ + return KenzoObject(__kabstractsimplex_aux1__(simplex.degeneracies(), + 's' + str(hash(simplex)))) + + +def KFiniteSimplicialSet(sset): + r""" + Convert a finite SimplicialSet in Sage to a finite simplicial set of Kenzo. + + INPUT: + + - ``sset`` -- A finite SimplicialSet. + + OUTPUT: + + - A finite simplicial set of Kenzo. + + EXAMPLES:: + + sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.interfaces.kenzo import KFiniteSimplicialSet # optional - kenzo + sage: s0 = AbstractSimplex(0, name='s0') + sage: s1 = AbstractSimplex(0, name='s1') + sage: s2 = AbstractSimplex(0, name='s2') + sage: s01 = AbstractSimplex(1, name='s01') + sage: s02 = AbstractSimplex(1, name='s02') + sage: s12 = AbstractSimplex(1, name='s12') + sage: s012 = AbstractSimplex(2, name='s012') + sage: Triangle = SimplicialSet({s01: (s1, s0),\ + ....: s02: (s2, s0), s12: (s2, s1)}, base_point = s0) + sage: KTriangle = KFiniteSimplicialSet(Triangle) # optional - kenzo + sage: KTriangle.homology(1) # optional - kenzo + Z + sage: KTriangle.basis(1) # optional - kenzo + ['CELL_1_0', 'CELL_1_1', 'CELL_1_2'] + sage: S1 = simplicial_sets.Sphere(1) + sage: S3 = simplicial_sets.Sphere(3) + sage: KS1vS3 = KFiniteSimplicialSet(S1.wedge(S3)) # optional - kenzo + sage: KS1vS3.homology(3) # optional - kenzo + Z + """ + from sage.homology.simplicial_set_constructions import ProductOfSimplicialSets + if isinstance(sset, ProductOfSimplicialSets): + f0 = KFiniteSimplicialSet(sset.factor(0)) + for f1 in sset.factors()[1:]: + f0 = f0.cartesian_product(KFiniteSimplicialSet(f1)) + return f0 + else: + allcells = sset.cells() + namecells = {c: 'cell_{}_{}'.format(d, allcells[d].index(c)) + for d in allcells for c in allcells[d]} + dim = sset.dimension() + list_rslt = [namecells[i] for i in sset.n_cells(0)] + if (dim > 0): + for k in range(1, dim + 1): + k_cells = sset.n_cells(k) + if k_cells: + list_rslt.append(k) + for x in k_cells: + list_rslt.append(namecells[x]) + auxiliar_list = [] + for z in sset.faces(x): + degen_z = z.degeneracies() + name = namecells[z.nondegenerate()] + degen_z.append(name) + auxiliar_list.append(degen_z) + list_rslt.append(auxiliar_list) + return KenzoSimplicialSet(__build_finite_ss2__(list_rslt)) + + +def SFiniteSimplicialSet(ksimpset, limit): + r""" + Convert the ``limit``-skeleton of a finite simplicial set in Kenzo to a + finite SimplicialSet in Sage. + + INPUT: + + - ``ksimpset`` -- A finite simplicial set in Kenzo. + + - ``limit`` -- A natural number. + + OUTPUT: + + - A finite SimplicialSet. + + EXAMPLES:: + + sage: from sage.homology.simplicial_set import SimplicialSet + sage: from sage.interfaces.kenzo import AbstractSimplex,\ + ....: KFiniteSimplicialSet, SFiniteSimplicialSet, Sphere # optional - kenzo + sage: s0 = AbstractSimplex(0, name='s0') # optional - kenzo + sage: s1 = AbstractSimplex(0, name='s1') # optional - kenzo + sage: s2 = AbstractSimplex(0, name='s2') # optional - kenzo + sage: s01 = AbstractSimplex(1, name='s01') # optional - kenzo + sage: s02 = AbstractSimplex(1, name='s02') # optional - kenzo + sage: s12 = AbstractSimplex(1, name='s12') # optional - kenzo + sage: s012 = AbstractSimplex(2, name='s012') # optional - kenzo + sage: Triangle = SimplicialSet({s01: (s1, s0),\ + ....: s02: (s2, s0), s12: (s2, s1)}, base_point = s0) # optional - kenzo + sage: KTriangle = KFiniteSimplicialSet(Triangle) # optional - kenzo + sage: STriangle = SFiniteSimplicialSet(KTriangle, 1) # optional - kenzo + sage: STriangle.homology() # optional - kenzo + {0: 0, 1: Z} + sage: S1 = simplicial_sets.Sphere(1) # optional - kenzo + sage: S3 = simplicial_sets.Sphere(3) # optional - kenzo + sage: KS1vS3 = KFiniteSimplicialSet(S1.wedge(S3)) # optional - kenzo + sage: SS1vS3 = SFiniteSimplicialSet(KS1vS3, 3) # optional - kenzo + sage: SS1vS3.homology() # optional - kenzo + {0: 0, 1: Z, 2: 0, 3: Z} + """ + list_orgn = __orgn_aux1__(ksimpset._kenzo).python() + if __nth__(0, list_orgn).python()[0] == 'CRTS-PRDC': + return SFiniteSimplicialSet( + KenzoSimplicialSet(__nth__(1, list_orgn)), limit).cartesian_product( + SFiniteSimplicialSet(KenzoSimplicialSet(__nth__(2, list_orgn)), limit)) + rslt = {} + simplices = [] + faces = [] + bases = [] + names = [] + for k in range(limit + 1): + basis_k = __basis_aux1__(ksimpset._kenzo, k) + names_k = ksimpset.basis(k) + lbasis_k = [AbstractSimplex(k, name=i) for i in EclListIterator(basis_k)] + bases.append(lbasis_k) + names.append(names_k) + all_simplices = __sfinitesimplicialset_aux1__(ksimpset._kenzo, limit) + lall_simplices = [i for i in EclListIterator(all_simplices)] + dim = 1 + for Kdim in lall_simplices: + for simp in Kdim: + index1 = names[dim].index(str(simp.car())) + lKdim_cdr = [] + for i in EclListIterator(simp.cdr()): + degenop = __dgop_int_ext__(__dgop__(i)).python() + if degenop is None: + degenop = [] + index2 = names[dim - len(degenop) - 1].index(str(__gmsm__(i))) + lKdim_cdr.append(bases[dim - len(degenop) - 1][index2].apply_degeneracies(*degenop)) + simplices.append(bases[dim][index1]) + faces.append(tuple(lKdim_cdr)) + dim += 1 + for i in range(len(simplices)): + rslt[simplices[i]] = faces[i] + return SimplicialSet(rslt) + + +class KenzoChainComplexMorphism(KenzoObject): + r""" + Wrapper to Kenzo morphisms between chain complexes. + """ + + def source_complex(self): + r""" + Return the source chain complex of the morphism. + + OUTPUT: + + - A :class:`KenzoChainComplex` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) + sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) + sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo + sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo + sage: kenzo_chcm # optional - kenzo + [K... Chain-Complex] + sage: differential_morphism = kenzo_chcm.differential() # optional - kenzo + sage: differential_morphism # optional - kenzo + [K... Morphism (degree -1): K... -> K...] + sage: differential_morphism.source_complex() # optional - kenzo + [K... Chain-Complex] + """ + return KenzoChainComplex(__sorc_aux__(self._kenzo)) + + def target_complex(self): + r""" + Return the target chain complex of the morphism. + + OUTPUT: + + - A :class:`KenzoChainComplex` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) + sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) + sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo + sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo + sage: kenzo_chcm # optional - kenzo + [K... Chain-Complex] + sage: differential_morphism = kenzo_chcm.differential() # optional - kenzo + sage: differential_morphism # optional - kenzo + [K... Morphism (degree -1): K... -> K...] + sage: differential_morphism.target_complex() # optional - kenzo + [K... Chain-Complex] + """ + return KenzoChainComplex(__trgt_aux__(self._kenzo)) + + def degree(self): + r""" + Return the degree of the morphism. + + OUTPUT: + + - An integer number, the degree of the morphism. + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) + sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) + sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo + sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo + sage: kenzo_chcm # optional - kenzo + [K... Chain-Complex] + sage: differential_morphism = kenzo_chcm.differential() # optional - kenzo + sage: differential_morphism # optional - kenzo + [K... Morphism (degree -1): K... -> K...] + sage: differential_morphism.degree() # optional - kenzo + -1 + sage: differential_morphism.composite(differential_morphism).degree() # optional - kenzo + -2 + sage: kenzo_chcm.null_morphism().degree() # optional - kenzo + 0 + """ + return __degr_aux__(self._kenzo).python() + + def evaluation(self, dim, comb): + r""" + Apply the morphism on a combination ``comb`` of dimension ``dim``. + + INPUT: + + - ``dim`` -- An integer number + + - ``comb`` -- A list representing a formal sum of generators in the module + of dimension ``dim``. For example, to represent G7G12 + 3*G7G0 - 5*G7G3 + we use the list [3, 'G7G0', -5, 'G7G3', 1, 'G7G12']. Note that the + generators must be in ascending order respect to the number after the + second G in their representation; the parameter + ``comb`` = [1, 'G7G12', 3, 'G7G0', -5, 'G7G3'] will produce an error in + Kenzo. + + OUTPUT: + + - A Kenzo combination representing the result of applying the morphism on the formal + combination represented by ``comb`` in the chain complex ``self`` in + dimension ``dim``. + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) + sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) + sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) + sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo + sage: kenzo_chcm # optional - kenzo + [K... Chain-Complex] + sage: differential_morphism = kenzo_chcm.differential() # optional - kenzo + sage: differential_morphism # optional - kenzo + [K... Morphism (degree -1): K... -> K...] + sage: dif_squared = differential_morphism.composite(differential_morphism) # optional - kenzo + sage: dif_squared # optional - kenzo + [K... Morphism (degree -2): K... -> K...] + sage: kenzo_chcm.basis(5) # optional - kenzo + ['G5G0', 'G5G1', 'G5G2'] + sage: kenzo_chcm.differential(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 4} + <6 * G4G0> + <-3 * G4G1> + ------------------------------------------------------------------------------ + + sage: differential_morphism.evaluation(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 4} + <6 * G4G0> + <-3 * G4G1> + ------------------------------------------------------------------------------ + + sage: dif_squared.evaluation(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 3} + ------------------------------------------------------------------------------ + + sage: idnt = kenzo_chcm.identity_morphism() # optional - kenzo + sage: idx2 = idnt.sum(idnt) # optional - kenzo + sage: idnt.evaluation(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 5} + <1 * G5G0> + <2 * G5G2> + ------------------------------------------------------------------------------ + + sage: idx2.evaluation(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 5} + <2 * G5G0> + <4 * G5G2> + ------------------------------------------------------------------------------ + + """ + if dim.is_integer(): + if isinstance(comb, list): + cmbn_list = pairing(comb) + return KenzoObject(__evaluation_aux1__(self._kenzo, dim, cmbn_list)) + else: + raise ValueError("'comb' parameter must be a list") + else: + raise ValueError("'dim' parameter must be an integer number") + + def opposite(self): + r""" + Return the opposite morphism of ``self``, i.e., -1 x ``self``. + + OUTPUT: + + - A :class:`KenzoChainComplexMorphism` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) + sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) + sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo + sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo + sage: kenzo_chcm # optional - kenzo + [K... Chain-Complex] + sage: idnt = kenzo_chcm.identity_morphism() # optional - kenzo + sage: idnt # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: opps_id = idnt.opposite() # optional - kenzo + sage: opps_id # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: kenzo_chcm.basis(4) # optional - kenzo + ['G4G0', 'G4G1'] + sage: idnt.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 4} + <2 * G4G0> + <-5 * G4G1> + ------------------------------------------------------------------------------ + + sage: opps_id.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 4} + <-2 * G4G0> + <5 * G4G1> + ------------------------------------------------------------------------------ + + """ + return KenzoChainComplexMorphism(__opps__(self._kenzo)) + + def composite(self, object=None): + r""" + Return the composite of ``self`` and the morphism(s) given by the parameter ``object``. + + INPUT: + + - ``object`` -- A KenzoChainComplexMorphism instance, a KenzoChainComplex instance, a tuple + of KenzoChainComplexMorphism and KenzoChainComplex instances, or None (default). + + OUTPUT: + + - A :class:`KenzoChainComplexMorphism`: if ``object`` is a KenzoChainComplexMorphism, the + composite of ``self`` and ``object`` is returned; if ``object`` is a KenzoChainComplex, + the composite of ``self`` and the differential morphism of ``object`` is returned; if + ``object`` is a tuple, the composite of ``self`` and the morphisms or the differential + morphisms of the given chain complexes in ``object`` is returned (if ``object`` is + None, ``self`` morphism is returned). + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere # optional - kenzo + sage: s2 = Sphere(2) # optional - kenzo + sage: s3 = Sphere(3) # optional - kenzo + sage: tp22 = s2.tensor_product(s2) # optional - kenzo + sage: tp23 = s2.tensor_product(s3) # optional - kenzo + sage: idnt = tp22.identity_morphism() # optional - kenzo + sage: idnt # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: null = tp23.null_morphism(target = tp22, degree = 4) # optional - kenzo + sage: null # optional - kenzo + [K... Morphism (degree 4): K... -> K...] + sage: idnt.composite((tp22, null)) # optional - kenzo + [K... Morphism (degree 3): K... -> K...] + """ + if object is None: + return self + if isinstance(object, KenzoChainComplexMorphism): + return KenzoChainComplexMorphism(__cmps__(self._kenzo, object._kenzo)) + elif isinstance(object, KenzoChainComplex): + return KenzoChainComplexMorphism(__cmps__(self._kenzo, __dffr_aux__(object._kenzo))) + elif isinstance(object, tuple): + rslt = self._kenzo + for mrph in object: + rslt = __cmps__(rslt, mrph._kenzo) + return KenzoChainComplexMorphism(rslt) + + def sum(self, object=None): + r""" + Return a morphism, sum of the morphism ``self`` and the morphism(s) given + by the parameter ``object``. + + INPUT: + + - ``object`` -- A KenzoChainComplexMorphism instance, a tuple of KenzoChainComplexMorphism + instances or None (default). + + OUTPUT: + + - A :class:`KenzoChainComplexMorphism`, sum of the morphism ``self`` and the morphism(s) + given by ``object`` (if ``object`` is None, ``self`` morphism is returned). + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) + sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) + sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo + sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo + sage: kenzo_chcm # optional - kenzo + [K... Chain-Complex] + sage: idnt = kenzo_chcm.identity_morphism() # optional - kenzo + sage: idnt # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: opps_id = idnt.opposite() # optional - kenzo + sage: opps_id # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: null = kenzo_chcm.null_morphism() # optional - kenzo + sage: null # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: idx2 = idnt.sum(idnt) # optional - kenzo + sage: idx5 = idx2.sum(\ + ....: (opps_id, idnt, idnt, null, idx2.sum(idnt), opps_id)) # optional - kenzo + sage: kenzo_chcm.basis(4) # optional - kenzo + ['G4G0', 'G4G1'] + sage: idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 4} + <4 * G4G0> + <-10 * G4G1> + ------------------------------------------------------------------------------ + + sage: idx5.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 4} + <10 * G4G0> + <-25 * G4G1> + ------------------------------------------------------------------------------ + + """ + if object is None: + return self + if isinstance(object, KenzoChainComplexMorphism): + return KenzoChainComplexMorphism(__add__(self._kenzo, object._kenzo)) + elif isinstance(object, tuple): + rslt = self._kenzo + for mrph in object: + rslt = __add__(rslt, mrph._kenzo) + return KenzoChainComplexMorphism(rslt) + + def substract(self, object=None): + r""" + Return a morphism, difference of the morphism ``self`` and the morphism(s) given by the + parameter ``object``. + + INPUT: + + - ``object`` -- A KenzoChainComplexMorphism instance, a tuple of KenzoChainComplexMorphism + instances or None (default). + + OUTPUT: + + - A :class:`KenzoChainComplexMorphism`, difference of the morphism ``self`` and the + morphism(s) given by ``object`` (if ``object`` is None, ``self`` morphism is returned). + For example, if ``object`` = (mrph1, mrph2, mrph3) the result is + ``self`` - mrph1 - mrph2 - mrph3. + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo + sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6]) + sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6]) + sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1]) + sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo + sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo + sage: kenzo_chcm # optional - kenzo + [K... Chain-Complex] + sage: idnt = kenzo_chcm.identity_morphism() # optional - kenzo + sage: idnt # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: opps_id = idnt.opposite() # optional - kenzo + sage: opps_id # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: null = kenzo_chcm.null_morphism() # optional - kenzo + sage: null # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: idx2 = idnt.substract(opps_id) # optional - kenzo + sage: opps_idx2 = idx2.substract\ + ....: ((opps_id, idnt, idnt, null, idx2.substract(opps_id))) # optional - kenzo + sage: kenzo_chcm.basis(4) # optional - kenzo + ['G4G0', 'G4G1'] + sage: idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 4} + <4 * G4G0> + <-10 * G4G1> + ------------------------------------------------------------------------------ + + sage: opps_idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo + + ----------------------------------------------------------------------{CMBN 4} + <-4 * G4G0> + <10 * G4G1> + ------------------------------------------------------------------------------ + + """ + if object is None: + return self + if isinstance(object, KenzoChainComplexMorphism): + return KenzoChainComplexMorphism(__sbtr__(self._kenzo, object._kenzo)) + elif isinstance(object, tuple): + rslt = self._kenzo + for mrph in object: + rslt = __sbtr__(rslt, mrph._kenzo) + return KenzoChainComplexMorphism(rslt) + + def change_source_target_complex(self, source=None, target=None): + r""" + Build, from the morphism ``self``, a new morphism with ``source`` + and ``target`` as source and target Kenzo chain complexes, respectively. + + INPUT: + + - ``source`` -- A KenzoChainComplex instance or None (default). + + - ``target`` -- A KenzoChainComplex instance or None (default). + + OUTPUT: + + - A :class:`KenzoChainComplexMorphism` inheriting from ``self`` the + degree (:degr slot in Kenzo), the algorithm (:intr slot in Kenzo) + and the strategy (:strt slot in Kenzo). The source and target slots + of this new morphism are given by the parameters ``source`` and + ``target`` respectively; if any parameter is ommited, the corresponding + slot is inherited from ``self``. + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere, KenzoChainComplex # optional - kenzo + sage: from sage.libs.ecl import ecl_eval + sage: ZCC = KenzoChainComplex(ecl_eval("(z-chcm)")) # optional - kenzo + sage: ZCC # optional - kenzo + [K... Chain-Complex] + sage: s2 = Sphere(2) # optional - kenzo + sage: s3 = Sphere(3) # optional - kenzo + sage: tp = s2.tensor_product(s3) # optional - kenzo + sage: tp # optional - kenzo + [K... Filtered-Chain-Complex] + sage: null = ZCC.null_morphism(tp) # optional - kenzo + sage: null # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: null.source_complex() # optional - kenzo + [K... Chain-Complex] + sage: null2 = null.change_source_target_complex(source = tp) # optional - kenzo + sage: null2 # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: null2.source_complex() # optional - kenzo + [K... Filtered-Chain-Complex] + """ + source = source or self.source_complex() + target = target or self.target_complex() + return KenzoChainComplexMorphism( + __change_sorc_trgt_aux__(self._kenzo, source._kenzo, target._kenzo)) + + def destructive_change_source_target_complex(self, source=None, target=None): + r""" + Modify destructively the morphism ``self`` taking ``source`` and ``target`` as source and + target Kenzo chain complexes of ``self``, respectively. + + INPUT: + + - ``source`` -- A KenzoChainComplex instance or None (default). + + - ``target`` -- A KenzoChainComplex instance or None (default). + + OUTPUT: + + - A :class:`KenzoChainComplexMorphism`. The source and target slots of ``self`` are replaced + respectively by the parameters ``source`` and ``target``; if any parameter is ommited, the + corresponding slot is inherited from ``self``. + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import Sphere, KenzoChainComplex # optional - kenzo + sage: from sage.libs.ecl import ecl_eval + sage: ZCC = KenzoChainComplex(ecl_eval("(z-chcm)")) # optional - kenzo + sage: ZCC # optional - kenzo + [K... Chain-Complex] + sage: s2 = Sphere(2) # optional - kenzo + sage: s3 = Sphere(3) # optional - kenzo + sage: tp = s2.tensor_product(s3) # optional - kenzo + sage: tp # optional - kenzo + [K... Filtered-Chain-Complex] + sage: null = ZCC.null_morphism(tp) # optional - kenzo + sage: null # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: null.target_complex() # optional - kenzo + [K... Filtered-Chain-Complex] + sage: null.destructive_change_source_target_complex(target = ZCC) # optional - kenzo + [K... Cohomology-Class on K... of degree 0] + sage: null.target_complex() # optional - kenzo + [K... Chain-Complex] + """ + source = source or self.source_complex() + target = target or self.target_complex() + return KenzoChainComplexMorphism( + __dstr_change_sorc_trgt_aux__(self._kenzo, source._kenzo, target._kenzo)) + + +def build_morphism(source_complex, target_complex, degree, algorithm, strategy, orgn): + r""" + Build a morphism of chain complexes by means of the corresponding build-mrph Kenzo + function. + + INPUT: + + - ``source_complex`` -- The source object as a KenzoChainComplex instance + + - ``target_complex`` -- The target object as a KenzoChainComplex instance + + - ``degree`` -- An integer number representing the degree of the morphism + + - ``algorithm`` -- A Lisp function defining the mapping (:intr slot in Kenzo) + + - ``strategy`` -- The strategy (:strt slot in Kenzo), which must be one of + the two strings ``gnrt`` or ``cmbn``, depending if the ``algorithm`` (a Lisp + function) uses as arguments a degree and a generator or a combination, + respectively. + + - ``orgn`` -- A list containing a description about the origin of the morphism + + OUTPUT: + + - A :class:`KenzoChainComplexMorphism` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KenzoChainComplex,\ + ....: build_morphism # optional - kenzo + sage: from sage.libs.ecl import ecl_eval + sage: ZCC = KenzoChainComplex(ecl_eval("(z-chcm)")) # optional - kenzo + sage: A = build_morphism(ZCC, ZCC, -1,\ + ....: ecl_eval("#'(lambda (comb) (cmbn (1- (degr comb))))"),\ + ....: "cmbn", ["zero morphism on ZCC"]) # optional - kenzo + sage: A.target_complex() # optional - kenzo + [K... Chain-Complex] + sage: A.degree() # optional - kenzo + -1 + sage: type(A) # optional - kenzo + + """ + return KenzoChainComplexMorphism( + __build_mrph_aux__(source_complex._kenzo, target_complex._kenzo, + degree, algorithm, ":"+strategy, orgn)) + + +def morphism_dictmat(morphism): + r""" + Computes a list of matrices in ECL associated to a morphism in Sage. + + INPUT: + + - ``morphism`` -- A morphism of chain complexes + + OUTPUT: + + - A :class:`EclObject` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import morphism_dictmat # optional - kenzo + sage: X = simplicial_complexes.Simplex(1) + sage: Y = simplicial_complexes.Simplex(0) + sage: g = Hom(X,Y)({0:0, 1:0}) + sage: f = g.associated_chain_complex_morphism() + sage: morphism_dictmat(f) # optional - kenzo + + """ + rslt = EclObject([]) + source = morphism.domain() + d = source.differential() + for k in d.keys(): + rslt = EclObject(k).cons(s2k_matrix(morphism.in_degree(k))).cons(rslt) + return rslt + + +def KChainComplexMorphism(morphism): + r""" + Construct a KenzoChainComplexMorphism from a ChainComplexMorphism in Sage. + + INPUT: + + - ``morphism`` -- A morphism of chain complexes + + OUTPUT: + + - A :class:`KenzoChainComplexMorphism` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import KChainComplexMorphism # optional - kenzo + sage: C = ChainComplex({0: identity_matrix(ZZ, 1)}) + sage: D = ChainComplex({0: zero_matrix(ZZ, 1), 1: zero_matrix(ZZ, 1)}) + sage: f = Hom(C,D)({0: identity_matrix(ZZ, 1), 1: zero_matrix(ZZ, 1)}) + sage: g = KChainComplexMorphism(f) # optional - kenzo + sage: g # optional - kenzo + [K... Morphism (degree 0): K... -> K...] + sage: g.source_complex() # optional - kenzo + [K... Chain-Complex] + sage: g.target_complex() # optional - kenzo + [K... Chain-Complex] + """ + source = KChainComplex(morphism.domain()) + target = KChainComplex(morphism.codomain()) + matrix_list = morphism_dictmat(morphism) + return KenzoChainComplexMorphism( + __kmorphismchaincomplex_aux1__(matrix_list, source._kenzo, target._kenzo)) + + +def s2k_listofmorphisms(l): + r""" + Computes a list of morphisms of chain complexes in Kenzo from a list of morphisms in Sage. + + INPUT: + + - ``l`` -- A list of morphisms of chain complexes + + OUTPUT: + + - A :class:`EclObject` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import s2k_listofmorphisms # optional - kenzo + sage: C1 = ChainComplex({1: matrix(ZZ, 0, 2, [])}, degree_of_differential=-1) + sage: C2 = ChainComplex({1: matrix(ZZ, 1, 2, [1, 0])},degree_of_differential=-1) + sage: C3 = ChainComplex({0: matrix(ZZ, 0,2 , [])},degree_of_differential=-1) + sage: M1 = Hom(C2,C1)({1: matrix(ZZ, 2, 2, [2, 0, 0, 2])}) + sage: M2 = Hom(C3,C2)({0: matrix(ZZ, 1, 2, [2, 0])}) + sage: l = [M1, M2] + sage: s2k_listofmorphisms(l) # optional - kenzo + K...] [K... Morphism (degree 0): K... -> K...])> + """ + rslt = EclObject([]) + for m in l: + rslt = EclObject(KChainComplexMorphism(m)._kenzo).cons(rslt) + return __nreverse__(rslt) + + +def BicomplexSpectralSequence(l): + r""" + Construct the spectral sequence associated to the bicomplex given by a list of morphisms. + + INPUT: + + - ``l`` -- A list of morphisms of chain complexes + + OUTPUT: + + - A :class:`KenzoSpectralSequence` + + EXAMPLES:: + + sage: from sage.interfaces.kenzo import BicomplexSpectralSequence # optional - kenzo + sage: C1 = ChainComplex({1: matrix(ZZ, 0, 2, [])}, degree_of_differential=-1) + sage: C2 = ChainComplex({1: matrix(ZZ, 1, 2, [1, 0])},degree_of_differential=-1) + sage: C3 = ChainComplex({0: matrix(ZZ, 0,2 , [])},degree_of_differential=-1) + sage: M1 = Hom(C2,C1)({1: matrix(ZZ, 2, 2, [2, 0, 0, 2])}) + sage: M2 = Hom(C3,C2)({0: matrix(ZZ, 1, 2, [2, 0])}) + sage: l = [M1, M2] + sage: E = BicomplexSpectralSequence(l) # optional - kenzo + sage: E.group(2,0,1) # optional - kenzo + Additive abelian group isomorphic to Z/2 + Z + sage: E.table(3,0,2,0,2) # optional - kenzo + 0 0 0 + Z/2 + Z/4 0 0 + 0 0 Z + sage: E.matrix(2,2,0) # optional - kenzo + [ 0 0] + [-4 0] + """ + return KenzoSpectralSequence(__bicomplex_spectral_sequence__(s2k_listofmorphisms(l))) diff --git a/src/sage/interfaces/latte.py b/src/sage/interfaces/latte.py index 5388ad44e04..59d1b02e40b 100644 --- a/src/sage/interfaces/latte.py +++ b/src/sage/interfaces/latte.py @@ -12,7 +12,7 @@ #***************************************************************************** from __future__ import print_function, absolute_import -import six + from sage.cpython.string import str_to_bytes, bytes_to_str from subprocess import Popen, PIPE @@ -70,12 +70,12 @@ def count(arg, ehrhart_polynomial=False, multivariate_generating_function=False, sage: print(count(cddin, **opts)) # optional - latte_int x[0]^2*x[1]^(-2)*x[2]^(-2)/((1-x[1])*(1-x[2])*(1-x[0]^(-1))) + x[0]^(-2)*x[1]^(-2)*x[2]^(-2)/((1-x[1])*(1-x[2])*(1-x[0])) - + x[0]^2*x[1]^(-2)*x[2]^2/((1-x[1])*(1-x[0]^(-1))*(1-x[2]^(-1))) + + x[0]^2*x[1]^(-2)*x[2]^2/((1-x[1])*(1-x[2]^(-1))*(1-x[0]^(-1))) + x[0]^(-2)*x[1]^(-2)*x[2]^2/((1-x[1])*(1-x[0])*(1-x[2]^(-1))) - + x[0]^2*x[1]^2*x[2]^(-2)/((1-x[2])*(1-x[0]^(-1))*(1-x[1]^(-1))) + + x[0]^2*x[1]^2*x[2]^(-2)/((1-x[2])*(1-x[1]^(-1))*(1-x[0]^(-1))) + x[0]^(-2)*x[1]^2*x[2]^(-2)/((1-x[2])*(1-x[0])*(1-x[1]^(-1))) - + x[0]^2*x[1]^2*x[2]^2/((1-x[0]^(-1))*(1-x[1]^(-1))*(1-x[2]^(-1))) - + x[0]^(-2)*x[1]^2*x[2]^2/((1-x[0])*(1-x[1]^(-1))*(1-x[2]^(-1))) + + x[0]^2*x[1]^2*x[2]^2/((1-x[2]^(-1))*(1-x[1]^(-1))*(1-x[0]^(-1))) + + x[0]^(-2)*x[1]^2*x[2]^2/((1-x[0])*(1-x[2]^(-1))*(1-x[1]^(-1))) TESTS: @@ -344,7 +344,7 @@ def integrate(arg, polynomial=None, algorithm='triangulate', raw_output=False, v args.append('--{}={}'.format(key, value)) if got_polynomial: - if not isinstance(polynomial, six.string_types): + if not isinstance(polynomial, str): # transform polynomial to LattE description monomials_list = to_latte_polynomial(polynomial) else: diff --git a/src/sage/interfaces/lisp.py b/src/sage/interfaces/lisp.py index 5fa799702b6..39094d2497d 100644 --- a/src/sage/interfaces/lisp.py +++ b/src/sage/interfaces/lisp.py @@ -48,7 +48,7 @@ # # Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # ########################################################################## @@ -57,6 +57,7 @@ from .expect import Expect, ExpectElement, ExpectFunction, FunctionElement, gc_disabled from sage.structure.element import RingElement, parent from sage.docs.instancedoc import instancedoc +from sage.structure.richcmp import rich_to_bool class Lisp(Expect): @@ -387,7 +388,7 @@ def function_call(self, function, args=None, kwds=None): # Inherit from RingElement to make __pow__ work @instancedoc class LispElement(RingElement, ExpectElement): - def _cmp_(self, other): + def _richcmp_(self, other, op): """ EXAMPLES:: @@ -411,11 +412,11 @@ def _cmp_(self, other): other = P(other) if P.eval('(= %s %s)'%(self.name(), other.name())) == P._true_symbol(): - return 0 + return rich_to_bool(op, 0) elif P.eval('(< %s %s)'%(self.name(), other.name())) == P._true_symbol(): - return -1 + return rich_to_bool(op, -1) else: - return 1 + return rich_to_bool(op, 1) def __bool__(self): """ diff --git a/src/sage/interfaces/macaulay2.py b/src/sage/interfaces/macaulay2.py index 5d8566d1a8e..a2e93ebb688 100644 --- a/src/sage/interfaces/macaulay2.py +++ b/src/sage/interfaces/macaulay2.py @@ -117,7 +117,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six import string_types import os import re @@ -858,7 +857,7 @@ def _macaulay2_input_ring(self, base_ring, vars, order='GRevLex'): sage: macaulay2._macaulay2_input_ring(R.base_ring(), R.gens(), 'Lex') # optional - macaulay2 'sage...[symbol x, MonomialSize=>16, MonomialOrder=>Lex]' """ - if not isinstance(base_ring, string_types): + if not isinstance(base_ring, str): base_ring = self(base_ring).name() varstr = str(vars)[1:-1].rstrip(',') @@ -1020,7 +1019,7 @@ def name(self, new_name=None): """ if new_name is None: return self._name - if not isinstance(new_name, string_types): + if not isinstance(new_name, str): raise TypeError("new_name must be a string") P = self.parent() diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index 4e8a47d972d..58da2ba4b53 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -213,7 +213,6 @@ #***************************************************************************** from __future__ import print_function from __future__ import absolute_import -from six import string_types import re import sys @@ -459,7 +458,7 @@ def _post_process_from_file(self, s): sage: magma._post_process_from_file("Hello") '' """ - if not isinstance(s, string_types): + if not isinstance(s, str): raise RuntimeError("Error evaluating object in %s:\n%s" % (self, s)) # Chop off the annoying "Loading ... " message that Magma # always outputs no matter what. @@ -2766,10 +2765,12 @@ class MagmaGBLogPrettyPrinter: A device which filters Magma Groebner basis computation logs. """ cmd_inpt = re.compile("^>>>$") - app_inpt = re.compile(r"^Append\(~_sage_, 0\);$") + app_inpt = re.compile("^Append\\(~_sage_, 0\\);$") - deg_curr = re.compile(r"^Basis length\: (\d+), queue length\: (\d+), step degree\: (\d+), num pairs\: (\d+)$") - pol_curr = re.compile(r"^Number of pair polynomials\: (\d+), at (\d+) column\(s\), .*") + deg_curr = re.compile( + "^Basis length\\: (\\d+), queue length\\: (\\d+), step degree\\: (\\d+), num pairs\\: (\\d+)$" + ) + pol_curr = re.compile("^Number of pair polynomials\\: (\\d+), at (\\d+) column\\(s\\), .*") def __init__(self, verbosity=1, style='magma'): """ @@ -2866,6 +2867,13 @@ def write(self, s): """ verbosity, style = self.verbosity, self.style + if isinstance(s, bytes): + # sys.stdout.encoding can be None or something else + if isinstance(sys.stdout.encoding, str): + s = s.decode(sys.stdout.encoding) + else: + s = s.decode("UTF-8") + if self.storage: s = self.storage + s self.storage = "" diff --git a/src/sage/interfaces/magma_free.py b/src/sage/interfaces/magma_free.py index 3b83007688c..b82b9c4699d 100644 --- a/src/sage/interfaces/magma_free.py +++ b/src/sage/interfaces/magma_free.py @@ -34,9 +34,8 @@ def magma_free_eval(code, strip=True, columns=0): sage: magma_free("Factorization(9290348092384)") # optional - internet [ <2, 5>, <290323377887, 1> ] """ - # import compatible with py2 and py3 - from six.moves.urllib.parse import urlencode - from six.moves import http_client as httplib + from urllib.parse import urlencode + from http import client as httplib from xml.dom.minidom import parseString server = "magma.maths.usyd.edu.au" diff --git a/src/sage/interfaces/maple.py b/src/sage/interfaces/maple.py index c3182ea7fb7..7c26f4bd8cc 100644 --- a/src/sage/interfaces/maple.py +++ b/src/sage/interfaces/maple.py @@ -244,6 +244,8 @@ from sage.misc.pager import pager from sage.interfaces.tab_completion import ExtraTabCompletion from sage.docs.instancedoc import instancedoc +from sage.structure.richcmp import rich_to_bool + COMMANDS_CACHE = '%s/maple_commandlist_cache.sobj' % DOT_SAGE @@ -947,7 +949,7 @@ def __hash__(self): """ return int(maple.eval('StringTools:-Hash(convert(%s, string))'%self.name())[1:-1],16) - def _cmp_(self, other): + def _richcmp_(self, other, op): """ Compare equality between self and other, using maple. @@ -1001,32 +1003,27 @@ def _cmp_(self, other): P = self.parent() if P.eval("evalb(%s %s %s)" % (self.name(), P._equality_symbol(), other.name())) == P._true_symbol(): - return 0 + return rich_to_bool(op, 0) # Maple does not allow comparing objects of different types and # it raises an error in this case. # We catch the error, and return True for < try: if P.eval("evalb(%s %s %s)" % (self.name(), P._lessthan_symbol(), other.name())) == P._true_symbol(): - return -1 + return rich_to_bool(op, -1) except RuntimeError as e: msg = str(e) if 'is not valid' in msg and 'to < or <=' in msg: if (hash(str(self)) < hash(str(other))): - return -1 + return rich_to_bool(op, -1) else: - return 1 + return rich_to_bool(op, 1) else: raise RuntimeError(e) if P.eval("evalb(%s %s %s)" % (self.name(), P._greaterthan_symbol(), other.name())) == P._true_symbol(): - return 1 - # everything is supposed to be comparable in Python, so we define - # the comparison thus when no comparable in interfaced system. - if (hash(self) < hash(other)): - return -1 - else: - return 1 + return rich_to_bool(op, 1) + return NotImplemented def _mul_(self, right): """ diff --git a/src/sage/interfaces/mathematica.py b/src/sage/interfaces/mathematica.py index 644b0b8ee69..802130b7a7b 100644 --- a/src/sage/interfaces/mathematica.py +++ b/src/sage/interfaces/mathematica.py @@ -172,8 +172,8 @@ :: - sage: e = mathematica('Exp[x] - 3x == 0') # optional - mathematica - sage: e.FindRoot(['x', 2]) # optional - mathematica + sage: eqn = mathematica('Exp[x] - 3x == 0') # optional - mathematica + sage: eqn.FindRoot(['x', 2]) # optional - mathematica {x -> 1.512134551657842} Note that this agrees with what the PARI interpreter gp produces:: @@ -354,6 +354,15 @@ sage: str(mathematica('Pi*x^2-1/2').N()) # optional -- mathematica 2 -0.5 + 3.14159 x + +Check that Mathematica's `E` exponential symbol is correctly backtranslated +as Sage's `e` (:trac:`29833`):: + + sage: x = var('x') + sage: (e^x)._mathematica_().sage() # optional -- mathematica + e^x + sage: exp(x)._mathematica_().sage() # optional -- mathematica + e^x """ #***************************************************************************** @@ -368,7 +377,7 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function @@ -381,6 +390,7 @@ from sage.interfaces.interface import AsciiArtString from sage.interfaces.tab_completion import ExtraTabCompletion from sage.docs.instancedoc import instancedoc +from sage.structure.richcmp import rich_to_bool def clean_output(s): @@ -982,19 +992,15 @@ def show(self, ImageSize=600): def str(self): return str(self) - def _cmp_(self, other): - #if not (isinstance(other, ExpectElement) and other.parent() is self.parent()): - # return coerce.cmp(self, other) + def _richcmp_(self, other, op): P = self.parent() if P.eval("%s < %s"%(self.name(), other.name())).strip() == 'True': - return -1 + return rich_to_bool(op, -1) elif P.eval("%s > %s"%(self.name(), other.name())).strip() == 'True': - return 1 + return rich_to_bool(op, 1) elif P.eval("%s == %s"%(self.name(), other.name())).strip() == 'True': - return 0 - else: - return -1 # everything is supposed to be comparable in Python, so we define - # the comparison thus when no comparable in interfaced system. + return rich_to_bool(op, 0) + return NotImplemented def __bool__(self): """ diff --git a/src/sage/interfaces/maxima.py b/src/sage/interfaces/maxima.py index d6d37fe3305..80369d52c86 100644 --- a/src/sage/interfaces/maxima.py +++ b/src/sage/interfaces/maxima.py @@ -128,9 +128,9 @@ sage: a = maxima('(1 + sqrt(2))^5') sage: float(a) - 82.01219330881975 + 82.0121933088197... sage: a.numer() - 82.01219330881975 + 82.0121933088197... :: @@ -464,7 +464,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import -from six import string_types import os import re @@ -523,10 +522,10 @@ def __init__(self, script_subdirectory=None, logfile=None, server=None, # setting inchar and outchar.. eval_using_file_cutoff = 256 self.__eval_using_file_cutoff = eval_using_file_cutoff - STARTUP = os.path.join(SAGE_LOCAL,'bin','sage-maxima.lisp') + STARTUP = os.path.join(os.path.dirname(__file__), 'sage-maxima.lisp') if not os.path.exists(STARTUP): - raise RuntimeError('You must get the file local/bin/sage-maxima.lisp') + raise RuntimeError('You must get the file sage-maxima.lisp') #self.__init_code = init_code if init_code is None: @@ -554,7 +553,7 @@ def __init__(self, script_subdirectory=None, logfile=None, server=None, init_code = init_code, logfile = logfile, eval_using_file_cutoff=eval_using_file_cutoff) - # Must match what is in the file local/bin/sage-maxima.lisp + # Must match what is in the file sage-maxima.lisp self._display_prompt = '' # See #15440 for the importance of the trailing space self._output_prompt_re = re.compile(r'\(\%o[0-9]+\) ') @@ -994,7 +993,7 @@ def set(self, var, value): sage: maxima.get('xxxxx') '2' """ - if not isinstance(value, string_types): + if not isinstance(value, str): raise TypeError cmd = '%s : %s$'%(var, value.rstrip(';')) if len(cmd) > self.__eval_using_file_cutoff: diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index bb23d7dbebd..ed3055da9d2 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -50,7 +50,6 @@ #***************************************************************************** from __future__ import print_function from __future__ import absolute_import -from six import string_types import os import re @@ -646,11 +645,11 @@ def function(self, args, defn, rep=None, latex=None): name = self._next_var_name() if isinstance(defn, MaximaAbstractElement): defn = defn.str() - elif not isinstance(defn, string_types): + elif not isinstance(defn, str): defn = str(defn) if isinstance(args, MaximaAbstractElement): args = args.str() - elif not isinstance(args, string_types): + elif not isinstance(args, str): args = str(args) cmd = '%s(%s) := %s' % (name, args, defn) self._eval_line(cmd) @@ -856,7 +855,7 @@ def de_solve(self, de, vars, ics=None): sage: maxima.de_solve('diff(y,x) + 3*x = y', ['x','y'],[1,1]) y=-%e^-1*(5*%e^x-3*%e*x-3*%e) """ - if not isinstance(vars, string_types): + if not isinstance(vars, str): str_vars = '%s, %s'%(vars[1], vars[0]) else: str_vars = vars @@ -1519,7 +1518,7 @@ def nintegral(self, var='x', a=0, b=1, EXAMPLES:: sage: maxima('exp(-sqrt(x))').nintegral('x',0,1) - (0.5284822353142306, 0.41633141378838...e-10, 231, 0) + (0.5284822353142306, 4.1633141378838...e-11, 231, 0) Note that GP also does numerical integration, and can do so to very high precision very quickly:: diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 7053f351680..75826f679f8 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -82,10 +82,10 @@ # # The full text of the GPL is available at: # -# https://www.gnu.org/licenses/ -# **************************************************************************** -from __future__ import print_function, absolute_import -from six import string_types +# http://www.gnu.org/licenses/ +#***************************************************************************** +from __future__ import print_function +from __future__ import absolute_import from sage.symbolic.ring import SR @@ -452,7 +452,7 @@ def _eval_line(self, line, locals=None, reformat=True, **kwds): statement = line[:ind_semi] line = line[ind_semi+1:] if statement: - result = ((result + '\n') if result else '') + max_to_string(maxima_eval("#$%s$"%statement)) + result = ((result + '\n') if result else '') + max_to_string(maxima_eval("#$%s$"%statement)) else: statement = line[:ind_dollar] line = line[ind_dollar+1:] @@ -508,7 +508,7 @@ def set(self, var, value): sage: maxima_lib.get('xxxxx') '2' """ - if not isinstance(value, string_types): + if not isinstance(value, str): raise TypeError cmd = '%s : %s$'%(var, value.rstrip(';')) self.eval(cmd) @@ -1011,7 +1011,7 @@ def _missing_assumption(self,errstr): """ Helper function for unified handling of failed computation because an assumption was missing. - + EXAMPLES:: sage: from sage.interfaces.maxima_lib import maxima_lib @@ -1027,7 +1027,7 @@ def _missing_assumption(self,errstr): if errstr[3] == ' ': jj = 3 k = errstr.find(' ',jj+1) - + outstr = "Computation failed since Maxima requested additional constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume("\ + errstr[jj+1:k] +">0)', see `assume?` for more details)\n" + errstr outstr = outstr.replace('_SAGE_VAR_','') diff --git a/src/sage/interfaces/octave.py b/src/sage/interfaces/octave.py index d71a155f85c..0adfda53c68 100644 --- a/src/sage/interfaces/octave.py +++ b/src/sage/interfaces/octave.py @@ -145,7 +145,7 @@ import os from .expect import Expect, ExpectElement import pexpect -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.docs.instancedoc import instancedoc from sage.cpython.string import bytes_to_str diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index 11bdd5cfeff..b4f1a4760b1 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -22,12 +22,9 @@ # # The full text of the GPL is available at: # -# hsttp://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -import six -from six.moves import range -from six import reraise as raise_ import os import re @@ -37,9 +34,10 @@ from .expect import Expect from .interface import (Interface, InterfaceElement, InterfaceFunctionElement) -from sage.misc.misc import get_verbose +from sage.misc.verbose import get_verbose from sage.misc.cachefunc import cached_method from sage.interfaces.tab_completion import ExtraTabCompletion +from sage.structure.richcmp import rich_to_bool import pexpect from random import randrange @@ -589,7 +587,7 @@ def set(self, var, value): 9 36 84 126 126 84 36 9 """ - if isinstance(value, six.string_types): + if isinstance(value, str): value = value.strip().rstrip(';').strip() cmd = "@{}{}({});".format(var, self._assign_symbol(), value) self.eval(cmd) @@ -979,7 +977,7 @@ def _repr_(self): out = P.get(name).strip() return out - def _cmp_(self, other): + def _richcmp_(self, other, op): """ Comparison of polymake elements. @@ -1011,12 +1009,12 @@ def _cmp_(self, other): """ P = self._check_valid() if P.eval("print {} {} {};".format(self.name(), P._equality_symbol(), other.name())).strip() == P._true_symbol(): - return 0 + return rich_to_bool(op, 0) if P.eval("print {} {} {};".format(self.name(), P._lessthan_symbol(), other.name())).strip() == P._true_symbol(): - return -1 + return rich_to_bool(op, -1) if P.eval("print {} {} {};".format(self.name(), P._greaterthan_symbol(), other.name())).strip() == P._true_symbol(): - return 1 - return -2 # that's supposed to be an error value. + return rich_to_bool(op, 1) + return NotImplemented def __bool__(self): """ @@ -2137,7 +2135,7 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if except (TypeError, RuntimeError): pass return self._eval_line(line, allow_use_file=allow_use_file, wait_for_prompt=wait_for_prompt, restart_if_needed=False, **kwds) - raise_(RuntimeError, "{}\nError evaluating {} in {}".format(msg, line, self), sys.exc_info()[2]) + raise RuntimeError("{}\nError evaluating {} in {}".format(msg, line, self)) p_warnings = [] p_errors = [] @@ -2148,7 +2146,7 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if first = True while True: try: - if isinstance(wait_for_prompt, six.string_types): + if isinstance(wait_for_prompt, str): pat = E.expect(wait_for_prompt, **kwds) else: pat = E.expect_list(self._prompt, **kwds) diff --git a/src/sage/interfaces/povray.py b/src/sage/interfaces/povray.py index 7dcf963d84d..880c5fecbcd 100644 --- a/src/sage/interfaces/povray.py +++ b/src/sage/interfaces/povray.py @@ -1,7 +1,6 @@ r""" POV-Ray, The Persistence of Vision Ray Tracer """ -from six import iteritems from sage.misc.pager import pager import os @@ -42,7 +41,7 @@ def __call__(self, pov_file, outfile='sage.ppm', block=True, **kwargs): return "You must specify a width and height." cmd = "povray -D +FP +I%s +O%s " % (pov_file, outfile) - for k, v in iteritems(kwargs): + for k, v in kwargs.items(): cmd += "+%s%s " % (k, v) if not block: diff --git a/src/sage/interfaces/primecount.pyx b/src/sage/interfaces/primecount.pyx index 6f0fea413f8..45a80c648dd 100644 --- a/src/sage/interfaces/primecount.pyx +++ b/src/sage/interfaces/primecount.pyx @@ -1,3 +1,5 @@ +# sage_setup: distribution = sage-primecount + r""" Interface to the primecount library """ diff --git a/src/sage/interfaces/qepcad.py b/src/sage/interfaces/qepcad.py index 508d1ef3f99..9484212f5c6 100644 --- a/src/sage/interfaces/qepcad.py +++ b/src/sage/interfaces/qepcad.py @@ -605,7 +605,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import -from six import string_types from sage.env import SAGE_LOCAL import pexpect @@ -839,12 +838,12 @@ def __init__(self, formula, varlist = None if vars is not None: - if isinstance(vars, string_types): + if isinstance(vars, str): varlist = vars.strip('()').split(',') else: varlist = [str(v) for v in vars] - if isinstance(formula, string_types): + if isinstance(formula, str): if varlist is None: raise ValueError("vars must be specified if formula is a string") @@ -920,7 +919,7 @@ def assume(self, assume): sage: qe.finish() # optional - qepcad 4 a c - b^2 <= 0 """ - if not isinstance(assume, string_types): + if not isinstance(assume, str): assume = qepcad_formula.formula(assume) if len(assume.qvars): raise ValueError("assumptions cannot be quantified") @@ -1622,7 +1621,7 @@ def qepcad(formula, assume=None, interact=False, solution=None, formula = qepcad_formula.formula(formula) if len(formula.qvars) == 0: if vars is None: - vars = sorted(list(formula.vars)) + vars = sorted(formula.vars) formula = qepcad_formula.exists(vars, formula) vars = None use_witness = True diff --git a/src/sage/interfaces/qsieve.py b/src/sage/interfaces/qsieve.py index 2006e6fbb8f..5b098130255 100644 --- a/src/sage/interfaces/qsieve.py +++ b/src/sage/interfaces/qsieve.py @@ -7,8 +7,6 @@ import os import subprocess as sp -import six - import sage.rings.integer from sage.cpython.string import bytes_to_str @@ -83,15 +81,10 @@ def qsieve_block(n, time, verbose=False): if time: cmd = ['time'] + cmd - if six.PY2: - enc_kwds = {} - else: - enc_kwds = {'encoding': 'latin1'} - env = os.environ.copy() env['TMPDIR'] = tmp_dir('qsieve') p = sp.Popen(cmd, env=env, stdout=sp.PIPE, stderr=sp.STDOUT, - stdin=sp.PIPE, **enc_kwds) + stdin=sp.PIPE, encoding='latin1') out, err = p.communicate(str(n)) z = data_to_list(out, n, time=time) if verbose: diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 2939d46644b..720bf5c71ad 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -11,7 +11,7 @@ The %R and %%R interface creating an R line or an R cell in the Jupyter notebook are briefly decribed at the end of this page. This documentation will be expanded and placed in the Jupyter notebook -manual when this manual exists. +manual when this manual exists. The following examples try to follow "An Introduction to R" which can be found at http://cran.r-project.org/doc/manuals/R-intro.html . @@ -238,7 +238,7 @@ - R graphics are (beautifully) displayed in output cells, but are not directly importable. You have to save them as .png, .pdf or .svg - files and import them in Sage for further use. + files and import them in Sage for further use. In its current incarnation, this interface is mostly useful to statisticians needing Sage for a few symbolic computations but mostly @@ -265,8 +265,6 @@ # ########################################################################## from __future__ import print_function, absolute_import -from six.moves import range -import six from .interface import Interface, InterfaceElement, InterfaceFunction, InterfaceFunctionElement from sage.env import DOT_SAGE @@ -1584,7 +1582,7 @@ def __getitem__(self, n): [1] 1 3 """ P = self._check_valid() - if isinstance(n, six.string_types): + if isinstance(n, str): n = n.replace('self', self._name) return P.new('%s[%s]'%(self._name, n)) elif parent(n) is P: # the key is RElement itself @@ -1872,7 +1870,7 @@ def _instancedoc_(self): title ----- - Length of an Object + Length of an Object name ---- @@ -1969,7 +1967,7 @@ def _instancedoc_(self): title ----- - Length of an Object + Length of an Object name ---- diff --git a/src/sage/interfaces/rubik.py b/src/sage/interfaces/rubik.py index 233626de4ef..298bceabd0c 100644 --- a/src/sage/interfaces/rubik.py +++ b/src/sage/interfaces/rubik.py @@ -65,6 +65,7 @@ class SingNot: """ This class is to resolve difference between various Singmaster notation. + Case is ignored, and the second and third letters may be swapped. EXAMPLES:: @@ -77,7 +78,7 @@ class SingNot: """ def __init__(self, s): self.rep = s - self.canonical = (s[0] + "".join(sorted(list(s[1:])))).lower() + self.canonical = (s[0] + "".join(sorted(s[1:]))).lower() def __eq__(self, other): return isinstance(other, SingNot) and other.canonical == self.canonical def __repr__(self): @@ -88,6 +89,7 @@ def __hash__(self): # This is our list singmaster_list = [''] + [SingNot(index2singmaster(i+1)) for i in range(48)]; singmaster_list + class OptimalSolver: """ Interface to Michael Reid's optimal Rubik's Cube solver. diff --git a/src/bin/sage-maxima.lisp b/src/sage/interfaces/sage-maxima.lisp similarity index 100% rename from src/bin/sage-maxima.lisp rename to src/sage/interfaces/sage-maxima.lisp diff --git a/src/sage/interfaces/sage0.py b/src/sage/interfaces/sage0.py index 03a19698c96..7d0aa68a6b3 100644 --- a/src/sage/interfaces/sage0.py +++ b/src/sage/interfaces/sage0.py @@ -15,10 +15,9 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six import iteritems, string_types -from six.moves import cPickle import os +import pickle import re import textwrap @@ -148,7 +147,7 @@ def __init__(self, if init_code is None: init_code = [] - elif isinstance(init_code, string_types): + elif isinstance(init_code, str): init_code = init_code.splitlines() else: try: @@ -175,11 +174,11 @@ def __init__(self, ]) prompt = re.compile(b'sage: ') - init_code.append('from six.moves import cPickle') + init_code.append('import pickle') init_code.append(textwrap.dedent(""" def _sage0_load_local(filename): with open(filename, 'rb') as f: - return cPickle.load(f) + return pickle.load(f) """)) init_code.append(textwrap.dedent(""" def _sage0_load_remote(filename): @@ -254,12 +253,12 @@ def __call__(self, x): else: return self(x.sage()) - if isinstance(x, string_types): + if isinstance(x, str): return SageElement(self, x) if self.is_local(): with open(self._local_tmpfile(), 'wb') as fobj: - fobj.write(cPickle.dumps(x, 2)) + fobj.write(pickle.dumps(x, 2)) code = '_sage0_load_local({!r})'.format(self._local_tmpfile()) return SageElement(self, code) else: @@ -387,7 +386,7 @@ def console(self): sage: sage0.console() #not tested ---------------------------------------------------------------------- | SageMath version ..., Release Date: ... | - | Type notebook() for the GUI, and license() for information. | + | Using Python .... Type "help()" for help. | ---------------------------------------------------------------------- ... """ @@ -538,7 +537,7 @@ def __call__(self, *args, **kwds): # get cleared from the interpreter before we complete the function # call. args = [P(x) for x in args] - kwds = [(k, P(v)) for k, v in iteritems(kwds)] + kwds = [(k, P(v)) for k, v in kwds.items()] arg_str = ','.join(x.name() for x in args) kwd_str = ','.join('%s=%s' % (k, v.name()) for k, v in kwds) @@ -602,7 +601,7 @@ def sage0_console(): sage: sage0_console() #not tested ---------------------------------------------------------------------- | SageMath version ..., Release Date: ... | - | Type notebook() for the GUI, and license() for information. | + | Using Python .... Type "help()" for help. | ---------------------------------------------------------------------- ... """ diff --git a/src/sage/interfaces/scilab.py b/src/sage/interfaces/scilab.py index 466347bd9a5..6ea490d5603 100644 --- a/src/sage/interfaces/scilab.py +++ b/src/sage/interfaces/scilab.py @@ -164,7 +164,7 @@ sage: M(9) = x # optional - scilab Traceback (most recent call last): ... - SyntaxError: can't assign to function call (..., line 1) + SyntaxError: can...t assign to function call (..., line 1) AUTHORS: diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 2f08a9dd3c3..cdec80d20c0 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -318,9 +318,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range -from six import integer_types, string_types -from six import reraise as raise_ import io import os @@ -338,7 +335,7 @@ import sage.rings.integer from sage.env import SINGULARPATH -from sage.misc.misc import get_verbose +from sage.misc.verbose import get_verbose from sage.docs.instancedoc import instancedoc @@ -586,6 +583,7 @@ def eval(self, x, allow_semicolon=True, strip=True, **kwds): :: + sage: from sage.misc.verbose import set_verbose sage: set_verbose(1) sage: o = singular.eval('hilb(%s)'%(s.name())) // 1 t^0 @@ -815,7 +813,7 @@ def _coerce_map_from_(self, S): if hasattr(S, 'an_element'): if hasattr(S.an_element(), '_singular_'): return True - elif S in integer_types: + elif S is int: return True return None @@ -901,7 +899,7 @@ def ideal(self, *gens): x0*x1-x0*x2-x1*x2, x0^2*x2-x0*x2^2-x1*x2^2 """ - if isinstance(gens, string_types): + if isinstance(gens, str): gens = self(gens) if isinstance(gens, SingularElement): @@ -1049,7 +1047,7 @@ def ring(self, char=0, vars='(x)', order='lp', check=True): for x in vars[1:-1].split(',')]) self.eval(s) - if check and isinstance(char, integer_types + (sage.rings.integer.Integer,)): + if check and isinstance(char, (int, sage.rings.integer.Integer)): if char != 0: n = sage.rings.integer.Integer(char) if not n.is_prime(): @@ -1280,7 +1278,7 @@ def __init__(self, parent, type, value, is_name=False): # coercion to work properly. except SingularError as x: self._session_number = -1 - raise_(TypeError, TypeError(x), sys.exc_info()[2]) + raise TypeError(x) except BaseException: self._session_number = -1 raise @@ -1678,6 +1676,18 @@ def sage_poly(self, R=None, kcache=None): sage: P2.0.lift().parent() Multivariate Polynomial Ring in x, y over Rational Field + Test that :trac:`29396` is fixed:: + + sage: Rxz. = RR[] + sage: f = x**3 + x*z + 1 + sage: f.discriminant(x) + -4.00000000000000*z^3 - 27.0000000000000 + sage: Rx. = RR[] + sage: Rx("x + 7.5")._singular_().sage_poly() + x + 7.50000 + sage: Rx("x + 7.5")._singular_().sage_poly(Rx) + x + 7.50000000000000 + AUTHORS: - Martin Albrecht (2006-05-18) @@ -1766,7 +1776,7 @@ def sage_poly(self, R=None, kcache=None): exp = dict() monomial = singular_poly_list[i] - if monomial!="1": + if monomial not in ['1', '(1.000e+00)']: variables = [var.split("^") for var in monomial.split("*") ] for e in variables: var = e[0] @@ -1794,7 +1804,7 @@ def sage_poly(self, R=None, kcache=None): monomial = singular_poly_list[i] exp = int(0) - if monomial!="1": + if monomial not in ['1', '(1.000e+00)']: term = monomial.split("^") if len(term)==int(2): exp = int(term[1]) diff --git a/src/sage/interfaces/sympy.py b/src/sage/interfaces/sympy.py index 97a5cd04394..cc35a42a9fa 100644 --- a/src/sage/interfaces/sympy.py +++ b/src/sage/interfaces/sympy.py @@ -54,6 +54,19 @@ def _sympysage_float(self): from sage.rings.real_mpfr import create_RealNumber return create_RealNumber(str(self)) +def _sympysage_integer(self): + """ + EXAMPLES:: + + sage: from sympy.core.numbers import Integer as SympyInt + sage: assert SR(2)._sympy_() == SympyInt(int(2)) + sage: assert SR(2) == SympyInt(int(2))._sage_() + sage: type(SympyInt(int(2))._sage_()) + + """ + from sage.rings.integer import Integer + return Integer(self.p) + def _sympysage_rational(self): """ EXAMPLES:: @@ -328,10 +341,30 @@ def _sympysage_derivative(self): sage: sympy_diff = Derivative(f(x)._sympy_(), x._sympy_()) sage: assert diff(f(x),x)._sympy_() == sympy_diff sage: assert diff(f(x),x) == sympy_diff._sage_() + + TESTS: + + Check that :trac:`28964` is fixed:: + + sage: f = function('f') + sage: _ = var('x,t') + sage: assert diff(f(x, t), t)._sympy_()._sage_() == diff(f(x, t), t) + sage: assert diff(f(x, t), x, 2, t)._sympy_()._sage_() == diff(f(x, t), x, 2, t) + + sage: diff(f(x, t), x).integrate(x) + f(x, t) + sage: diff(f(x, t), x).integrate(t, algorithm='maxima') + integrate(diff(f(x, t), x), t) + sage: diff(f(x, t), x).integrate(t, algorithm='sympy') + integrate(diff(f(x, t), x), t) + sage: integrate(f(x, t), x).diff(t) + integrate(diff(f(x, t), t), x) """ from sage.calculus.functional import derivative + from sympy.core.containers import Tuple f = self.args[0]._sage_() - args = [[a._sage_() for a in arg] if isinstance(arg,tuple) else arg._sage_() for arg in self.args[2:]] + args = [a._sage_() for arg in self.args[1:] + for a in (arg if isinstance(arg, (tuple, Tuple)) else [arg])] return derivative(f, *args) def _sympysage_order(self): @@ -763,6 +796,7 @@ def sympy_init(): from sympy.series.order import Order Float._sage_ = _sympysage_float + Integer._sage_ = _sympysage_integer Rational._sage_ = _sympysage_rational Infinity._sage_ = _sympysage_pinfty NegativeInfinity._sage_ = _sympysage_ninfty diff --git a/src/sage/interfaces/tachyon.py b/src/sage/interfaces/tachyon.py index a8fc73441fc..8f1a1cf8281 100644 --- a/src/sage/interfaces/tachyon.py +++ b/src/sage/interfaces/tachyon.py @@ -74,7 +74,7 @@ class TachyonRT(SageObject): EXAMPLES: - + .. automethod:: __call__ """ def _repr_(self): @@ -173,7 +173,11 @@ def usage(self, use_pager=True): sage: from sage.interfaces.tachyon import TachyonRT sage: t = TachyonRT() sage: t.usage(use_pager=False) - Tachyon Parallel/Multiprocessor Ray Tracer Version... + ... + tachyon modelfile [options]... + + Model file formats supported: + filename.dat ... """ with os.popen('tachyon') as f: r = f.read() diff --git a/src/sage/interfaces/tests.py b/src/sage/interfaces/tests.py index fc77f129cbb..a6847a9c85e 100644 --- a/src/sage/interfaces/tests.py +++ b/src/sage/interfaces/tests.py @@ -31,8 +31,8 @@ ....: except IOError: ....: f = open('/dev/null', 'w') sage: kwds = dict(shell=True, stdout=f, stderr=f) - sage: subprocess.call("echo syntax error | ecl", **kwds) - 0 + sage: subprocess.call("echo syntax error | ecl", **kwds) in (0, 255) + True sage: subprocess.call("echo syntax error | gap", **kwds) in (0, 1) True sage: subprocess.call("echo syntax error | gp", **kwds) diff --git a/src/sage/knots/knot.py b/src/sage/knots/knot.py index cde79e43973..f9b7ec532d7 100644 --- a/src/sage/knots/knot.py +++ b/src/sage/knots/knot.py @@ -18,8 +18,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from six import add_metaclass - from sage.knots.link import Link from sage.knots.knot_table import small_knots_table from sage.knots.gauss_code import (recover_orientations, dowker_to_gauss, @@ -32,8 +30,7 @@ from sage.categories.monoids import Monoids # We need Link to be first in the MRO in order to use its equality, hash, etc. -@add_metaclass(InheritComparisonClasscallMetaclass) -class Knot(Link, Element): +class Knot(Link, Element, metaclass=InheritComparisonClasscallMetaclass): r""" A knot. diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index eda927fb177..9ab236bd7c3 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -48,8 +48,6 @@ # **************************************************************************** from __future__ import division -from six.moves import range - from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ from sage.graphs.digraph import DiGraph @@ -312,6 +310,15 @@ def __init__(self, data): Traceback (most recent call last): ... ValueError: invalid input: data must be either a list or a braid + + Verify that :trac:`29692` is fixed:: + + sage: B = BraidGroup(5) + sage: L = Link(B([3,4,3,-4])) + sage: L + Link with 1 component represented by 4 crossings + sage: L.braid() + s0*s1*s0*s1^-1 """ if isinstance(data, list): if len(data) != 2 or not all(isinstance(i, list) for i in data[0]): @@ -340,20 +347,7 @@ def __init__(self, data): from sage.groups.braid import Braid, BraidGroup if isinstance(data, Braid): # Remove all unused strands - support = sorted(set(abs(x) for x in data.Tietze())) - i = 0 - cur = 1 - while i < len(support): - if support[i] == cur: - cur += 1 - i += 1 - elif support[i] == cur + 1: - support.insert(i, cur+1) - cur += 2 - i += 2 - else: - cur = support[i] - i += 1 + support = sorted(set().union(*((abs(x), abs(x) + 1) for x in data.Tietze()))) d = {} for i,s in enumerate(support): d[s] = i+1 @@ -361,7 +355,7 @@ def __init__(self, data): if not support: B = BraidGroup(2) else: - B = BraidGroup(len(support)+1) + B = BraidGroup(len(support)) self._braid = B([d[x] for x in data.Tietze()]) self._oriented_gauss_code = None self._pd_code = None @@ -429,14 +423,14 @@ def arcs(self, presentation='pd'): elif presentation == 'gauss_code': res = [] for comp in self.gauss_code(): - if not any(i<0 for i in comp): + if not any(i < 0 for i in comp): res.append(comp) else: rescom = [] par = [] for i in comp: par.append(i) - if i<0: + if i < 0: rescom.append(copy(par)) par = [i] rescom[0] = par + rescom[0] @@ -801,7 +795,7 @@ def _directions_of_edges(self): tails[C[2]] = C a = C[2] D = C - while not a in heads: + while a not in heads: next_crossing = [x for x in pd_code if a in x and x != D] if not next_crossing: heads[a] = D @@ -825,7 +819,7 @@ def _directions_of_edges(self): if a in x: D = x break - while not a in heads: + while a not in heads: tails[a] = D for x in pd_code: if a in x and x != D: @@ -1027,12 +1021,14 @@ def _enhanced_states(self): G = Graph() for j, cr in enumerate(crossings): n = nmax + j - if not v[j]: # For negative crossings, we go from undercrossings to the left + if not v[j]: + # For negative crossings, we go from undercrossings to the left G.add_edge((cr[3], cr[0], n), cr[0]) G.add_edge((cr[3], cr[0], n), cr[3]) G.add_edge((cr[1], cr[2], n), cr[2]) G.add_edge((cr[1], cr[2], n), cr[1]) - else: # positive crossings, from undercrossing to the right + else: + # positive crossings, from undercrossing to the right G.add_edge((cr[0], cr[1], n), cr[0]) G.add_edge((cr[0], cr[1], n), cr[1]) G.add_edge((cr[2], cr[3], n), cr[2]) @@ -1043,10 +1039,10 @@ def _enhanced_states(self): jmin = writhe + iindex - len(sm) jmax = writhe + iindex + len(sm) smoothings.append((tuple(v), sm, iindex, jmin, jmax)) - states = [] # we got all the smoothings, now find all the states + states = [] # we got all the smoothings, now find all the states for sm in smoothings: for k in range(len(sm[1])+1): - for circpos in combinations(sorted(sm[1]), k): # Add each state + for circpos in combinations(sorted(sm[1]), k): # Add each state circneg = sm[1].difference(circpos) j = writhe + sm[2] + len(circpos) - len(circneg) states.append((sm[0], tuple(sorted(circneg)), tuple(circpos), sm[2], j)) @@ -1088,7 +1084,7 @@ def _khovanov_homology_cached(self, height, ring=ZZ): ncross = len(crossings) states = [(_0, set(_1), set(_2), _3, _4) for (_0, _1, _2, _3, _4) in self._enhanced_states()] - bases = {} # arrange them by (i,j) + bases = {} # arrange them by (i,j) for st in states: i, j = st[3], st[4] if j == height: @@ -1108,7 +1104,7 @@ def _khovanov_homology_cached(self, height, ring=ZZ): difs = [index for index,value in enumerate(V1[0]) if value != V20[index]] if len(difs) == 1 and not (V2[2].intersection(V1[1]) or V2[1].intersection(V1[2])): m[ii,jj] = (-1)**sum(V2[0][x] for x in range(difs[0]+1, ncross)) - #Here we have the matrix constructed, now we have to put it in the dictionary of complexes + # Here we have the matrix constructed, now we have to put it in the dictionary of complexes else: m = matrix(ring, len(bases[(i,j)]), 0) complexes[i] = m.transpose() @@ -1576,39 +1572,25 @@ def seifert_matrix(self): """ x = self._braid_word_components_vector() h = self._homology_generators() - hl = len(h) - A = matrix(ZZ, hl, hl) indices = [i for i, hi in enumerate(h) if hi] - for i in indices: + N = len(indices) + A = matrix(ZZ, N, N, 0) + for ni, i in enumerate(indices): hi = h[i] - for j in range(i, hl): - if i == j: - A[i, j] = -(x[i] + x[hi]).sign() - elif hi > h[j]: - A[i, j] = 0 - A[j, i] = 0 - elif hi < j: - A[i, j] = 0 - A[j, i] = 0 - elif hi == j: + A[ni, ni] = -(x[i] + x[hi]).sign() + for nj in range(ni + 1, N): + j = indices[nj] + if hi > h[j] or hi < j: + continue + if hi == j: if x[j] > 0: - A[i, j] = 0 - A[j, i] = 1 + A[nj, ni] = 1 else: - A[i, j] = -1 - A[j, i] = 0 - elif abs(abs(x[i]) - abs(x[j])) > 1: - A[i, j] = 0 + A[ni, nj] = -1 elif abs(x[i]) - abs(x[j]) == 1: - A[i, j] = 0 - A[j, i] = -1 + A[nj, ni] = -1 elif abs(x[j]) - abs(x[i]) == 1: - A[i, j] = 1 - A[j, i] = 0 - else: # for debugging - A[i, j] = 2 - A[j, i] = 2 - A = A.matrix_from_rows_and_columns(indices, indices) + A[ni, nj] = 1 A.set_immutable() return A @@ -1857,6 +1839,7 @@ def alexander_polynomial(self, var='t'): t = R.gen() seifert_matrix = self.seifert_matrix() f = (seifert_matrix - t * seifert_matrix.transpose()).determinant() + # could we use a charpoly here ? or faster determinant ? if f != 0: exp = f.exponents() return t ** ((-max(exp) - min(exp)) // 2) * f @@ -2004,7 +1987,7 @@ def seifert_circles(self): result = [] # detect looped segments. They must be their own seifert circles for a in available_segments: - if any(C.count(a)>1 for C in self.pd_code()): + if any(C.count(a) > 1 for C in self.pd_code()): result.append([a]) # remove the looped segments from the available for a in result: @@ -2017,7 +2000,7 @@ def seifert_circles(self): else: C = heads[a] par = [] - while not a in par: + while a not in par: par.append(a) posnext = C[(C.index(a) + 1) % 4] if tails[posnext] == C and not [posnext] in result: @@ -2105,7 +2088,7 @@ def regions(self): while available_edges: edge = available_edges.pop() region = [] - while not edge in region: + while edge not in region: region.append(edge) if edge > 0: cros = heads[edge] @@ -3060,7 +3043,7 @@ def plot(self, gap=0.1, component_gap=0.5, solver=None, rev = segments[-e][1:] rev.reverse() sig = sign(s[edges.index(-e)]) - nregion+=[[a, -sig] for a in rev] + nregion += [[a, -sig] for a in rev] nregion.append([segments[-e][0], 1]) nregions.append(nregion) N = max(segments) + 1 @@ -3188,8 +3171,8 @@ def plot(self, gap=0.1, component_gap=0.5, solver=None, y1 = y0 elif direction == 3: x1 = x0 - y1 = y0 -l - im.append(([[x0,y0],[x1,y1]], l, direction)) + y1 = y0 - l + im.append(([[x0, y0], [x1, y1]], l, direction)) direction = (direction + turn) % 4 x0 = x1 y0 = y1 diff --git a/src/sage/lfunctions/dokchitser.py b/src/sage/lfunctions/dokchitser.py index 680ac17dfd4..46fe5e45746 100644 --- a/src/sage/lfunctions/dokchitser.py +++ b/src/sage/lfunctions/dokchitser.py @@ -34,7 +34,8 @@ from sage.structure.sage_object import SageObject from sage.rings.all import ComplexField, Integer -from sage.misc.all import verbose, sage_eval, SAGE_TMP +from sage.misc.all import sage_eval, SAGE_TMP +from sage.misc.verbose import verbose import sage.interfaces.gp from sage.env import SAGE_EXTCODE @@ -109,9 +110,8 @@ class Dokchitser(SageObject): 48 sage: L.taylor_series(1,4) 0.000000000000000 + 0.305999773834052*z + 0.186547797268162*z^2 - 0.136791463097188*z^3 + O(z^4) - sage: L.check_functional_equation() - 6.11218974700000e-18 # 32-bit - 6.04442711160669e-18 # 64-bit + sage: L.check_functional_equation() # abs tol 1e-19 + 6.04442711160669e-18 RANK 2 ELLIPTIC CURVE: @@ -668,9 +668,8 @@ def check_functional_equation(self, T=1.2): EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') - sage: L.check_functional_equation() - -1.35525271600000e-20 # 32-bit - -2.71050543121376e-20 # 64-bit + sage: L.check_functional_equation() # abs tol 1e-19 + -2.71050543121376e-20 If we choose the sign in functional equation for the `\zeta` function incorrectly, the functional equation diff --git a/src/sage/lfunctions/pari.py b/src/sage/lfunctions/pari.py index c60f944ed4e..c45f3bfd5c0 100644 --- a/src/sage/lfunctions/pari.py +++ b/src/sage/lfunctions/pari.py @@ -25,9 +25,7 @@ from cypari2.gen import Gen from sage.libs.pari import pari from sage.structure.sage_object import SageObject -from sage.rings.all import (ZZ, RealField, ComplexField, - PowerSeriesRing, IntegerModRing) -from sage.rings.complex_field import is_ComplexField +from sage.rings.all import (ZZ, RealField, ComplexField, PowerSeriesRing) class lfun_generic(object): @@ -235,7 +233,7 @@ def lfun_character(chi): sage: chi = DirichletGroup(6).gen().primitive_character() sage: L = LFunction(lfun_character(chi)) sage: L(3) - 1.20205690315959 + 0.884023811750080 TESTS: @@ -243,7 +241,7 @@ def lfun_character(chi): sage: L = LFunction(lfun_character(DirichletGroup(6).gen())) sage: L(4) - 1.08232323371114 + 0.940025680877124 With complex arguments:: @@ -251,34 +249,21 @@ def lfun_character(chi): sage: chi = DirichletGroup(6, CC).gen().primitive_character() sage: L = LFunction(lfun_character(chi)) sage: L(3) - 1.20205690315959 + 0.884023811750080 + + Check the values:: + + sage: chi = DirichletGroup(24)([1,-1,-1]); chi + Dirichlet character modulo 24 of conductor 24 + mapping 7 |--> 1, 13 |--> -1, 17 |--> -1 + sage: Lchi = lfun_character(chi) + sage: v = [0] + Lchi.lfunan(30).sage() + sage: all(v[i] == chi(i) for i in (7,13,17)) + True """ if not chi.is_primitive(): chi = chi.primitive_character() - - conductor = chi.conductor() - G = pari.znstar(conductor, 1) - - pari_orders = [pari(o) for o in G[2][1]] - pari_gens = IntegerModRing(conductor).unit_gens(algorithm="pari") - # should coincide with G[2][2] - - values_on_gens = (chi(x) for x in pari_gens) - - # now compute the input for pari (list of exponents) - P = chi.parent() - if is_ComplexField(P.base_ring()): - zeta = P.zeta() - zeta_argument = zeta.argument() - v = [int(x.argument() / zeta_argument) - for x in values_on_gens] - else: - dlog = P._zeta_dlog - v = [dlog[x] for x in values_on_gens] - - m = P.zeta_order() - v = [(vi * oi) // m for vi, oi in zip(v, pari_orders)] - + G, v = chi._pari_conversion() return pari.lfuncreate([G, v]) @@ -422,7 +407,7 @@ class LFunction(SageObject): 50 sage: L.taylor_series(1,4) 0.000000000000000 + 0.305999773834052*z + 0.186547797268162*z^2 - 0.136791463097188*z^3 + O(z^4) - sage: L.check_functional_equation() + sage: L.check_functional_equation() # abs tol 4e-19 1.08420217248550e-19 .. RUBRIC:: Rank 2 elliptic curve diff --git a/src/sage/lfunctions/sympow.py b/src/sage/lfunctions/sympow.py index ab697904118..92cb01fd733 100644 --- a/src/sage/lfunctions/sympow.py +++ b/src/sage/lfunctions/sympow.py @@ -51,7 +51,8 @@ import os from sage.structure.sage_object import SageObject -from sage.misc.all import pager, verbose +from sage.misc.all import pager +from sage.misc.verbose import verbose import sage.rings.all diff --git a/src/sage/lfunctions/zero_sums.pyx b/src/sage/lfunctions/zero_sums.pyx index 1c52870fe71..1bf9c406125 100644 --- a/src/sage/lfunctions/zero_sums.pyx +++ b/src/sage/lfunctions/zero_sums.pyx @@ -28,7 +28,7 @@ from sage.functions.log import log, exp from sage.functions.other import real, imag from sage.symbolic.constants import pi, euler_gamma from sage.libs.pari.all import pari -from sage.misc.all import verbose +from sage.misc.verbose import verbose from sage.parallel.decorate import parallel from sage.parallel.ncpus import ncpus as num_cpus from sage.libs.flint.ulong_extras cimport n_is_prime @@ -1622,7 +1622,7 @@ cdef class LFunctionZeroSum_EllipticCurve(LFunctionZeroSum_abstract): :func:`LFunctionZeroSum` :meth:`EllipticCurve.root_number` - :func:`set_verbose` + :func:`~sage.misc.verbose.set_verbose` EXAMPLES: diff --git a/src/sage/libs/arb/arith.pyx b/src/sage/libs/arb/arith.pyx index dba1a4fc461..31db27d6bb6 100644 --- a/src/sage/libs/arb/arith.pyx +++ b/src/sage/libs/arb/arith.pyx @@ -46,7 +46,7 @@ def bernoulli(n): sage: bernoulli(-1) Traceback (most recent call last): ... - OverflowError: can't convert negative value to mp_limb_t + OverflowError: can...t convert negative value to mp_limb_t """ cdef ulong i = n cdef Rational q = Rational.__new__(Rational) diff --git a/src/sage/libs/braiding.pyx b/src/sage/libs/braiding.pyx index ce6d49c5c10..3eebbdb2a91 100644 --- a/src/sage/libs/braiding.pyx +++ b/src/sage/libs/braiding.pyx @@ -1,3 +1,5 @@ +# distutils: libraries = braiding +# distutils: language = c++ r""" Cython wrapper for the libbraiding library. diff --git a/src/sage/libs/coxeter3/coxeter.pyx b/src/sage/libs/coxeter3/coxeter.pyx index b6fbfccab4f..59614f465ec 100644 --- a/src/sage/libs/coxeter3/coxeter.pyx +++ b/src/sage/libs/coxeter3/coxeter.pyx @@ -1,4 +1,8 @@ # -*- coding: utf-8 -*- +# distutils: language = c++ +# distutils: libraries = coxeter3 +# sage_setup: distribution = sage-coxeter3 + """ Low level part of the interface to Fokko Ducloux's Coxeter 3 library @@ -241,21 +245,28 @@ cdef class CoxGroup(SageObject): Traceback (most recent call last): ... NotImplementedError: Coxeter group of type ['A',0] using Coxeter 3 not yet implemented + + Successfully initializes from a relabeled Cartan type:: + + sage: ctype = CartanType(['B', 3]).relabel({1: 3, 2: 2, 3: 1}) + sage: W = CoxGroup(ctype) # optional - coxeter3 + sage: CoxeterMatrix(W.coxeter_matrix(), ctype.index_set()) == CoxeterMatrix(ctype) # optional - coxeter3 + True """ from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.coxeter_matrix import CoxeterMatrix self.cartan_type = CartanType(cartan_type) ordering = self._ordering_from_cartan_type(self.cartan_type) - if len(cartan_type) == 2: - type, rank = cartan_type - else: - type, rank, affine = cartan_type - if affine != 1: - raise NotImplementedError - + type, rank = self.cartan_type.type(), self.cartan_type.rank() + if self.cartan_type.is_affine(): + # Only untwisted affine groups are supported + try: + if not self.cartan_type.is_untwisted_affine(): + raise NotImplementedError('twisted affine groups are not supported in coxeter3') + except AttributeError: + pass type = type.lower() - rank = rank + 1 type = 'B' if type == 'C' else type @@ -267,6 +278,16 @@ cdef class CoxGroup(SageObject): self.out_ordering = {i+1: o for i,o in enumerate(ordering)} self.in_ordering = {self.out_ordering[a]: a for a in self.out_ordering} + # If the Cartan type supplied is relabeled, compose these orderings + # with the relabelling on the appropriate sides: + if hasattr(self.cartan_type, '_relabelling'): + r = self.cartan_type._relabelling + r_inv = {v: k for (k, v) in r.items()} + # Pre-compose in_ordering with r + self.in_ordering = {i: self.in_ordering[r[i]] for i in self.in_ordering} + # Post-compose out_ordering with r inverse + self.out_ordering = {i: r_inv[self.out_ordering[i]] for i in self.out_ordering} + # Check that the Coxeter matrices match up. cox_mat = CoxeterMatrix(self.coxeter_matrix(), self.cartan_type.index_set()) if cox_mat != CoxeterMatrix(self.cartan_type): diff --git a/src/sage/libs/coxeter3/coxeter_group.py b/src/sage/libs/coxeter3/coxeter_group.py index a480e1243f2..b4118ca9592 100644 --- a/src/sage/libs/coxeter3/coxeter_group.py +++ b/src/sage/libs/coxeter3/coxeter_group.py @@ -8,7 +8,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** -from six import iteritems from sage.libs.coxeter3.coxeter import get_CoxGroup, CoxGroupElement from sage.misc.cachefunc import cached_method @@ -19,6 +18,8 @@ from sage.categories.all import CoxeterGroups from sage.structure.parent import Parent +from sage.combinat.root_system.coxeter_matrix import CoxeterMatrix + from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -32,6 +33,8 @@ def __classcall__(cls, cartan_type, *args, **options): sage: from sage.libs.coxeter3.coxeter_group import CoxeterGroup # optional - coxeter3 sage: CoxeterGroup(['B',2]) # optional - coxeter3 Coxeter group of type ['B', 2] implemented by Coxeter3 + sage: CoxeterGroup(CartanType(['B', 3]).relabel({1: 3, 2: 2, 3: 1})) # optional - coxeter3 + Coxeter group of type ['B', 3] relabelled by {1: 3, 2: 2, 3: 1} implemented by Coxeter3 """ from sage.combinat.all import CartanType @@ -213,13 +216,15 @@ def coxeter_matrix(self): EXAMPLES:: sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.coxeter_matrix() # optional - coxeter3 + sage: m = W.coxeter_matrix(); m # optional - coxeter3 [1 3 2] [3 1 3] [2 3 1] + sage: m.index_set() == W.index_set() # optional - coxeter3 + True """ - return self._coxgroup.coxeter_matrix() + return CoxeterMatrix(self._coxgroup.coxeter_matrix(), self.index_set()) def root_system(self): """ @@ -250,19 +255,21 @@ def _an_element_(self): return self.element_class(self, []) def m(self, i, j): - """ - Return the entry in the Coxeter matrix between the generator - with label ``i`` and the generator with label ``j``. + r""" + This is deprecated, use ``self.coxeter_matrix()[i,j]`` instead. - EXAMPLES:: + TESTS:: - sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 - sage: W.m(1,1) # optional - coxeter3 + sage: W = CoxeterGroup(['A', 3], implementation='coxeter3') # optional - coxeter3 + sage: W.m(1, 1) # optional - coxeter3 + doctest:warning...: + DeprecationWarning: the .m(i, j) method has been deprecated; use .coxeter_matrix()[i,j] instead. + See https://trac.sagemath.org/30237 for details. 1 - sage: W.m(1,0) # optional - coxeter3 - 2 """ - return self.coxeter_matrix()[i-1,j-1] + from sage.misc.superseded import deprecation + deprecation(30237, "the .m(i, j) method has been deprecated; use .coxeter_matrix()[i,j] instead.") + return self.coxeter_matrix()[i,j] def kazhdan_lusztig_polynomial(self, u, v, constant_term_one=True): r""" @@ -673,7 +680,7 @@ def action_on_rational_function(self, f): exponent = self.action(exponent) monomial = 1 - for s, c in iteritems(exponent.monomial_coefficients()): + for s, c in exponent.monomial_coefficients().items(): monomial *= Q_gens[basis_to_order[s]]**int(c) result += monomial diff --git a/src/sage/libs/coxeter3/decl.pxd b/src/sage/libs/coxeter3/decl.pxd index b8978ffbb0d..56002154226 100644 --- a/src/sage/libs/coxeter3/decl.pxd +++ b/src/sage/libs/coxeter3/decl.pxd @@ -8,17 +8,17 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -cdef extern from "globals.h": +cdef extern from "coxeter/globals.h": ctypedef unsigned long Ulong ######## # Bits # ######## -cdef extern from "bits.h": +cdef extern from "coxeter/bits.h": ctypedef Ulong LFlags -cdef extern from "coxtypes.h" namespace "coxtypes": +cdef extern from "coxeter/coxtypes.h" namespace "coxtypes": ctypedef unsigned short Rank ctypedef Ulong CoxSize # should hold at least 32 bits ctypedef Ulong BettiNbr # should hold at least 32 bits @@ -46,7 +46,7 @@ cdef extern from "coxtypes.h" namespace "coxtypes": ################ # String # ################ -cdef extern from "io.h" namespace "io": +cdef extern from "coxeter/io.h" namespace "io": cdef cppclass c_String "io::String": c_String() c_String(char* s) @@ -59,7 +59,7 @@ cdef extern from "io.h" namespace "io": ############## # Type # ############## -cdef extern from "type.h": +cdef extern from "coxeter/type.h": cdef cppclass c_Type "coxeter::Type": c_Type() c_Type(char* s) @@ -69,13 +69,13 @@ cdef extern from "type.h": ############## # Bits # ############## -cdef extern from "bits.h" namespace "bits": +cdef extern from "coxeter/bits.h" namespace "bits": Generator firstBit(Ulong n) ################## # CoxGraph # ################## -cdef extern from "graph.h" namespace "graph": +cdef extern from "coxeter/graph.h" namespace "graph": ctypedef unsigned short CoxEntry ctypedef struct c_CoxGraph "graph::CoxGraph": pass @@ -83,19 +83,19 @@ cdef extern from "graph.h" namespace "graph": ############### # KLPol # ############### -cdef extern from "kl.h" namespace "kl": +cdef extern from "coxeter/kl.h" namespace "kl": cdef cppclass c_KLPol "kl::KLPol": const unsigned short& operator[](Ulong j) unsigned long deg() int isZero() -cdef extern from "polynomials.h" namespace "polynomials": +cdef extern from "coxeter/polynomials.h" namespace "polynomials": c_String klpoly_append "polynomials::append"(c_String str, c_KLPol, char* x) ################## # List # ################## -cdef extern from "list.h" namespace "list": +cdef extern from "coxeter/list.h" namespace "list": cdef cppclass c_List_CoxWord "list::List ": c_List_CoxWord() c_List_CoxWord(Ulong len) @@ -106,7 +106,7 @@ cdef extern from "list.h" namespace "list": ################### # CoxGroup # ################### -cdef extern from "coxgroup.h": +cdef extern from "coxeter/coxgroup.h": cdef cppclass c_CoxGroup "coxeter::CoxGroup": c_CoxGroup() c_CoxGroup(c_Type t, Rank r) @@ -139,16 +139,16 @@ cdef extern from "coxgroup.h": ##################### # Interactive # ##################### -cdef extern from "interactive.h" namespace "interactive": +cdef extern from "coxeter/interactive.h" namespace "interactive": c_CoxGroup* coxeterGroup(c_Type x, Rank l) -cdef extern from "constants.h": +cdef extern from "coxeter/constants.h": void initConstants() ############################### # Finite Coxeter groups # ############################### -cdef extern from "fcoxgroup.h" namespace "fcoxgroup": +cdef extern from "coxeter/fcoxgroup.h" namespace "fcoxgroup": ctypedef struct c_FiniteCoxGroup "fcoxgroup::FiniteCoxGroup": void fullContext() bint isFullContext() @@ -161,5 +161,5 @@ cdef extern from "fcoxgroup.h" namespace "fcoxgroup": ###################### # Sage specific # ###################### -cdef extern from "sage.h" namespace "sage": +cdef extern from "coxeter/sage.h" namespace "sage": void interval(c_List_CoxWord& l, c_CoxGroup& W, c_CoxWord& g, c_CoxWord& h) diff --git a/src/sage/libs/ecl.pxd b/src/sage/libs/ecl.pxd index 4dd273feb6e..f92c561d5c9 100644 --- a/src/sage/libs/ecl.pxd +++ b/src/sage/libs/ecl.pxd @@ -30,7 +30,7 @@ cdef extern from "ecl/ecl.h": ctypedef long int cl_fixnum ctypedef cl_fixnum cl_narg ctypedef void *cl_object - ctypedef unsigned int cl_index + ctypedef unsigned long int cl_index ctypedef enum ecl_option: ECL_OPT_INCREMENTAL_GC = 0, @@ -39,7 +39,6 @@ cdef extern from "ecl/ecl.h": ECL_OPT_TRAP_SIGINT, ECL_OPT_TRAP_SIGILL, ECL_OPT_TRAP_SIGBUS, - ECL_OPT_TRAP_SIGCHLD, ECL_OPT_TRAP_SIGPIPE, ECL_OPT_TRAP_INTERRUPT_SIGNAL, ECL_OPT_SIGNAL_HANDLING_THREAD, @@ -53,7 +52,6 @@ cdef extern from "ecl/ecl.h": ECL_OPT_LISP_STACK_SAFETY_AREA, ECL_OPT_C_STACK_SIZE, ECL_OPT_C_STACK_SAFETY_AREA, - ECL_OPT_SIGALTSTACK_SIZE, ECL_OPT_HEAP_SIZE, ECL_OPT_HEAP_SAFETY_AREA, ECL_OPT_THREAD_INTERRUPT_SIGNAL, @@ -127,6 +125,7 @@ cdef extern from "ecl/ecl.h": cl_object cl_cddr(cl_object x) cl_object cl_rplaca(cl_object x, cl_object v) cl_object cl_rplacd(cl_object x, cl_object v) + cl_object ecl_list1(cl_object a) # string parsing and string IO @@ -136,6 +135,7 @@ cdef extern from "ecl/ecl.h": cl_object cl_write_to_string(cl_narg narg, cl_object o) cl_object ecl_cstring_to_base_string_or_nil(char *s) cl_object si_coerce_to_base_string(cl_object x) + cl_object si_base_string_p(cl_object x) # S-expr evaluation and function calls @@ -147,6 +147,10 @@ cdef extern from "ecl/ecl.h": int ecl_nvalues "NVALUES" cl_object ecl_values "VALUES"(int n) - #Common Lisp "EQUAL" compatible hash function + # Common Lisp "EQUAL" compatible hash function cl_object cl_sxhash(cl_object key) + + # symbols + + cl_object ecl_make_symbol(const char *name, const char *package) \ No newline at end of file diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index 1f05f4a5656..062adae193f 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -15,7 +15,7 @@ Library interface to Embeddable Common Lisp (ECL) #adapted to work with pure Python types. from libc.stdlib cimport abort -from libc.signal cimport SIGINT, SIGBUS, SIGSEGV, SIGCHLD +from libc.signal cimport SIGINT, SIGBUS, SIGFPE, SIGSEGV from libc.signal cimport raise_ as signal_raise from posix.signal cimport sigaction, sigaction_t cimport cysignals.signals @@ -42,14 +42,24 @@ cdef bint bint_integerp(cl_object obj): cdef bint bint_rationalp(cl_object obj): return not(cl_rationalp(obj) == Cnil) +cdef bint bint_base_string_p(cl_object obj): + return not(si_base_string_p(obj) == Cnil) + cdef extern from "eclsig.h": int ecl_sig_on() except 0 void ecl_sig_off() cdef sigaction_t ecl_sigint_handler cdef sigaction_t ecl_sigbus_handler + cdef sigaction_t ecl_sigfpe_handler cdef sigaction_t ecl_sigsegv_handler cdef mpz_t ecl_mpz_from_bignum(cl_object obj) cdef cl_object ecl_bignum_from_mpz(mpz_t num) + cdef cl_object conditions_to_handle_clobj + void safe_cl_boot(int argc, char** argv) + cl_object safe_cl_funcall(cl_object *error, cl_object fun, cl_object arg) + cl_object safe_cl_apply(cl_object *error, cl_object fun, cl_object args) + cl_object safe_cl_eval(cl_object *error, cl_object form) + cdef cl_object string_to_object(char * s): return ecl_read_from_cstring(s) @@ -93,10 +103,9 @@ cdef void remove_node(cl_object node): cdef cl_object list_of_objects -cdef cl_object safe_eval_clobj #our own error catching eval -cdef cl_object safe_apply_clobj #our own error catching apply -cdef cl_object safe_funcall_clobj #our own error catching funcall cdef cl_object read_from_string_clobj #our own error catching reader +cdef cl_object make_unicode_string_clobj +cdef cl_object unicode_string_codepoints_clobj cdef bint ecl_has_booted = 0 @@ -139,7 +148,6 @@ def test_ecl_options(): ECL_OPT_TRAP_SIGINT = 1 ECL_OPT_TRAP_SIGILL = 1 ECL_OPT_TRAP_SIGBUS = 1 - ECL_OPT_TRAP_SIGCHLD = 0 ECL_OPT_TRAP_SIGPIPE = 1 ECL_OPT_TRAP_INTERRUPT_SIGNAL = 1 ECL_OPT_SIGNAL_HANDLING_THREAD = 0 @@ -153,7 +161,6 @@ def test_ecl_options(): ECL_OPT_LISP_STACK_SAFETY_AREA = ... ECL_OPT_C_STACK_SIZE = ... ECL_OPT_C_STACK_SAFETY_AREA = ... - ECL_OPT_SIGALTSTACK_SIZE = 1 ECL_OPT_HEAP_SIZE = ... ECL_OPT_HEAP_SAFETY_AREA = ... ECL_OPT_THREAD_INTERRUPT_SIGNAL = ... @@ -171,8 +178,6 @@ def test_ecl_options(): ecl_get_option(ECL_OPT_TRAP_SIGILL))) print('ECL_OPT_TRAP_SIGBUS = {0}'.format( ecl_get_option(ECL_OPT_TRAP_SIGBUS))) - print('ECL_OPT_TRAP_SIGCHLD = {0}'.format( - ecl_get_option(ECL_OPT_TRAP_SIGCHLD))) print('ECL_OPT_TRAP_SIGPIPE = {0}'.format( ecl_get_option(ECL_OPT_TRAP_SIGPIPE))) print('ECL_OPT_TRAP_INTERRUPT_SIGNAL = {0}'.format( @@ -199,8 +204,6 @@ def test_ecl_options(): ecl_get_option(ECL_OPT_C_STACK_SIZE))) print('ECL_OPT_C_STACK_SAFETY_AREA = {0}'.format( ecl_get_option(ECL_OPT_C_STACK_SAFETY_AREA))) - print('ECL_OPT_SIGALTSTACK_SIZE = {0}'.format( - ecl_get_option(ECL_OPT_SIGALTSTACK_SIZE))) print('ECL_OPT_HEAP_SIZE = {0}'.format( ecl_get_option(ECL_OPT_HEAP_SIZE))) print('ECL_OPT_HEAP_SAFETY_AREA = {0}'.format( @@ -231,10 +234,10 @@ def init_ecl(): RuntimeError: ECL is already initialized """ global list_of_objects - global safe_eval_clobj - global safe_apply_clobj - global safe_funcall_clobj global read_from_string_clobj + global make_unicode_string_clobj + global unicode_string_codepoints_clobj + global conditions_to_handle_clobj global ecl_has_booted cdef char *argv[1] cdef sigaction_t sage_action[32] @@ -243,9 +246,6 @@ def init_ecl(): if ecl_has_booted: raise RuntimeError("ECL is already initialized") - # we need it to stop handling SIGCHLD - ecl_set_option(ECL_OPT_TRAP_SIGCHLD, 0); - #we keep our own GMP memory functions. ECL should not claim them ecl_set_option(ECL_OPT_SET_GMP_MEMORY_FUNCTIONS,0); @@ -259,19 +259,14 @@ def init_ecl(): #initialize ECL ecl_set_option(ECL_OPT_SIGNAL_HANDLING_THREAD, 0) - cl_boot(1, argv) + safe_cl_boot(1, argv) #save signal handler from ECL sigaction(SIGINT, NULL, &ecl_sigint_handler) sigaction(SIGBUS, NULL, &ecl_sigbus_handler) + sigaction(SIGFPE, NULL, &ecl_sigfpe_handler) sigaction(SIGSEGV, NULL, &ecl_sigsegv_handler) - #verify that no SIGCHLD handler was installed - cdef sigaction_t sig_test - sigaction(SIGCHLD, NULL, &sig_test) - assert sage_action[SIGCHLD].sa_handler == NULL # Sage does not set SIGCHLD handler - assert sig_test.sa_handler == NULL # And ECL bootup did not set one - #and put the Sage signal handlers back for i in range(1,32): sigaction(i, &sage_action[i], NULL) @@ -293,35 +288,30 @@ def init_ecl(): read_from_string_clobj=cl_eval(string_to_object(b"(symbol-function 'read-from-string)")) - cl_eval(string_to_object(b""" - (defun sage-safe-eval (form) - (handler-case - (values (eval form)) - (serious-condition (cnd) - (values nil (princ-to-string cnd))))) - """)) - safe_eval_clobj=cl_eval(string_to_object(b"(symbol-function 'sage-safe-eval)")) + conditions_to_handle_clobj=ecl_list1(ecl_make_symbol(b"SERIOUS-CONDITION", b"COMMON-LISP")) + insert_node_after(list_of_objects,conditions_to_handle_clobj) cl_eval(string_to_object(b""" - (defun sage-safe-apply (func args) - (handler-case - (values (apply func args)) - (serious-condition (cnd) - (values nil (princ-to-string cnd))))) + (defun sage-make-unicode-string (codepoints) + (map 'string #'code-char codepoints)) """)) + make_unicode_string_clobj = cl_eval(string_to_object(b"#'sage-make-unicode-string")) - safe_apply_clobj=cl_eval(string_to_object(b"(symbol-function 'sage-safe-apply)")) cl_eval(string_to_object(b""" - (defun sage-safe-funcall (func arg) - (handler-case - (values (funcall func arg)) - (serious-condition (cnd) - (values nil (princ-to-string cnd))))) + (defun sage-unicode-string-codepoints (s) + (map 'list #'char-code s)) """)) - safe_funcall_clobj=cl_eval(string_to_object(b"(symbol-function 'sage-safe-funcall)")) + unicode_string_codepoints_clobj = cl_eval(string_to_object(b"#'sage-unicode-string-codepoints")) ecl_has_booted = 1 +cdef ecl_string_to_python(cl_object s): + if bint_base_string_p(s): + return char_to_str(ecl_base_string_pointer_safe(s)) + else: + s = cl_funcall(2, unicode_string_codepoints_clobj, s) + return ''.join(chr(code) for code in ecl_to_python(s)) + cdef cl_object ecl_safe_eval(cl_object form) except NULL: """ TESTS: @@ -339,45 +329,43 @@ cdef cl_object ecl_safe_eval(cl_object form) except NULL: ... RuntimeError: ECL says: Console interrupt. """ - cdef cl_object s + cdef cl_object ret, error = NULL + ecl_sig_on() - cl_funcall(2,safe_eval_clobj,form) + ret = safe_cl_eval(&error,form) ecl_sig_off() - if ecl_nvalues > 1: - s = si_coerce_to_base_string(ecl_values(1)) + if error != NULL: raise RuntimeError("ECL says: {}".format( - char_to_str(ecl_base_string_pointer_safe(s)))) + ecl_string_to_python(error))) else: - return ecl_values(0) + return ret cdef cl_object ecl_safe_funcall(cl_object func, cl_object arg) except NULL: - cdef cl_object l, s - l = cl_cons(func,cl_cons(arg,Cnil)); + cdef cl_object ret, error = NULL ecl_sig_on() - cl_apply(2,safe_funcall_clobj,cl_cons(func,cl_cons(arg,Cnil))) + ret = safe_cl_funcall(&error,func,arg) ecl_sig_off() - if ecl_nvalues > 1: - s = si_coerce_to_base_string(ecl_values(1)) + if error != NULL: raise RuntimeError("ECL says: {}".format( - char_to_str(ecl_base_string_pointer_safe(s)))) + ecl_string_to_python(error))) else: - return ecl_values(0) + return ret cdef cl_object ecl_safe_apply(cl_object func, cl_object args) except NULL: - cdef cl_object s + cdef cl_object ret, error = NULL + ecl_sig_on() - cl_funcall(3,safe_apply_clobj,func,args) + ret = safe_cl_apply(&error,func,args) ecl_sig_off() - if ecl_nvalues > 1: - s = si_coerce_to_base_string(ecl_values(1)) + if error != NULL: raise RuntimeError("ECL says: {}".format( - char_to_str(ecl_base_string_pointer_safe(s)))) + ecl_string_to_python(error))) else: - return ecl_values(0) + return ret cdef cl_object ecl_safe_read_string(char * s) except NULL: cdef cl_object o @@ -428,21 +416,24 @@ def print_objects(): cdef cl_object c, s c = list_of_objects while True: - s = si_coerce_to_base_string(cl_write_to_string(1,cl_car(c))) - print(char_to_str(ecl_base_string_pointer_safe(s))) + + s = cl_write_to_string(1, cl_car(c)) + print(ecl_string_to_python(s)) + c = cl_cadr(c) if c == Cnil: break -cdef cl_object python_to_ecl(pyobj) except NULL: +cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: # conversion of a python object into an ecl object # most conversions are straightforward. Noteworthy are: # python lists -> lisp (NIL terminated) lists # tuples -> dotted lists - # strings ->parsed by lisp reader + # strings -> if read_strings is true, parsed by lisp reader + # otherwise creates a simple-string cdef bytes s - cdef cl_object L, ptr + cdef cl_object L, ptr, o if isinstance(pyobj,bool): if pyobj: @@ -455,17 +446,30 @@ cdef cl_object python_to_ecl(pyobj) except NULL: if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM: return ecl_make_integer(pyobj) else: - return python_to_ecl(Integer(pyobj)) + return python_to_ecl(Integer(pyobj), read_strings) elif isinstance(pyobj,int): return ecl_make_integer(pyobj) elif isinstance(pyobj,float): return ecl_make_doublefloat(pyobj) elif isinstance(pyobj,unicode): - s=str_to_bytes(pyobj) - return ecl_safe_read_string(s) + try: + s = str_to_bytes(pyobj, 'ascii') + except UnicodeEncodeError: + o = cl_funcall(2, make_unicode_string_clobj, + python_to_ecl([ord(c) for c in pyobj], read_strings)) + else: + o = ecl_cstring_to_base_string_or_nil(s) + + if read_strings: + return ecl_safe_funcall(read_from_string_clobj, o) + else: + return o elif isinstance(pyobj,bytes): s=pyobj - return ecl_safe_read_string(s) + if read_strings: + return ecl_safe_read_string(s) + else: + return ecl_cstring_to_base_string_or_nil(s) elif isinstance(pyobj,Integer): if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM: return ecl_make_integer(pyobj) @@ -473,32 +477,32 @@ cdef cl_object python_to_ecl(pyobj) except NULL: return ecl_bignum_from_mpz( (pyobj).value ) elif isinstance(pyobj,Rational): return ecl_make_ratio( - python_to_ecl( (pyobj).numerator() ), - python_to_ecl( (pyobj).denominator())) + python_to_ecl( (pyobj).numerator(), read_strings ), + python_to_ecl( (pyobj).denominator(), read_strings )) elif isinstance(pyobj,EclObject): return (pyobj).obj elif isinstance(pyobj, list): if not pyobj: return Cnil else: - L=cl_cons(python_to_ecl(pyobj[0]),Cnil) - ptr=L + L = cl_cons(python_to_ecl(pyobj[0], read_strings),Cnil) + ptr = L for a in pyobj[1:]: - cl_rplacd(ptr,cl_cons(python_to_ecl(a),Cnil)) - ptr=cl_cdr(ptr) + cl_rplacd(ptr, cl_cons(python_to_ecl(a, read_strings), Cnil)) + ptr = cl_cdr(ptr) return L elif isinstance(pyobj, tuple): if not pyobj: return Cnil elif len(pyobj) == 1: - return python_to_ecl(pyobj[0]) + return python_to_ecl(pyobj[0], read_strings) else: - L=cl_cons(python_to_ecl(pyobj[0]),Cnil) - ptr=L + L = cl_cons(python_to_ecl(pyobj[0], read_strings), Cnil) + ptr = L for a in pyobj[1:-1]: - cl_rplacd(ptr,cl_cons(python_to_ecl(a),Cnil)) - ptr=cl_cdr(ptr) - cl_rplacd(ptr,python_to_ecl(pyobj[-1])) + cl_rplacd(ptr, cl_cons(python_to_ecl(a, read_strings), Cnil)) + ptr = cl_cdr(ptr) + cl_rplacd(ptr, python_to_ecl(pyobj[-1], read_strings)) return L else: raise TypeError("Unimplemented type for python_to_ecl") @@ -539,8 +543,8 @@ cdef ecl_to_python(cl_object o): return tuple(L) return L else: - s = si_coerce_to_base_string(cl_write_to_string(1,o)) - return char_to_str(ecl_base_string_pointer_safe(s)) + s = cl_write_to_string(1, o) + return ecl_string_to_python(s) #Maxima's BFLOAT multiprecision float type can be read with: #def bfloat_to_python(e): @@ -595,6 +599,8 @@ cdef class EclObject: sage: EclObject( (false, true)) + sage: EclObject( (1, 2, 3) ) + Strings are fed to the reader, so a string normally results in a symbol:: @@ -606,6 +612,28 @@ cdef class EclObject: sage: EclObject('"Symbol"') + Or any other object that the Lisp reader can construct:: + + sage: EclObject('#("I" am "just" a "simple" vector)') + + + By means of Lisp reader macros, you can include arbitrary objects:: + + sage: EclObject([ 1, 2, '''#.(make-hash-table :test #'equal)''', 4]) + 4)> + + Using an optional argument, you can control how strings are handled:: + + sage: EclObject("String", False) + + sage: EclObject('#(I may look like a vector but I am a string)', False) + + + This also affects strings within nested lists and tuples :: + + sage: EclObject([1, 2, "String", 4], False) + + EclObjects translate to themselves, so one can mix:: sage: EclObject([1,2,EclObject([3])]) @@ -640,6 +668,19 @@ cdef class EclObject: True sage: EclObject(-i).python() == -i True + + We check that symbols with Unicode names are converted correctly:: + + sage: EclObject('λ') + + sage: EclObject('|λ|') + + + We check that Unicode strings are converted correctly:: + + sage: EclObject('"Mαξιμα"') + + """ cdef cl_object obj #the wrapped object cdef cl_object node #linked list pointer: car(node) == obj @@ -652,7 +693,7 @@ cdef class EclObject: if not(bint_fixnump(o) or bint_characterp(o) or bint_nullp(o)): self.node=insert_node_after(list_of_objects,o) - def __init__(self,*args): + def __init__(self, *args): r""" Create an EclObject @@ -665,8 +706,14 @@ cdef class EclObject: """ - if len(args) != 0: - self.set_obj(python_to_ecl(args[0])) + if not args: + return + elif len(args) == 1: + self.set_obj(python_to_ecl(args[0], True)) + elif len(args) == 2: + self.set_obj(python_to_ecl(args[0], args[1])) + else: + raise TypeError('EclObject.__init__ received a wrong number of arguments') def __reduce__(self): r""" @@ -756,8 +803,8 @@ cdef class EclObject: """ cdef cl_object s - s = si_coerce_to_base_string(cl_write_to_string(1,self.obj)) - return char_to_str(ecl_base_string_pointer_safe(s)) + s = cl_write_to_string(1, self.obj) + return ecl_string_to_python(s) def __hash__(self): r""" @@ -1323,7 +1370,7 @@ cdef EclObject ecl_wrap(cl_object o): #convenience routine to more easily evaluate strings cpdef EclObject ecl_eval(str s): - """ + r""" Read and evaluate string in Lisp and return the result EXAMPLES:: @@ -1334,10 +1381,18 @@ cpdef EclObject ecl_eval(str s): sage: ecl_eval("(mapcar 'fibo '(1 2 3 4 5 6 7))") + TESTS: + + We check that Unicode is handled correctly:: + + sage: ecl_eval('''(defun double-struck-number (n) (map 'string #'(lambda (c) (code-char (+ (char-code #\𝟘) (- (char-code c) (char-code #\\0))))) (format nil "~A" n)))''') + + sage: _(4711) + + """ cdef cl_object o - o=ecl_safe_read_string(str_to_bytes(s)) - o=ecl_safe_eval(o) + o=ecl_safe_eval(python_to_ecl(s, True)) return ecl_wrap(o) init_ecl() diff --git a/src/sage/libs/eclib/mat.pyx b/src/sage/libs/eclib/mat.pyx index b061982110e..9cd06f9b344 100644 --- a/src/sage/libs/eclib/mat.pyx +++ b/src/sage/libs/eclib/mat.pyx @@ -131,7 +131,8 @@ cdef class Matrix: ## """ ## Return the rank of this matrix. -## EXAMPLES: +## EXAMPLES:: +## ## sage: M = CremonaModularSymbols(389) ## sage: t = M.hecke_matrix(2) ## sage: t.rank() diff --git a/src/sage/libs/eclsig.h b/src/sage/libs/eclsig.h index f9f2690d182..e249ccf6874 100644 --- a/src/sage/libs/eclsig.h +++ b/src/sage/libs/eclsig.h @@ -11,15 +11,18 @@ #include static struct sigaction ecl_sigint_handler; static struct sigaction ecl_sigbus_handler; +static struct sigaction ecl_sigfpe_handler; static struct sigaction ecl_sigsegv_handler; static struct sigaction sage_sigint_handler; static struct sigaction sage_sigbus_handler; +static struct sigaction sage_sigfpe_handler; static struct sigaction sage_sigsegv_handler; static inline void set_ecl_signal_handler(void) { sigaction(SIGINT, &ecl_sigint_handler, &sage_sigint_handler); sigaction(SIGBUS, &ecl_sigbus_handler, &sage_sigbus_handler); + sigaction(SIGFPE, &ecl_sigfpe_handler, &sage_sigfpe_handler); sigaction(SIGSEGV, &ecl_sigsegv_handler, &sage_sigsegv_handler); } @@ -27,6 +30,7 @@ static inline void unset_ecl_signal_handler(void) { sigaction(SIGINT, &sage_sigint_handler, NULL); sigaction(SIGBUS, &sage_sigbus_handler, NULL); + sigaction(SIGFPE, &sage_sigfpe_handler, NULL); sigaction(SIGSEGV, &sage_sigsegv_handler, NULL); } @@ -49,3 +53,52 @@ cl_object ecl_bignum_from_mpz(mpz_t num) mpz_set(ecl_mpz_from_bignum(z), num); return _ecl_big_register_copy(z); } + +static inline void safe_cl_boot(int argc, char** argv) { + ECL_WITH_LISP_FPE_BEGIN { + cl_boot(argc, argv); + } ECL_WITH_LISP_FPE_END; +} + +/* List of conditions to catch in the following functions. Is + * initialized after cl_boot in init_ecl. */ +static cl_object conditions_to_handle_clobj = ECL_NIL; + +static inline cl_object safe_cl_funcall(cl_object *error, cl_object fun, cl_object arg) { + cl_object ret = NULL; + cl_env_ptr the_env = ecl_process_env(); + ECL_WITH_LISP_FPE_BEGIN { + ECL_HANDLER_CASE_BEGIN(the_env, conditions_to_handle_clobj) { + ret = cl_funcall(2, fun, arg); + } ECL_HANDLER_CASE(1, condition) { + *error = cl_princ_to_string(condition); + } ECL_HANDLER_CASE_END; + } ECL_WITH_LISP_FPE_END; + return ret; +} + +static inline cl_object safe_cl_apply(cl_object *error, cl_object fun, cl_object args) { + cl_object ret = NULL; + cl_env_ptr the_env = ecl_process_env(); + ECL_WITH_LISP_FPE_BEGIN { + ECL_HANDLER_CASE_BEGIN(the_env, conditions_to_handle_clobj) { + ret = cl_apply(2, fun, args); + } ECL_HANDLER_CASE(1, condition) { + *error = cl_princ_to_string(condition); + } ECL_HANDLER_CASE_END; + } ECL_WITH_LISP_FPE_END; + return ret; +} + +static inline cl_object safe_cl_eval(cl_object *error, cl_object form) { + cl_object ret = NULL; + cl_env_ptr the_env = ecl_process_env(); + ECL_WITH_LISP_FPE_BEGIN { + ECL_HANDLER_CASE_BEGIN(the_env, conditions_to_handle_clobj) { + ret = cl_eval(form); + } ECL_HANDLER_CASE(1, condition) { + *error = cl_princ_to_string(condition); + } ECL_HANDLER_CASE_END; + } ECL_WITH_LISP_FPE_END; + return ret; +} diff --git a/src/sage/libs/fes.pyx b/src/sage/libs/fes.pyx index 3cc2e35c8c0..06a8db7f62e 100644 --- a/src/sage/libs/fes.pyx +++ b/src/sage/libs/fes.pyx @@ -1,3 +1,6 @@ +# distutils: language = c +# distutils: libraries = fes +# sage_setup: distribution = sage-fes """ Binding for the FES library diff --git a/src/sage/libs/flint/arith.pyx b/src/sage/libs/flint/arith.pyx index 946371b6294..4a378ce9862 100644 --- a/src/sage/libs/flint/arith.pyx +++ b/src/sage/libs/flint/arith.pyx @@ -1,3 +1,4 @@ +# distutils: extra_compile_args = -D_XPG6 """ FLINT Arithmetic Functions """ diff --git a/src/sage/libs/flint/flint.pyx b/src/sage/libs/flint/flint.pyx index 09b0693ff51..ad645bdf2d5 100644 --- a/src/sage/libs/flint/flint.pyx +++ b/src/sage/libs/flint/flint.pyx @@ -1,3 +1,4 @@ +# distutils: extra_compile_args = -D_XPG6 """ Flint imports diff --git a/src/sage/libs/flint/flint_ntl_wrap.h b/src/sage/libs/flint/flint_ntl_wrap.h index 058d13b2e92..841d990817d 100644 --- a/src/sage/libs/flint/flint_ntl_wrap.h +++ b/src/sage/libs/flint/flint_ntl_wrap.h @@ -15,7 +15,7 @@ #include /* If flint was already previously included via another header (e.g. - * arb_wrap.h) then it may be neessary to redefine ulong and slong again */ + * arb_wrap.h) then it may be necessary to redefine ulong and slong again */ #ifndef ulong #define ulong mp_limb_t diff --git a/src/sage/libs/flint/flint_wrap.h b/src/sage/libs/flint/flint_wrap.h index 179597d2d56..9c0ca4e88b1 100644 --- a/src/sage/libs/flint/flint_wrap.h +++ b/src/sage/libs/flint/flint_wrap.h @@ -24,7 +24,7 @@ #include /* If flint was already previously included via another header (e.g. - * arb_wrap.h) then it may be neessary to redefine ulong and slong again */ + * arb_wrap.h) then it may be necessary to redefine ulong and slong again */ #ifndef ulong #define ulong mp_limb_t @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/src/sage/libs/flint/fmpz_poly.pyx b/src/sage/libs/flint/fmpz_poly.pyx index 0772779d16c..97338c79cad 100644 --- a/src/sage/libs/flint/fmpz_poly.pyx +++ b/src/sage/libs/flint/fmpz_poly.pyx @@ -1,3 +1,4 @@ +# distutils: extra_compile_args = -D_XPG6 """ FLINT fmpz_poly class wrapper diff --git a/src/sage/libs/flint/fmpz_poly_mat.pxd b/src/sage/libs/flint/fmpz_poly_mat.pxd new file mode 100644 index 00000000000..d045c745de3 --- /dev/null +++ b/src/sage/libs/flint/fmpz_poly_mat.pxd @@ -0,0 +1,27 @@ +# distutils: libraries = flint +# distutils: depends = flint/fmpz_poly_mat.h + +from sage.libs.flint.types cimport fmpz_poly_mat_t, fmpz_poly_t, fmpz_t, slong, fmpz_mat_t + +# flint/fmpz_poly_mat.h +cdef extern from "flint_wrap.h": + + void fmpz_poly_mat_init(fmpz_poly_mat_t mat, slong rows, slong cols) + void fmpz_poly_mat_init_set(fmpz_poly_mat_t mat, const fmpz_poly_mat_t src) + void fmpz_poly_mat_clear(fmpz_poly_mat_t mat) + fmpz_poly_t fmpz_poly_mat_entry(fmpz_poly_mat_t mat, long i, long j) + slong fmpz_poly_mat_nrows(const fmpz_poly_mat_t mat) + slong fmpz_poly_mat_ncols(const fmpz_poly_mat_t mat) + + void fmpz_poly_mat_set(fmpz_poly_mat_t mat1, const fmpz_poly_mat_t mat2) + + void fmpz_poly_mat_swap(fmpz_poly_mat_t mat1, fmpz_poly_mat_t mat2) + + void fmpz_poly_mat_transpose(fmpz_poly_mat_t B, const fmpz_poly_mat_t A) + + void fmpz_poly_mat_evaluate_fmpz(fmpz_mat_t B, const fmpz_poly_mat_t A, + const fmpz_t x) + + void fmpz_poly_mat_trace(fmpz_poly_t trace, const fmpz_poly_mat_t mat) + + void fmpz_poly_mat_det(fmpz_poly_t det, const fmpz_poly_mat_t A) diff --git a/src/sage/libs/flint/types.pxd b/src/sage/libs/flint/types.pxd index 92173a78156..3e0d595ee9f 100644 --- a/src/sage/libs/flint/types.pxd +++ b/src/sage/libs/flint/types.pxd @@ -79,6 +79,13 @@ cdef extern from "flint_wrap.h": ctypedef fmpq_mat_struct fmpq_mat_t[1] +# flint/fmpz_poly_mat.h: +cdef extern from "flint_wrap.h": + ctypedef struct fmpz_poly_mat_struct: + pass + + ctypedef fmpz_poly_mat_struct fmpz_poly_mat_t[1] + # flint/fmpz_mod_poly.h: cdef extern from "flint_wrap.h": ctypedef struct fmpz_mod_poly_struct: diff --git a/src/sage/libs/gap/assigned_names.py b/src/sage/libs/gap/assigned_names.py index 46ea8542bd2..e8d1f1707cc 100644 --- a/src/sage/libs/gap/assigned_names.py +++ b/src/sage/libs/gap/assigned_names.py @@ -21,7 +21,8 @@ # http://www.gnu.org/licenses/ ############################################################################### -from six.moves import cPickle +import pickle + from sage.libs.gap.libgap import libgap from sage.libs.gap.saved_workspace import workspace @@ -60,12 +61,12 @@ def load_or_compute(name, function): filename, up_to_date = workspace(name=name) if up_to_date: with open(filename, 'rb') as f: - return cPickle.load(f) + return pickle.load(f) else: value = function() from sage.misc.temporary_file import atomic_write with atomic_write(filename, binary=True) as f: - cPickle.dump(value, f) + pickle.dump(value, f) return value diff --git a/src/sage/libs/glpk/error.pyx b/src/sage/libs/glpk/error.pyx index f7046b3ec45..93d600ff6da 100644 --- a/src/sage/libs/glpk/error.pyx +++ b/src/sage/libs/glpk/error.pyx @@ -21,7 +21,10 @@ from sage.numerical.mip import MIPSolverException class GLPKError(MIPSolverException): """ - An error raised by the GLPK library. + A low-level error that is raised by ``sage_glpk_term_hook``. + + The GLPK API considers these errors non-recoverable. User code should not try + to catch this exception. EXAMPLES:: @@ -68,9 +71,17 @@ def setup_glpk_error_handler(): Setup the GLPK error handler. Called automatically when this module is imported at Sage startup. + We install this error handler so that an error does not lead to + an immediate error exit of the process. Instead, we raise a + ``GLPKError`` for the convenience of developers. + + The GLPK API considers errors non-recoverable. + Therefore, user code should not try to catch this exception. + TESTS:: - sage: cython(''' + sage: cython( # optional - glpk_error_recovery_patch + ....: ''' ....: # distutils: libraries = glpk z gmp ....: from cysignals.signals cimport sig_on, sig_off ....: from sage.libs.glpk.env cimport glp_term_out @@ -97,8 +108,8 @@ def setup_glpk_error_handler(): sage: p.add_constraint(3*x + 2*y <= 6) sage: p.add_constraint(x >= 0) sage: p.set_objective(x + y) - sage: res = p.solve() - 0: obj = ... + sage: print('output', flush=True); res = p.solve() + output ... 0: obj = ... sage: res # rel tol 1e-15 2.4 """ diff --git a/src/sage/libs/glpk/lp.pxd b/src/sage/libs/glpk/lp.pxd index 1911fb13561..cc4f05e5368 100644 --- a/src/sage/libs/glpk/lp.pxd +++ b/src/sage/libs/glpk/lp.pxd @@ -90,3 +90,5 @@ cdef extern from "glpk.h": double glp_ios_mip_gap(glp_tree *T) int glp_ios_best_node(glp_tree *tree) double glp_ios_node_bound(glp_tree *T, int p) + + int glp_bf_exists(glp_prob *lp) diff --git a/src/sage/libs/homfly.pyx b/src/sage/libs/homfly.pyx index 1889a40ea09..c53ccb740ce 100644 --- a/src/sage/libs/homfly.pyx +++ b/src/sage/libs/homfly.pyx @@ -1,3 +1,4 @@ +# distutils: libraries = homfly gc r""" Cython wrapper for libhomfly library diff --git a/src/sage/libs/lcalc/lcalc_Lfunction.pyx b/src/sage/libs/lcalc/lcalc_Lfunction.pyx index a90d2e5d6be..debfdf97251 100644 --- a/src/sage/libs/lcalc/lcalc_Lfunction.pyx +++ b/src/sage/libs/lcalc/lcalc_Lfunction.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = m ntl Lfunction +# distutils: extra_compile_args = -O3 -ffast-math +# distutils: language = c++ r""" Rubinstein's lcalc library diff --git a/src/sage/libs/libecm.pyx b/src/sage/libs/libecm.pyx index 41a788aca3f..1deacb45b39 100644 --- a/src/sage/libs/libecm.pyx +++ b/src/sage/libs/libecm.pyx @@ -1,3 +1,5 @@ +# distutils: libraries = ecm +# distutils: extra_link_args = LINUX_NOEXECSTACK r""" The Elliptic Curve Method for Integer Factorization (ECM) diff --git a/src/sage/libs/linbox/fflas.pxd b/src/sage/libs/linbox/fflas.pxd index b685af1f27d..ecbd2579909 100644 --- a/src/sage/libs/linbox/fflas.pxd +++ b/src/sage/libs/linbox/fflas.pxd @@ -1,6 +1,8 @@ # distutils: extra_compile_args = FFLASFFPACK_CFLAGS +# distutils: include_dirs = FFLASFFPACK_INCDIR # 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/givaro.pxd b/src/sage/libs/linbox/givaro.pxd index 34209f4c611..e6b5a06c3b0 100644 --- a/src/sage/libs/linbox/givaro.pxd +++ b/src/sage/libs/linbox/givaro.pxd @@ -1,4 +1,5 @@ # distutils: extra_compile_args = GIVARO_CFLAGS +# distutils: include_dirs = GIVARO_INCDIR # distutils: libraries = GIVARO_LIBRARIES FFLASFFPACK_LIBRARIES # distutils: library_dirs = GIVARO_LIBDIR # distutils: language = c++ diff --git a/src/sage/libs/linbox/linbox.pxd b/src/sage/libs/linbox/linbox.pxd index fb0e0dcf9f9..9112d151f8b 100644 --- a/src/sage/libs/linbox/linbox.pxd +++ b/src/sage/libs/linbox/linbox.pxd @@ -1,6 +1,8 @@ # distutils: extra_compile_args = LINBOX_CFLAGS +# distutils: include_dirs = LINBOX_INCDIR # 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/meataxe.pyx b/src/sage/libs/meataxe.pyx index 7549e55e0d9..f6fc4be1585 100644 --- a/src/sage/libs/meataxe.pyx +++ b/src/sage/libs/meataxe.pyx @@ -1,3 +1,5 @@ +# distutils: libraries = mtx +# sage_setup: distribution = sage-meataxe #***************************************************************************** # Copyright (C) 2017 Simon King # diff --git a/src/sage/libs/mpmath/ext_main.pyx b/src/sage/libs/mpmath/ext_main.pyx index 298d289feea..694cc089e7c 100644 --- a/src/sage/libs/mpmath/ext_main.pyx +++ b/src/sage/libs/mpmath/ext_main.pyx @@ -1065,14 +1065,10 @@ cdef class Context: sage: class MyInt(int): ....: pass - sage: class MyLong(long): # py2 - ....: pass sage: class MyFloat(float): ....: pass sage: mag(MyInt(10)) 4 - sage: mag(MyLong(10)) # py2 - 4 """ cdef int typ @@ -1590,19 +1586,6 @@ cdef class mpnumber: """ return binop(OP_MUL, self, other, global_opts) - def __div__(self, other): - """ - Division of mpmath numbers. Compatible numerical types - are automatically converted to mpmath numbers :: - - sage: from mpmath import mpf, mpc - sage: mpf(10) / mpc(5) - mpc(real='2.0', imag='0.0') - sage: float(9) / mpf(3) - mpf('3.0') - """ - return binop(OP_DIV, self, other, global_opts) - def __truediv__(self, other): """ Division of mpmath numbers. Compatible numerical types @@ -1791,18 +1774,6 @@ cdef class mpf_base(mpnumber): """ return int(libmp.to_int(self._mpf_)) - def __long__(self): - """ - Support long conversion for derived classes :: - - sage: from mpmath import mpf - sage: from sage.libs.mpmath.ext_main import mpf_base - sage: class X(mpf_base): _mpf_ = mpf(3.25)._mpf_ - sage: long(X()) - 3L - """ - return long(self.__int__()) - def __float__(self): """ Support float conversion for derived classes :: @@ -2052,26 +2023,6 @@ cdef class mpf(mpf_base): MPF_to_fixed(tmp_mpz, &self.value, 0, True) return mpzi(tmp_mpz) - def __long__(self): - r""" - Convert this mpf value to a long. - - (Due to http://bugs.python.org/issue9869, to allow NZMATH to use - this Sage-modified version of mpmath, it is vital that we - return a long, not an int.) - - TESTS:: - - sage: import mpmath # py2 - sage: v = mpmath.mpf(2) # py2 - sage: class MyLong(long): # py2 - ....: pass - sage: MyLong(v) # py2 - 2L - """ - MPF_to_fixed(tmp_mpz, &self.value, 0, True) - return mpzl(tmp_mpz) - def __float__(self): """ Convert to a double-precision Python float :: diff --git a/src/sage/libs/ntl/convert.pyx b/src/sage/libs/ntl/convert.pyx index 1da7afd077c..19380ded8ea 100644 --- a/src/sage/libs/ntl/convert.pyx +++ b/src/sage/libs/ntl/convert.pyx @@ -1,4 +1,7 @@ # distutils: depends = NTL/ZZ.h +# distutils: libraries = ntl gmp +# distutils: language = c++ + """ Conversion between NTL's ``ZZ`` and various other types """ diff --git a/src/sage/libs/ntl/error.pyx b/src/sage/libs/ntl/error.pyx index 9052fe0a286..99d6ca92a0c 100644 --- a/src/sage/libs/ntl/error.pyx +++ b/src/sage/libs/ntl/error.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp +# distutils: language = c++ + """ NTL error handler diff --git a/src/sage/libs/ntl/ntl_GF2.pyx b/src/sage/libs/ntl/ntl_GF2.pyx index 5aa701f451f..1a03ed5ebd7 100644 --- a/src/sage/libs/ntl/ntl_GF2.pyx +++ b/src/sage/libs/ntl/ntl_GF2.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2007 Martin Albrecht # @@ -150,9 +153,6 @@ cdef class ntl_GF2(object): GF2_div(r.x, (self).x, (other).x) return r - def __div__(self, other): - return self / other - def __sub__(self, other): """ sage: o = ntl.GF2(1) diff --git a/src/sage/libs/ntl/ntl_GF2E.pyx b/src/sage/libs/ntl/ntl_GF2E.pyx index ee6ab1c6adc..f9072cbc612 100644 --- a/src/sage/libs/ntl/ntl_GF2E.pyx +++ b/src/sage/libs/ntl/ntl_GF2E.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2005 William Stein # Copyright (C) 2007 Martin Albrecht @@ -281,9 +284,6 @@ cdef class ntl_GF2E(object): GF2E_div(r.x, self.x, (other).x) return r - def __div__(self, other): - return self / other - def __neg__(ntl_GF2E self): """ EXAMPLES:: diff --git a/src/sage/libs/ntl/ntl_GF2EContext.pyx b/src/sage/libs/ntl/ntl_GF2EContext.pyx index ab0ea3b9d0e..b1b453ae781 100644 --- a/src/sage/libs/ntl/ntl_GF2EContext.pyx +++ b/src/sage/libs/ntl/ntl_GF2EContext.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2005 William Stein # diff --git a/src/sage/libs/ntl/ntl_GF2EX.pyx b/src/sage/libs/ntl/ntl_GF2EX.pyx index 19956e0043f..4a3ac4a3c0e 100644 --- a/src/sage/libs/ntl/ntl_GF2EX.pyx +++ b/src/sage/libs/ntl/ntl_GF2EX.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2005 William Stein # @@ -51,7 +54,9 @@ cdef class ntl_GF2EX(object): if modulus is None: raise ValueError("You must specify a modulus when creating a GF2E.") - ccreadstr(self.x, str(x)) + str_x = str(x) # can cause modulus to change trac #25790 + self.c.restore_c() + ccreadstr(self.x, str_x) def __cinit__(self, modulus=None, x=[]): #################### WARNING ################### diff --git a/src/sage/libs/ntl/ntl_GF2X.pyx b/src/sage/libs/ntl/ntl_GF2X.pyx index 19dee747696..b80766b4ec1 100644 --- a/src/sage/libs/ntl/ntl_GF2X.pyx +++ b/src/sage/libs/ntl/ntl_GF2X.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + # **************************************************************************** # Copyright (C) 2005 William Stein # Copyright (C) 2007 Martin Albrecht @@ -221,9 +224,6 @@ cdef class ntl_GF2X(object): raise ArithmeticError("self (=%s) is not divisible by b (=%s)" % (self, b)) return q - def __div__(self, other): - return self / other - def DivRem(ntl_GF2X self, b): """ EXAMPLES:: diff --git a/src/sage/libs/ntl/ntl_ZZ.pyx b/src/sage/libs/ntl/ntl_ZZ.pyx index 7e040cd9c76..f8aefaf36dd 100644 --- a/src/sage/libs/ntl/ntl_ZZ.pyx +++ b/src/sage/libs/ntl/ntl_ZZ.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2005 William Stein # @@ -62,8 +65,6 @@ cdef class ntl_ZZ(object): 12 sage: ntl.ZZ(Integer(95413094)) 95413094 - sage: ntl.ZZ(long(223895239852389582983)) - 223895239852389582983 sage: ntl.ZZ('-1') -1 sage: ntl.ZZ('1L') @@ -253,9 +254,7 @@ cdef class ntl_ZZ(object): sage: ntl.ZZ(10^30).__int__() 1000000000000000000000000000000L - sage: type(ntl.ZZ(10^30).__int__()) # py2 - - sage: type(ntl.ZZ(10^30).__int__()) # py3 + sage: type(ntl.ZZ(10^30).__int__()) """ return int(self._integer_()) diff --git a/src/sage/libs/ntl/ntl_ZZX.pyx b/src/sage/libs/ntl/ntl_ZZX.pyx index 228da080442..4d602df9032 100644 --- a/src/sage/libs/ntl/ntl_ZZX.pyx +++ b/src/sage/libs/ntl/ntl_ZZX.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2005 William Stein # @@ -357,9 +360,6 @@ cdef class ntl_ZZX(object): result = make_ZZX_sig_off(q) return result - def __div__(self, other): - return self / other - def __mod__(ntl_ZZX self, ntl_ZZX other): """ Given polynomials a, b in ZZ[X], there exist polynomials q, r diff --git a/src/sage/libs/ntl/ntl_ZZ_p.pyx b/src/sage/libs/ntl/ntl_ZZ_p.pyx index 5dd7b7bad9e..c99b8d2750e 100644 --- a/src/sage/libs/ntl/ntl_ZZ_p.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_p.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2005 William Stein # @@ -80,6 +83,7 @@ cdef class ntl_ZZ_p(object): This class takes care of making sure that the C++ library NTL global variable is set correctly before performing any arithmetic. """ + def __init__(self, v=None, modulus=None): r""" Initializes an NTL integer mod p. @@ -91,8 +95,6 @@ cdef class ntl_ZZ_p(object): 1 sage: ntl.ZZ_p(Integer(95413094), c) 7 - sage: ntl.ZZ_p(long(223895239852389582988), c) - 5 sage: ntl.ZZ_p('-1', c) 10 @@ -101,7 +103,7 @@ cdef class ntl_ZZ_p(object): if modulus is None: raise ValueError("You must specify a modulus when creating a ZZ_p.") - #self.c.restore_c() ## The context was restored in __new__ + # self.c._assert_is_current_modulus() # The context was restored in __new__ cdef ZZ_c temp, num, den cdef long failed @@ -121,7 +123,9 @@ cdef class ntl_ZZ_p(object): (v.denominator())._to_ZZ(&den) ZZ_p_div(self.x, ZZ_to_ZZ_p(num), ZZ_to_ZZ_p(den)) else: - ccreadstr(self.x, str(v)) + str_v = str(v) # can cause modulus to change trac #25790 + self.c.restore_c() + ccreadstr(self.x, str_v) def __cinit__(self, v=None, modulus=None): #################### WARNING ################### diff --git a/src/sage/libs/ntl/ntl_ZZ_pContext.pxd b/src/sage/libs/ntl/ntl_ZZ_pContext.pxd index 8f25a08d912..124ba102ac8 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pContext.pxd +++ b/src/sage/libs/ntl/ntl_ZZ_pContext.pxd @@ -1,5 +1,7 @@ from .types cimport ZZ_pContext_c from .ntl_ZZ cimport ntl_ZZ +from .types cimport ZZ_c + cdef class ntl_ZZ_pContext_class(object): cdef ZZ_pContext_c x @@ -7,7 +9,13 @@ cdef class ntl_ZZ_pContext_class(object): cdef ntl_ZZ p cdef double p_bits cdef object __weakref__ + cpdef void _assert_is_current_modulus(self) except * + cdef class ntl_ZZ_pContext_factory(object): cdef object context_dict cdef ntl_ZZ_pContext_class make_c(self, ntl_ZZ v) + + +cdef extern from "ntlwrap.h": + cdef const ZZ_c& ntl_ZZ_p_current_modulus "ZZ_p::modulus"() diff --git a/src/sage/libs/ntl/ntl_ZZ_pContext.pyx b/src/sage/libs/ntl/ntl_ZZ_pContext.pyx index 274500ec0f7..81415974f4b 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pContext.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pContext.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2005 William Stein # @@ -17,10 +20,10 @@ include 'misc.pxi' include 'decl.pxi' import weakref +from sage.ext.cplusplus cimport ccrepr from sage.rings.integer cimport Integer - cdef class ntl_ZZ_pContext_class(object): def __init__(self, ntl_ZZ v): """ @@ -94,7 +97,6 @@ cdef class ntl_ZZ_pContext_class(object): """ return Integer(self.p) - def restore(self): """ EXAMPLES:: @@ -111,7 +113,48 @@ cdef class ntl_ZZ_pContext_class(object): cdef void restore_c(self): self.x.restore() + cpdef void _assert_is_current_modulus(self) except *: + """ + Assert that is currently-set NTL modulus. + + Mostly for debugging purposes. If false, an assertion is raised. This method segfaults if + the NTL modulus has never been set before. + + EXAMPLES:: + + sage: c1 = ntl.ZZ_pContext(7) + sage: c2 = ntl.ZZ_pContext(5) + sage: c1.restore() + sage: c1._assert_is_current_modulus() + sage: c2._assert_is_current_modulus() + Traceback (most recent call last): + ... + AssertionError: modulus mismatch: 5 != 7 + sage: c2.restore() + sage: c1._assert_is_current_modulus() + Traceback (most recent call last): + ... + AssertionError: modulus mismatch: 7 != 5 + sage: c2._assert_is_current_modulus() + sage: ntl.ZZ_pContext(3).restore() + sage: c1._assert_is_current_modulus() + Traceback (most recent call last): + ... + AssertionError: modulus mismatch: 7 != 3 + sage: c2._assert_is_current_modulus() + Traceback (most recent call last): + ... + AssertionError: modulus mismatch: 5 != 3 + """ + if self.p.x == ntl_ZZ_p_current_modulus(): + return + raise AssertionError('modulus mismatch: {} != {}'.format( + self.p, + ccrepr(ntl_ZZ_p_current_modulus()))) + + cdef class ntl_ZZ_pContext_factory(object): + def __init__(self): self.context_dict = {} @@ -131,11 +174,14 @@ cdef class ntl_ZZ_pContext_factory(object): self.context_dict[v] = weakref.ref(context) return context + ZZ_pContext_factory = ntl_ZZ_pContext_factory() + def ntl_ZZ_pContext( v ): """ Create a new ZZ_pContext. + EXAMPLES:: sage: c = ntl.ZZ_pContext(178) diff --git a/src/sage/libs/ntl/ntl_ZZ_pE.pyx b/src/sage/libs/ntl/ntl_ZZ_pE.pyx index 47bebfcc608..d5d79b33d21 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pE.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pE.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2005 William Stein # @@ -64,6 +67,7 @@ cdef class ntl_ZZ_pE(object): This class takes care of making sure that the C++ library NTL global variable is set correctly before performing any arithmetic. """ + def __init__(self, v=None, modulus=None): r""" Initializes an ntl ZZ_pE. @@ -75,8 +79,6 @@ cdef class ntl_ZZ_pE(object): [1 3] sage: c.ZZ_pE(Integer(95413094)) [7] - sage: c.ZZ_pE(long(223895239852389582988)) - [5] sage: c.ZZ_pE('[1]') [1] @@ -109,9 +111,7 @@ cdef class ntl_ZZ_pE(object): self.x = ZZ_pX_to_ZZ_pE((v).x) elif isinstance(v, list) or isinstance(v, tuple): tmp_zzpx = ntl_ZZ_pX(v, self.c.pc) - # random values without the following restore call - # surely because the above call restore things and breaks the modulus - self.c.restore_c() + self.c.restore_c() # allocating tmp_zzpx can change the current modulus trac #25790 self.x = ZZ_pX_to_ZZ_pE(tmp_zzpx.x) elif isinstance(v, long): PyLong_to_ZZ(&temp, v) @@ -126,7 +126,9 @@ cdef class ntl_ZZ_pE(object): (v)._to_ZZ(&temp) self.x = ZZ_to_ZZ_pE(temp) else: - ccreadstr(self.x, str(v)) + str_v = str(v) # can cause modulus to change trac #25790 + self.c.restore_c() + ccreadstr(self.x, str_v) def __cinit__(ntl_ZZ_pE self, v=None, modulus=None): #################### WARNING ################### @@ -333,6 +335,7 @@ cdef class ntl_ZZ_pE(object): r.x = (self.c.f).x return r + def make_ZZ_pE(x, c): """ Here for unpickling. diff --git a/src/sage/libs/ntl/ntl_ZZ_pEContext.pxd b/src/sage/libs/ntl/ntl_ZZ_pEContext.pxd index e41673b7e6b..0e4f679218e 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEContext.pxd +++ b/src/sage/libs/ntl/ntl_ZZ_pEContext.pxd @@ -1,13 +1,22 @@ from .types cimport ZZ_pContext_c, ZZ_pEContext_c from .ntl_ZZ_pContext cimport ntl_ZZ_pContext_class +from .ntl_ZZ_pX cimport ntl_ZZ_pX +from .types cimport ZZ_pX_Modulus_c + cdef struct ZZ_pEContext_ptrs: ZZ_pEContext_c *zzpec ZZ_pContext_c *zzpc + cdef class ntl_ZZ_pEContext_class(object): cdef ZZ_pEContext_ptrs ptrs cdef ZZ_pEContext_c x cdef ntl_ZZ_pContext_class pc cdef void restore_c(self) - cdef f + cdef ntl_ZZ_pX f + cpdef void _assert_is_current_modulus(self) except * + + +cdef extern from "ntlwrap.h": + cdef ZZ_pX_Modulus_c& ZZ_pE_current_modulus "ZZ_pE::modulus"() diff --git a/src/sage/libs/ntl/ntl_ZZ_pEContext.pyx b/src/sage/libs/ntl/ntl_ZZ_pEContext.pyx index bfa408be73f..c5abe44a5f8 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEContext.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pEContext.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2005 William Stein # @@ -16,12 +19,15 @@ include 'misc.pxi' include 'decl.pxi' -ZZ_pEContextDict = {} - +from sage.ext.cplusplus cimport ccrepr from sage.libs.ntl.ntl_ZZ_pX cimport ntl_ZZ_pX from sage.libs.ntl.ntl_ZZ_pContext import ntl_ZZ_pContext from sage.libs.ntl.ntl_ZZ cimport ntl_ZZ + +ZZ_pEContextDict = {} + + cdef class ntl_ZZ_pEContext_class(object): def __init__(self, ntl_ZZ_pX f): """ @@ -152,6 +158,47 @@ cdef class ntl_ZZ_pEContext_class(object): from .ntl_ZZ_pEX import ntl_ZZ_pEX return ntl_ZZ_pEX(v, modulus=self) + cpdef void _assert_is_current_modulus(self) except *: + """ + Assert that this is currently-set NTL modulus. + + Mostly for debugging purposes. If false, an assertion is raised. This method + segfaults if the NTL modulus has never been set before. + + EXAMPLES:: + + sage: c1 = ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1], 5)) + sage: c2 = ntl.ZZ_pEContext(ntl.ZZ_pX([1,2,1], 5)) + sage: c1.restore() + sage: c1._assert_is_current_modulus() + sage: c2._assert_is_current_modulus() + Traceback (most recent call last): + ... + AssertionError: modulus mismatch: [1 2 1] != [1 1 1] + sage: c2.restore() + sage: c1._assert_is_current_modulus() + Traceback (most recent call last): + ... + AssertionError: modulus mismatch: [1 1 1] != [1 2 1] + sage: c2._assert_is_current_modulus() + sage: ntl.ZZ_pContext(3).restore() + sage: c1._assert_is_current_modulus() + Traceback (most recent call last): + ... + AssertionError: modulus mismatch: 5 != 3 + sage: c2._assert_is_current_modulus() + Traceback (most recent call last): + ... + AssertionError: modulus mismatch: 5 != 3 + """ + self.pc._assert_is_current_modulus() + if self.f.x == ZZ_pE_current_modulus().val(): + return + raise AssertionError('modulus mismatch: {} != {}'.format( + ccrepr(self.f.x), + ccrepr(ZZ_pE_current_modulus().val()))) + + def ntl_ZZ_pEContext( ntl_ZZ_pX f): """ Creates an ntl_ZZ_pEContext. diff --git a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx index 9910523e9b9..58939074d82 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + """ Wrapper for NTL's polynomials over finite ring extensions of $\Z / p\Z.$ @@ -74,8 +77,7 @@ cdef class ntl_ZZ_pEX(object): if modulus is None and v is None: raise ValueError("You must specify a modulus when creating a ZZ_pEX.") - # self.c.restore_c() ## Restoring the context is taken care of in __new__ - + # self.c._assert_is_current_modulus() ## Restoring the context is taken care of in __new__ cdef ntl_ZZ_pE cc cdef Py_ssize_t i @@ -86,6 +88,7 @@ cdef class ntl_ZZ_pEX(object): x = v[i] if not isinstance(x, ntl_ZZ_pE): cc = ntl_ZZ_pE(x,self.c) + self.c.restore_c() else: if self.c is not (x).c: raise ValueError("inconsistent moduli") @@ -138,13 +141,14 @@ cdef class ntl_ZZ_pEX(object): def __reduce__(self): """ - TESTS: - sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1], 7)) - sage: a = ntl.ZZ_pE([3,2], c) - sage: b = ntl.ZZ_pE([1,2], c) - sage: f = ntl.ZZ_pEX([a, b, b]) - sage: loads(dumps(f)) == f - True + TESTS:: + + sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1], 7)) + sage: a = ntl.ZZ_pE([3,2], c) + sage: b = ntl.ZZ_pE([1,2], c) + sage: f = ntl.ZZ_pEX([a, b, b]) + sage: loads(dumps(f)) == f + True """ return make_ZZ_pEX, (self.list(), self.get_modulus_context()) @@ -152,13 +156,14 @@ cdef class ntl_ZZ_pEX(object): """ Returns a string representation of self. - TESTS: - sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1], 7)) - sage: a = ntl.ZZ_pE([3,2], c) - sage: b = ntl.ZZ_pE([1,2], c) - sage: f = ntl.ZZ_pEX([a, b, b]) - sage: f - [[3 2] [1 2] [1 2]] + TESTS:: + + sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1], 7)) + sage: a = ntl.ZZ_pE([3,2], c) + sage: b = ntl.ZZ_pE([1,2], c) + sage: f = ntl.ZZ_pEX([a, b, b]) + sage: f + [[3 2] [1 2] [1 2]] """ self.c.restore_c() return ccrepr(self.x) @@ -167,20 +172,21 @@ cdef class ntl_ZZ_pEX(object): """ Return a copy of self. - TESTS: - sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1], 7)) - sage: a = ntl.ZZ_pE([3,2], c) - sage: b = ntl.ZZ_pE([1,2], c) - sage: f = ntl.ZZ_pEX([a, b, b]) - sage: f - [[3 2] [1 2] [1 2]] - sage: y = copy(f) - sage: y == f - True - sage: y is f - False - sage: f[0] = 0; y - [[3 2] [1 2] [1 2]] + TESTS:: + + sage: c=ntl.ZZ_pEContext(ntl.ZZ_pX([1,1,1], 7)) + sage: a = ntl.ZZ_pE([3,2], c) + sage: b = ntl.ZZ_pE([1,2], c) + sage: f = ntl.ZZ_pEX([a, b, b]) + sage: f + [[3 2] [1 2] [1 2]] + sage: y = copy(f) + sage: y == f + True + sage: y is f + False + sage: f[0] = 0; y + [[3 2] [1 2] [1 2]] """ cdef ntl_ZZ_pEX r = self._new() #self.c.restore_c() ## _new() restores @@ -376,9 +382,6 @@ cdef class ntl_ZZ_pEX(object): raise ArithmeticError("self (=%s) is not divisible by other (=%s)" % (self, other)) return r - def __div__(self, other): - return self / other - def __mod__(ntl_ZZ_pEX self, ntl_ZZ_pEX other): """ Given polynomials a, b in ZZ_pE[X], if p is prime and the defining modulus irreducible, diff --git a/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi b/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi index a5de653c2e8..c22f31afe78 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi +++ b/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi @@ -65,9 +65,9 @@ cdef int celement_destruct(ZZ_pEX_c *e, cparent parent): sage: P. = PolynomialRing(GF(next_prime(2**60)**3,'a'),implementation='NTL') sage: del x """ - if parent != NULL: - parent[0].zzpc[0].restore() - parent[0].zzpec[0].restore() + # do not call restore here + # 1) the NTL context might have already been destroyed when exiting Python + # 2) you better not make any NTL calls after destruct, no need to set the context cdef int celement_gen(ZZ_pEX_c *e, long i, cparent parent) except -2: """ @@ -133,9 +133,6 @@ cdef inline bint celement_is_zero(ZZ_pEX_c* a, cparent parent) except -2: sage: bool(P(0)), P(0).is_zero() (False, True) """ -# if parent != NULL: -# parent[0].zzpc[0].restore() -# parent[0].zzpec[0].restore() return ZZ_pEX_IsZero(a[0]) cdef inline bint celement_is_one(ZZ_pEX_c *a, cparent parent) except -2: diff --git a/src/sage/libs/ntl/ntl_ZZ_pX.pyx b/src/sage/libs/ntl/ntl_ZZ_pX.pyx index 169313e272e..0d18d06c4fa 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pX.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + # **************************************************************************** # Copyright (C) 2005 William Stein # @@ -30,6 +33,7 @@ from sage.libs.ntl.ntl_ZZ import unpickle_class_args from sage.misc.randstate cimport randstate, current_randstate from sage.libs.gmp.mpz cimport * + cdef inline make_ZZ_p(ZZ_p_c* x, ntl_ZZ_pContext_class ctx): cdef ntl_ZZ_p y sig_off() @@ -38,6 +42,7 @@ cdef inline make_ZZ_p(ZZ_p_c* x, ntl_ZZ_pContext_class ctx): del x return y + cdef make_ZZ_pX(ZZ_pX_c* x, ntl_ZZ_pContext_class ctx): cdef ntl_ZZ_pX y y = ntl_ZZ_pX.__new__(ntl_ZZ_pX) @@ -47,6 +52,7 @@ cdef make_ZZ_pX(ZZ_pX_c* x, ntl_ZZ_pContext_class ctx): sig_off() return y + ############################################################################## # # ZZ_pX -- polynomials over the integers modulo p @@ -65,6 +71,7 @@ cdef class ntl_ZZ_pX(object): Small degree polynomials are multiplied either with classical or Karatsuba algorithms. """ + # See ntl_ZZ_pX.pxd for definition of data members def __init__(self, v=None, modulus=None): """ @@ -85,8 +92,7 @@ cdef class ntl_ZZ_pX(object): if modulus is None: raise ValueError("You must specify a modulus when creating a ZZ_pX.") - #self.c.restore_c() ## the context was restored in __new__ - + # self.c._assert_is_current_modulus() # the context was restored in __new__ cdef ntl_ZZ_p cc cdef Py_ssize_t i @@ -96,11 +102,13 @@ cdef class ntl_ZZ_pX(object): for i, x in enumerate(v): if not isinstance(x, ntl_ZZ_p): cc = ntl_ZZ_p(x, self.c) + self.c.restore_c() else: cc = x ZZ_pX_SetCoeff(self.x, i, cc.x) elif v is not None: - s = str(v).replace(',', ' ').replace('L', '') + s = str(v).replace(',', ' ').replace('L', '') # can change the modulus trac #25790 + self.c.restore_c() ccreadstr(self.x, s) def __cinit__(self, v=None, modulus=None): @@ -399,9 +407,6 @@ cdef class ntl_ZZ_pX(object): raise ArithmeticError("self (=%s) is not divisible by other (=%s)" % (self, other)) return r - def __div__(self, other): - return self / other - def __mod__(ntl_ZZ_pX self, ntl_ZZ_pX other): """ Given polynomials a, b in ZZ_p[X], if p is prime, then there exist polynomials q, r diff --git a/src/sage/libs/ntl/ntl_lzz_p.pyx b/src/sage/libs/ntl/ntl_lzz_p.pyx index 7809288152f..0aef08c3601 100644 --- a/src/sage/libs/ntl/ntl_lzz_p.pyx +++ b/src/sage/libs/ntl/ntl_lzz_p.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + """ ntl_lzz_p.pyx @@ -162,7 +165,8 @@ cdef class ntl_zz_p(object): """ For pickling. - TESTS: + TESTS:: + sage: f = ntl.zz_p(16,244) sage: loads(dumps(f)) == f True @@ -250,9 +254,6 @@ cdef class ntl_zz_p(object): sig_off() return q - def __div__(self, other): - return self / other - def __pow__(ntl_zz_p self, long n, ignored): """ Return the n-th nonnegative power of self. @@ -420,11 +421,13 @@ cdef class ntl_zz_p(object): self.c.restore_c() zz_p_clear(self.x) + def make_zz_p(val, context): """ For unpickling. - TESTS: + TESTS:: + sage: f = ntl.zz_p(1, 12) sage: loads(dumps(f)) == f True diff --git a/src/sage/libs/ntl/ntl_lzz_pContext.pyx b/src/sage/libs/ntl/ntl_lzz_pContext.pyx index c38d7e8ed67..30667a452d2 100644 --- a/src/sage/libs/ntl/ntl_lzz_pContext.pyx +++ b/src/sage/libs/ntl/ntl_lzz_pContext.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2005 William Stein # @@ -43,6 +46,10 @@ cdef class ntl_zz_pContext_class(object): def __cinit__(self, long v): if v > NTL_SP_BOUND: raise ValueError("Modulus (=%s) is too big" % v) + elif v < 2: + # Trac 13940: only moduli greater than one are supported. + raise ValueError("Modulus (=%s) is too small" % v) + self.x = zz_pContext_c(v) zz_pContextDict[repr(v)] = self self.p = v @@ -104,6 +111,14 @@ def ntl_zz_pContext( v ): Traceback (most recent call last): ... ValueError: Modulus (=10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) is too big + sage: f = ntl.zz_pContext(1) + Traceback (most recent call last): + ... + ValueError: Modulus (=1) is too small + sage: f = ntl.zz_pContext(0) + Traceback (most recent call last): + ... + ValueError: Modulus (=0) is too small """ if v > NTL_SP_BOUND: raise ValueError("Modulus (=%s) is too big" % v) diff --git a/src/sage/libs/ntl/ntl_lzz_pX.pyx b/src/sage/libs/ntl/ntl_lzz_pX.pyx index c063e30548b..c08ad28491a 100644 --- a/src/sage/libs/ntl/ntl_lzz_pX.pyx +++ b/src/sage/libs/ntl/ntl_lzz_pX.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + """ ntl_lzz_pX.pyx @@ -162,7 +165,8 @@ cdef class ntl_zz_pX(object): def __reduce__(self): """ - TESTS: + TESTS:: + sage: f = ntl.zz_pX([10,10^30+1], 20) sage: f == loads(dumps(f)) True @@ -349,9 +353,6 @@ cdef class ntl_zz_pX(object): raise ArithmeticError("self (=%s) is not divisible by other (=%s)" % (self, other)) return q - def __div__(self, other): - return self / other - def __mod__(ntl_zz_pX self, other): """ Given polynomials a, b in ZZ[X], there exist polynomials q, r @@ -891,7 +892,8 @@ def make_zz_pX(L, context): """ For unpickling. - TESTS: + TESTS:: + sage: f = ntl.zz_pX(range(16), 12) sage: loads(dumps(f)) == f True diff --git a/src/sage/libs/ntl/ntl_mat_GF2.pyx b/src/sage/libs/ntl/ntl_mat_GF2.pyx index 19641a869f8..111010f8d14 100644 --- a/src/sage/libs/ntl/ntl_mat_GF2.pyx +++ b/src/sage/libs/ntl/ntl_mat_GF2.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + """ Matrices over the $\GF{2}$ via NTL @@ -37,18 +40,20 @@ from .ntl_GF2 cimport ntl_GF2 from sage.rings.integer cimport Integer from sage.libs.ntl.ntl_ZZ import unpickle_class_args + cdef class ntl_mat_GF2(object): r""" The \class{mat_GF2} class implements arithmetic with matrices over $F_2$. """ def __init__(self, nrows=0, ncols=0, v=None): """ - Constructs a matrix over ntl.GF2. + Construct a matrix over ntl.GF2. INPUT: - nrows -- number of rows - ncols -- number of columns - v -- either a list or a matrix over GF(2^x) + + - nrows -- number of rows + - ncols -- number of columns + - v -- either a list or a matrix over GF(2^x) EXAMPLES:: @@ -409,7 +414,7 @@ cdef class ntl_mat_GF2(object): def determinant(self): """ - Returns the determinant. + Return the determinant. EXAMPLES:: @@ -435,9 +440,11 @@ cdef class ntl_mat_GF2(object): the rank of the first ncols columns). INPUT: - ncols -- number of columns to process (default: all) + + ncols -- number of columns to process (default: all) EXAMPLES:: + sage: A = random_matrix(GF(2), 10, 10) sage: Abar = ntl.mat_GF2(A) sage: A.echelon_form() @@ -476,9 +483,10 @@ cdef class ntl_mat_GF2(object): def list(self): """ - Returns a list of the entries in this matrix + Return a list of the entries in this matrix. EXAMPLES:: + sage: A = random_matrix(GF(2), 4, 4) sage: Abar = ntl.mat_GF2(A) sage: A.list() @@ -496,6 +504,7 @@ cdef class ntl_mat_GF2(object): Return \code{True} if this matrix contains only zeroes, and \code{False} otherwise. EXAMPLES:: + sage: A = random_matrix(GF(2), 10, 10) sage: Abar = ntl.mat_GF2(A) sage: Abar.IsZero() @@ -512,7 +521,7 @@ cdef class ntl_mat_GF2(object): def _sage_(ntl_mat_GF2 self): r""" - Returns a \class{Matrix} over GF(2). + Return a \class{Matrix} over GF(2). EXAMPLES:: @@ -554,9 +563,10 @@ cdef class ntl_mat_GF2(object): def transpose(ntl_mat_GF2 self): """ - Returns the transposed matrix of this matrix. + Return the transposed matrix of this matrix. EXAMPLES:: + sage: A = random_matrix(GF(2), 10, 10) sage: Abar = ntl.mat_GF2(A); Abar [[0 1 0 1 1 0 0 0 1 1] @@ -595,6 +605,7 @@ cdef class ntl_mat_GF2(object): Return $X = A^{-1}$; an error is raised if A is singular. EXAMPLES:: + sage: l = [0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, \ 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, \ 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, \ @@ -615,6 +626,7 @@ cdef class ntl_mat_GF2(object): test if this matrix is the n x n identity matrix. EXAMPLES:: + sage: A = ntl.mat_GF2(4,4) sage: A[0,0] = 1 sage: A[1,1] = 1 @@ -634,6 +646,7 @@ cdef class ntl_mat_GF2(object): test if X is an n x n diagonal matrix with d on diagonal. EXAMPLES:: + sage: A = ntl.mat_GF2(4,4) sage: A[0,0] = 1 sage: A[1,1] = 1 @@ -653,6 +666,7 @@ cdef class ntl_mat_GF2(object): is in row echelon form. EXAMPLES:: + sage: A = random_matrix(GF(2),10,10) sage: Abar = ntl.mat_GF2(A) sage: A.image() diff --git a/src/sage/libs/ntl/ntl_mat_GF2E.pyx b/src/sage/libs/ntl/ntl_mat_GF2E.pyx index 123dc81e3c0..23c998a6e84 100644 --- a/src/sage/libs/ntl/ntl_mat_GF2E.pyx +++ b/src/sage/libs/ntl/ntl_mat_GF2E.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2005 William Stein # diff --git a/src/sage/libs/ntl/ntl_mat_ZZ.pyx b/src/sage/libs/ntl/ntl_mat_ZZ.pyx index 2fa8e3e7487..faeb3a1a552 100644 --- a/src/sage/libs/ntl/ntl_mat_ZZ.pyx +++ b/src/sage/libs/ntl/ntl_mat_ZZ.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = ntl gmp m +# distutils: language = c++ + #***************************************************************************** # Copyright (C) 2005 William Stein # diff --git a/src/sage/libs/ppl.pyx b/src/sage/libs/ppl.pyx index 3bcf99b09ae..f1c617576ff 100644 --- a/src/sage/libs/ppl.pyx +++ b/src/sage/libs/ppl.pyx @@ -1,3 +1,4 @@ +# distutils: sources = sage/libs/ppl_shim.cc # distutils: language = c++ # distutils: libraries = ppl m r""" @@ -281,6 +282,7 @@ cdef extern from "ppl.hh" namespace "Parma_Polyhedra_Library": bint OK() cdef cppclass PPL_Constraint: + PPL_Constraint() PPL_Constraint(PPL_Constraint &g) PPL_dimension_type space_dimension() PPL_ConstraintType type() @@ -5728,8 +5730,16 @@ cdef class Generator_System_iterator(object): cdef _wrap_Constraint(PPL_Constraint constraint): """ Wrap a C++ ``PPL_Constraint`` into a Cython ``Constraint``. + + Check that :trac:`27278` is fixed:: + + sage: from sage.libs.ppl import Variable, Constraint + sage: x=Variable(0) + sage: c = x == 0 + sage: type(Constraint(c)) + """ - cdef Constraint c = Constraint(True) + cdef Constraint c = Constraint.__new__(Constraint) c.thisptr = new PPL_Constraint(constraint) return c @@ -5805,8 +5815,15 @@ cdef class Constraint(object): cdef PPL_Constraint *thisptr + def __init__(self, arg=None): + if arg is None: + self.thisptr = new PPL_Constraint() + elif isinstance(arg, Constraint): + self.thisptr = new PPL_Constraint(( arg).thisptr[0]) + else: + raise TypeError("invalid argument for Constraint") - def __cinit__(self, do_not_construct_manually=False): + def __cinit__(self): """ The Cython constructor. @@ -5819,7 +5836,6 @@ cdef class Constraint(object): sage: x>0 # indirect doctest x0>0 """ - assert(do_not_construct_manually) self.thisptr = NULL @@ -6335,8 +6351,7 @@ cdef _wrap_Constraint_System(PPL_Constraint_System constraint_system): """ Wrap a C++ ``PPL_Constraint_System`` into a Cython ``Constraint_System``. """ - cdef Constraint_System cs = Constraint_System() - del cs.thisptr + cdef Constraint_System cs = Constraint_System.__new__(Constraint_System) cs.thisptr = new PPL_Constraint_System(constraint_system) return cs @@ -6367,18 +6382,7 @@ cdef class Constraint_System(object): cdef PPL_Constraint_System *thisptr - def __cinit__(self, arg=None): - """ - The Cython constructor. - - See :class:`Constraint_System` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import Constraint_System - sage: Constraint_System() - Constraint_System {} - """ + def __init__(self, arg=None): if arg is None: self.thisptr = new PPL_Constraint_System() return @@ -6397,6 +6401,19 @@ cdef class Constraint_System(object): return raise ValueError('Cannot initialize with '+str(arg)+'.') + def __cinit__(self, arg=None): + """ + The Cython constructor. + + See :class:`Constraint_System` for documentation. + + Tests: + + >>> from ppl import Constraint_System + >>> Constraint_System() + Constraint_System {} + """ + self.thisptr = NULL def __dealloc__(self): """ diff --git a/src/sage/libs/pynac/pynac.pxd b/src/sage/libs/pynac/pynac.pxd index 94d5e3fb1a8..577401c5b18 100644 --- a/src/sage/libs/pynac/pynac.pxd +++ b/src/sage/libs/pynac/pynac.pxd @@ -1,7 +1,9 @@ # distutils: language = c++ # distutils: libraries = pynac gmp # distutils: extra_compile_args = -std=c++11 SINGULAR_CFLAGS -# pynac/basic.h includes factory/factory.h so this ^ is needed to find it +# distutils: include_dirs = SINGULAR_INCDIR +# pynac/basic.h includes +# factory/factory.h so this ^ is needed to find it """ Declarations for pynac, a Python frontend for ginac diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 284cc17376d..0ebc8b5ff35 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -1106,8 +1106,6 @@ cdef bint py_is_integer(x): sage: py_is_integer(1r) True - sage: py_is_integer(long(1)) - True sage: py_is_integer(3^57) True sage: py_is_integer(SR(5)) @@ -1312,8 +1310,6 @@ def py_is_cinteger_for_doctest(x): sage: from sage.libs.pynac.pynac import py_is_cinteger_for_doctest sage: py_is_cinteger_for_doctest(1) True - sage: py_is_cinteger_for_doctest(long(-3)) - True sage: py_is_cinteger_for_doctest(I.pyobject()) True sage: py_is_cinteger_for_doctest(I.pyobject() - 3) @@ -1710,12 +1706,8 @@ cdef py_log(x): 3.141592653589793j sage: py_log(int(1)) 0.0 - sage: py_log(long(1)) - 0.0 sage: py_log(int(0)) -inf - sage: py_log(long(0)) - -inf sage: py_log(complex(0)) -inf sage: py_log(2) diff --git a/src/sage/libs/ratpoints.pyx b/src/sage/libs/ratpoints.pyx index 33911901166..a0ac9e48938 100644 --- a/src/sage/libs/ratpoints.pyx +++ b/src/sage/libs/ratpoints.pyx @@ -1,3 +1,4 @@ +# distutils: libraries = ratpoints r""" Hyperelliptic Curve Point Finding, via ratpoints (deprecated) diff --git a/src/sage/libs/readline.pyx b/src/sage/libs/readline.pyx index 2012f77191b..5d4e253fd78 100644 --- a/src/sage/libs/readline.pyx +++ b/src/sage/libs/readline.pyx @@ -1,3 +1,4 @@ +# distutils: libraries = readline """ Readline diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index 607c0cb12ae..0ac4169402c 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -1,3 +1,4 @@ +# distutils: include_dirs = SINGULAR_INCDIR # distutils: extra_compile_args = SINGULAR_CFLAGS # distutils: libraries = SINGULAR_LIBRARIES # distutils: library_dirs = SINGULAR_LIBDIR @@ -334,6 +335,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/function.pyx b/src/sage/libs/singular/function.pyx index b649ab1e649..0fea70ad259 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -107,7 +107,7 @@ from sage.libs.singular.singular import error_messages from sage.interfaces.singular import get_docstring -from sage.misc.misc import get_verbose +from sage.misc.verbose import get_verbose from sage.structure.sequence import Sequence, Sequence_generic from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence, PolynomialSequence_generic @@ -1554,7 +1554,7 @@ cdef class SingularLibraryFunction(SingularFunction): """ def __init__(self, name): """ - Construct a new Singular kernel function. + Construct a new Singular library function. EXAMPLES:: 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/libs/singular/ring.pxd b/src/sage/libs/singular/ring.pxd index 8c5d16f59df..b6cc791355b 100644 --- a/src/sage/libs/singular/ring.pxd +++ b/src/sage/libs/singular/ring.pxd @@ -21,16 +21,16 @@ from sage.libs.singular.decl cimport ring # # cdef class myclass_new(): # cdef ring* myring; -# cdef __cinit__(): +# def __cinit__(): # self.myring = singular_ring_new(...) -# cdef __dealloc__(): +# def __dealloc__(): # singular_ring_delete(self.myring) # # cdef class myclass_reference(): # cdef ring* refring; -# cdef __cinit__(ring* some_ring): +# def __cinit__(ring* some_ring): # self.refring = singular_ring_reference(some_ring) -# cdef __dealloc__(): +# def __dealloc__(): # singular_ring_delete(self.refring) # # You must not refer to Python/Cython classes in the Cython @@ -38,9 +38,9 @@ from sage.libs.singular.decl cimport ring # # cdef class myclass_invalid(): # cdef Parent parent; -# cdef __cinit__(Parent p): +# def __cinit__(Parent p): # self.parent = p -# cdef __dealloc__(): +# def __dealloc__(): # do_something_with(self.parent.ring) # segfault diff --git a/src/sage/libs/singular/ring.pyx b/src/sage/libs/singular/ring.pyx index 5d4fa74e804..63b63070cd2 100644 --- a/src/sage/libs/singular/ring.pyx +++ b/src/sage/libs/singular/ring.pyx @@ -116,6 +116,17 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: sage: P. = Zmod(25213521351515232)[]; P Multivariate Polynomial Ring in x, y, z over Ring of integers modulo 25213521351515232 + + TESTS: + + Check that ``degneglex`` and ``degrevlex`` are the same up to reversal of + variables (:trac:`29635`):: + + sage: R = PolynomialRing(QQ, 'x', 4, order='degrevlex') + sage: S = PolynomialRing(QQ, tuple(reversed(R.gens())), order='degneglex') + sage: L = [v for d in (0..4) for v in IntegerVectors(d, 4)] + sage: sorted([R.monomial(*e) for e in L]) == sorted([S.monomial(*e) for e in L]) + True """ cdef long cexponent cdef GFInfo* _param @@ -212,12 +223,13 @@ cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL: nlen = len(order[i]) _wvhdl[idx] = omAlloc0(len(order[i])*sizeof(int)) - for j in range(nlen): _wvhdl[idx][j] = 1 - _block0[idx] = offset + 1 # same like subsequent rp block + for j in range(nlen): + _wvhdl[idx][j] = 1 + _block0[idx] = offset + 1 # same like subsequent ls block _block1[idx] = offset + nlen idx += 1; # we need one more block here - _order[idx] = ringorder_rp + _order[idx] = ringorder_ls else: # ordinary orders _order[idx] = order_dict.get(s, ringorder_dp) @@ -559,7 +571,7 @@ cdef ring *singular_ring_reference(ring *existing_ring) except NULL: sage: ring_ptr # random output The ring pointer 0x7f78a646b8d0 sage: ring_refcount_dict[ring_ptr] - 4 + 3 sage: strat = GroebnerStrategy(Ideal([P.gen(0) + P.gen(1)])) sage: ring_refcount_dict[ring_ptr] diff --git a/src/sage/libs/sirocco.pyx b/src/sage/libs/sirocco.pyx index f3b4fabbc90..fecf69bfb1b 100644 --- a/src/sage/libs/sirocco.pyx +++ b/src/sage/libs/sirocco.pyx @@ -1,4 +1,7 @@ #cython: boundscheck=False, wraparound=False +# distutils: libraries = sirocco +# distutils: language = c++ +# sage_setup: distribution = sage-sirocco r""" Cython wrapper for sirocco library diff --git a/src/sage/libs/symmetrica/symmetrica.pxi b/src/sage/libs/symmetrica/symmetrica.pxi index 263ad7c35b4..5497d26b56e 100644 --- a/src/sage/libs/symmetrica/symmetrica.pxi +++ b/src/sage/libs/symmetrica/symmetrica.pxi @@ -448,7 +448,7 @@ cdef void late_import(): import sage.combinat.sf.sf SymmetricFunctions = sage.combinat.sf.sf.SymmetricFunctions - from six.moves import builtins + import builtins builtinlist = builtins.list import sage.rings.polynomial.multi_polynomial_ring diff --git a/src/sage/libs/symmetrica/symmetrica.pyx b/src/sage/libs/symmetrica/symmetrica.pyx index 4adc86fce36..7ca41fbc82b 100644 --- a/src/sage/libs/symmetrica/symmetrica.pyx +++ b/src/sage/libs/symmetrica/symmetrica.pyx @@ -1,3 +1,4 @@ +# distutils: libraries = symmetrica """ Symmetrica library """ diff --git a/src/sage/logic/boolformula.py b/src/sage/logic/boolformula.py index 8f1ccab2ebc..c01e5e57b2d 100644 --- a/src/sage/logic/boolformula.py +++ b/src/sage/logic/boolformula.py @@ -124,7 +124,7 @@ """ from __future__ import absolute_import, division -#***************************************************************************** +# ***************************************************************************** # Copyright (C) 2006 William Stein # Copyright (C) 2006 Chris Gorecki # Copyright (C) 2013 Paul Scurek @@ -132,8 +132,8 @@ # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from . import booleval from . import logictable @@ -743,6 +743,90 @@ def is_contradiction(self): """ return not self.is_satisfiable() + def is_consequence(self, *hypotheses): + r""" + Determine if ``self`` (the desired conclusion) is a logical consequence of the + hypotheses. The function call ``is_consequence(conclusion, *hypotheses)`` is a + synonym for ``conclusion.is_consequence(*hypotheses)``. + + INPUT: + + - ``*hypotheses`` -- instances of :class:`BooleanFormula` + + OUTPUT: + + A boolean value to be determined as follows: + + - ``True`` - if ``self`` (the desired conclusion) is a logical consequence + of the set of hypotheses + + - ``False`` - if ``self`` (the desired conclusion) is not a logical consequence + of the set of hypotheses + + EXAMPLES:: + + sage: from sage.logic.propcalc import formula + sage: formula("a | b").is_consequence(formula("b")) + True + sage: formula("a & b").is_consequence(formula("b")) + False + sage: formula("b").is_consequence(formula("a"), formula("a -> b")) + True + sage: formula("b -> a").is_consequence(formula("a -> b")) + False + sage: formula("~b -> ~a").is_consequence(formula("a -> b")) + True + + :: + + sage: f, g, h = propcalc.get_formulas("a & ~b", "c -> b", "c | e") + sage: propcalc.formula("a & e").is_consequence(f, g, h) + True + sage: i = propcalc.formula("a & ~e") + sage: i.is_consequence(f, g, h) + False + sage: from sage.logic.boolformula import is_consequence + sage: is_consequence(i, f, g, h) + False + sage: is_consequence(propcalc.formula("((p <-> q) & r) -> ~c"), f, g, h) + True + + Only a tautology is a logical consequence of an empty set of formulas:: + + sage: propcalc.formula("a | ~a").is_consequence() + True + sage: propcalc.formula("a | b").is_consequence() + False + + TESTS: + + Arguments must be instances of :class:`BooleanFormula` (not strings, for example):: + + sage: propcalc.formula("a | b").is_consequence("a | b") + Traceback (most recent call last): + ... + TypeError: is_consequence only takes instances of BooleanFormula() class as input + + AUTHORS: + + - Paul Scurek (2013-08-12) + """ + # make sure every argument is an instance of :class:`BooleanFormula` + for formula in (self,) + hypotheses: + if not isinstance(formula, BooleanFormula): + raise TypeError("is_consequence only takes instances of BooleanFormula() class as input") + + if not hypotheses: + # if there are no hypotheses, then we just want to know whether self is a tautology + return self.is_tautology() + else: + # conjoin all of the hypotheses into a single Boolean formula + conjunction = hypotheses[0] + for hypothesis in hypotheses[1:]: + conjunction = conjunction & hypothesis + + return conjunction.implies(self) + def implies(self, other): r""" Determine if calling formula implies other formula. @@ -1424,3 +1508,51 @@ def get_next_op(self, str): while i < len(str) - 1 and str[i] != '&' and str[i] != '|': i += 1 return str[i] + + def __len__(self): + r""" + Return the length of a Boolean formula. + + OUTPUT: + + The length of the Boolean formula. This is the number of operators plus + the number of variables (counting multiplicity). Parentheses are ignored. + + EXAMPLES:: + + sage: import sage.logic.propcalc as propcalc + sage: s = propcalc.formula("a") + sage: len(s) + 1 + sage: s = propcalc.formula("(a)") + sage: len(s) + 1 + sage: s = propcalc.formula("~a") + sage: len(s) + 2 + sage: s = propcalc.formula("a -> b") + sage: len(s) + 3 + sage: s = propcalc.formula("alpha -> beta") + sage: len(s) + 3 + sage: s = propcalc.formula("a -> a") + sage: len(s) + 3 + sage: s = propcalc.formula("~(a -> b)") + sage: len(s) + 4 + sage: s = propcalc.formula("((a&b)|(a&c))->~d") + sage: len(s) + 10 + + TESTS:: + + sage: s = propcalc.formula("(((alpha) -> ((beta))))") + sage: len(s) + 3 + """ + return len(flatten(self.full_tree())) + +# allow is_consequence to be called as a function (not only as a method of BooleanFormula) +is_consequence = BooleanFormula.is_consequence diff --git a/src/sage/logic/propcalc.py b/src/sage/logic/propcalc.py index fafc7bfea5d..985adee96ee 100644 --- a/src/sage/logic/propcalc.py +++ b/src/sage/logic/propcalc.py @@ -130,7 +130,7 @@ NameError: invalid variable name 9b: identifiers must begin with a letter and contain only alphanumerics and underscores """ from __future__ import absolute_import -#***************************************************************************** +# ***************************************************************************** # Copyright (C) 2006 William Stein # Copyright (C) 2006 Chris Gorecki # Copyright (C) 2013 Paul Scurek @@ -138,14 +138,15 @@ # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** ### TODO: ### converts (cnf) returns w/o change from . import boolformula from . import logicparser +from sage.misc.superseded import deprecated_function_alias def formula(s): @@ -310,72 +311,5 @@ def consistent(*formulas): # if conjunction is a contradiction, the formulas are inconsistent return not conjunction.is_contradiction() -def valid_consequence(consequence, *formulas): - r""" - Determine if ``consequence`` is a valid consequence of the set - of formulas in ``*formulas``. - - INPUT: - - - ``*formulas`` -- instances of :class:`BooleanFormula` - - - ``consequence`` -- instance of :class:`BooleanFormula` - - OUTPUT: - - A boolean value to be determined as follows: - - - ``True`` - if ``consequence`` is a valid consequence of the set - of ``*formulas`` - - - ``False`` - if ``consequence is not a valid consequence of the set - of ``*formulas`` - - EXAMPLES: - - This example illustrates determining if a formula is a valid - consequence of a set of other formulas:: - - sage: f, g, h, i = propcalc.get_formulas("a&~b", "c->b", "c|e", "e&a") - sage: propcalc.valid_consequence(i, f, g, h) - True - - :: - - sage: j = propcalc.formula("a&~e") - sage: propcalc.valid_consequence(j, f, g, h) - False - - :: - - sage: k = propcalc.formula("((p<->q)&r)->~c") - sage: propcalc.valid_consequence(k, f, g, h) - True - - AUTHORS: - - - Paul Scurek (2013-08-12) - """ - valid_input = True - - # make sure only instances of :class:`BooleanFormula` were passed as arguments - if not isinstance(consequence, boolformula.BooleanFormula): - valid_input = False - else: - for formula in formulas: - if not isinstance(formula, boolformula.BooleanFormula): - valid_input = False - break - if not valid_input: - raise TypeError("valid_input only takes instances of BooleanFormula() class as input") - - # conjoin all of the formulas in the list ``formulas`` - conjunction = formulas[0] - for formula in formulas[1:]: - conjunction = conjunction & formula - - # create a conditional where conjunction is the antecedent and ``consequence`` is the consequent - corresponding_conditional = conjunction.ifthen(consequence) - - return corresponding_conditional.is_tautology() - +# define function ``valid_consequence`` for backward compatibility +valid_consequence = deprecated_function_alias(28052, boolformula.is_consequence) diff --git a/src/sage/manifolds/calculus_method.py b/src/sage/manifolds/calculus_method.py index 7042afe77a5..3f3fec039ca 100644 --- a/src/sage/manifolds/calculus_method.py +++ b/src/sage/manifolds/calculus_method.py @@ -65,7 +65,7 @@ def _SR_to_Sympy(expression): """ # Nothing to do if expression is already a SymPy object: - if type(expression) in sympy.core.all_classes: + if isinstance(expression, sympy.Basic): return expression return SR(expression)._sympy_() diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index f1a8cb3f58d..07e3fa94aee 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -1649,8 +1649,10 @@ def _init_coordinates(self, coord_list): coord_symb = coord_properties[0].strip() # the coordinate symbol # default values, possibly redefined below: coord_latex = None - xmin = -Infinity; xmin_included = False - xmax = +Infinity; xmax_included = False + xmin = -Infinity + xmin_included = False + xmax = +Infinity + xmax_included = False # scan of the properties other than the symbol: is_periodic = False for prop in coord_properties[1:]: @@ -2912,7 +2914,7 @@ def _plot_xx_list(xx_list, rem_coords, ranges, steps, number_values): first_invalid = False # next invalid point will not # be the first one xc += dx - if curve != []: + if curve: resu += line(curve, color=color_c, linestyle=style_c, thickness=thickness_c) diff --git a/src/sage/manifolds/chart_func.py b/src/sage/manifolds/chart_func.py index 9874cbdc9ca..231e3d3f70e 100644 --- a/src/sage/manifolds/chart_func.py +++ b/src/sage/manifolds/chart_func.py @@ -178,6 +178,14 @@ class ChartFunction(AlgebraElement): sage: f == h True + A coercion by means of the restriction is implemented:: + + sage: D = M.open_subset('D') + sage: X_D = X.restrict(D, x^2+y^2<1) # open disk + sage: c = X_D.function(x^2) + sage: c + f + 2*x^2 + 3*y + 1 + Expansion to a given order with respect to a small parameter:: sage: t = var('t') # the small parameter @@ -855,6 +863,53 @@ def is_trivial_zero(self): curr = self._calc_method._current return self._calc_method.is_trivial_zero(self.expr(curr)) + def is_trivial_one(self): + r""" + Check if ``self`` is trivially equal to one without any + simplification. + + This method is supposed to be fast as compared with + ``self == 1`` and is intended to be used in library code where + trying to obtain a mathematically correct result by applying + potentially expensive rewrite rules is not desirable. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: f = X.function(1) + sage: f.is_trivial_one() + True + sage: f = X.function(float(1.0)) + sage: f.is_trivial_one() + True + sage: f = X.function(x-x+1) + sage: f.is_trivial_one() + True + sage: X.one_function().is_trivial_one() + True + + No simplification is attempted, so that ``False`` is returned for + non-trivial cases:: + + sage: f = X.function(cos(x)^2 + sin(x)^2) + sage: f.is_trivial_one() + False + + On the contrary, the method + :meth:`~sage.structure.element.Element.is_zero` and the direct + comparison to one involve some simplification algorithms and + return ``True``:: + + sage: (f - 1).is_zero() + True + sage: f == 1 + True + + """ + curr = self._calc_method._current + return self._calc_method.is_trivial_zero(self.expr(curr) - SR.one()) + # TODO: Remove this method as soon as ticket #28629 is solved? def is_unit(self): r""" @@ -912,7 +967,7 @@ def copy(self): resu._order = self._order return resu - def diff(self, coord): + def derivative(self, coord): r""" Partial derivative with respect to a coordinate. @@ -937,28 +992,38 @@ def diff(self, coord): sage: X. = M.chart(calc_method='SR') sage: f = X.function(x^2+3*y+1); f x^2 + 3*y + 1 - sage: f.diff(x) + sage: f.derivative(x) 2*x - sage: f.diff(y) + sage: f.derivative(y) 3 - Each partial derivatives is itself a chart function:: + An alias is ``diff``:: + + sage: f.diff(x) + 2*x + + Each partial derivative is itself a chart function:: sage: type(f.diff(x)) + The same result is returned by the function ``diff``:: + + sage: diff(f, x) + 2*x + An index can be used instead of the coordinate symbol:: sage: f.diff(0) 2*x - sage: f.diff(1) + sage: diff(f, 1) 3 The index range depends on the convention used on the chart's domain:: sage: M = Manifold(2, 'M', structure='topological', start_index=1) - sage: X. = M.chart(calc_method='sympy') - sage: f = X.function(x**2+3*y+1) + sage: X. = M.chart() + sage: f = X.function(x^2+3*y+1) sage: f.diff(0) Traceback (most recent call last): ... @@ -1007,6 +1072,8 @@ def diff(self, coord): else: return self._der[self._chart[:].index(coord)] + diff = derivative + def __eq__(self, other): r""" Comparison (equality) operator. @@ -2573,7 +2640,6 @@ def collect_common_factors(self): self._del_derived() return self - class ChartFunctionRing(Parent, UniqueRepresentation): """ Ring of all chart functions on a chart. @@ -2583,7 +2649,9 @@ class ChartFunctionRing(Parent, UniqueRepresentation): - ``chart`` -- a coordinate chart, as an instance of class :class:`~sage.manifolds.chart.Chart` - EXAMPLES:: + EXAMPLES: + + The ring of all chart functions w.r.t. to a chart:: sage: M = Manifold(2, 'M', structure='topological') sage: X. = M.chart() @@ -2594,6 +2662,22 @@ class ChartFunctionRing(Parent, UniqueRepresentation): sage: FR.category() Category of commutative algebras over Symbolic Ring + Coercions by means of restrictions are implemented:: + + sage: FR_X = X.function_ring() + sage: D = M.open_subset('D') + sage: X_D = X.restrict(D, x^2+y^2<1) # open disk + sage: FR_X_D = X_D.function_ring() + sage: FR_X_D.has_coerce_map_from(FR_X) + True + + But only if the charts are compatible:: + + sage: Y. = D.chart() + sage: FR_Y = Y.function_ring() + sage: FR_Y.has_coerce_map_from(FR_X) + False + """ Element = ChartFunction @@ -2631,8 +2715,16 @@ def _element_constructor_(self, expression, calc_method=None): sin(x*y) sage: f.parent() is FR True + sage: D = M.open_subset('D') + sage: X_D = X.restrict(D, x^2+y^2<1) + sage: FR_D = X_D.function_ring() + sage: FR_D(f) + sin(x*y) """ + if isinstance(expression, ChartFunction): + if self._chart in expression._chart._subcharts: + expression = expression.expr(method=calc_method) return self.element_class(self, expression, calc_method=calc_method) def _coerce_map_from_(self, other): @@ -2644,13 +2736,20 @@ def _coerce_map_from_(self, other): sage: M = Manifold(2, 'M', structure='topological') sage: X. = M.chart() sage: FR = X.function_ring() - sage: FR._coerce_map_from_(RR) + sage: FR.has_coerce_map_from(RR) + True + sage: D = M.open_subset('D') + sage: X_D = X.restrict(D, x^2+y^2<1) + sage: FR_D = X_D.function_ring() + sage: FR_D.has_coerce_map_from(FR) True """ - from sage.rings.all import RR, ZZ, QQ - if other is SR or other is ZZ or other is RR or other is QQ: + if SR.has_coerce_map_from(other): return True + if isinstance(other, ChartFunctionRing): + if self._chart in other._chart._subcharts: + return True return False def _repr_(self): diff --git a/src/sage/manifolds/continuous_map.py b/src/sage/manifolds/continuous_map.py index ed74df31b2a..05f82dbd7db 100644 --- a/src/sage/manifolds/continuous_map.py +++ b/src/sage/manifolds/continuous_map.py @@ -631,7 +631,9 @@ def _call_(self, point): sage: M = Manifold(2, 'R^2', latex_name=r'\RR^2', structure='topological') # Euclidean plane sage: c_cart. = M.chart() # Cartesian coordinates - sage: # A pi/3 rotation around the origin defined in Cartesian coordinates: + + A pi/3 rotation around the origin defined in Cartesian coordinates:: + sage: rot = M.continuous_map(M, ((x - sqrt(3)*y)/2, (sqrt(3)*x + y)/2), ....: name='R') sage: p = M.point((1,2), name='p') @@ -959,6 +961,20 @@ def display(self, chart1=None, chart2=None): EXAMPLES: + A simple reparamentrization:: + + sage: R. = RealLine() + sage: I = R.open_interval(0, 2*pi) + sage: J = R.open_interval(2*pi, 6*pi) + sage: h = J.continuous_map(I, ((t-2*pi)/2,), name='h') + sage: h.display() + h: (2*pi, 6*pi) --> (0, 2*pi) + t |--> t = -pi + 1/2*t + sage: latex(h.display()) + \begin{array}{llcl} h:& \left(2 \, \pi, 6 \, \pi\right) & + \longrightarrow & \left(0, 2 \, \pi\right) \\ & t & \longmapsto & + t = -\pi + \frac{1}{2} \, t \end{array} + Standard embedding of the sphere `S^2` in `\RR^3`:: sage: M = Manifold(2, 'S^2', structure='topological') # the 2-dimensional sphere S^2 @@ -980,7 +996,7 @@ def display(self, chart1=None, chart2=None): Phi: S^2 --> R^3 on V: (u, v) |--> (X, Y, Z) = (2*u/(u^2 + v^2 + 1), 2*v/(u^2 + v^2 + 1), -(u^2 + v^2 - 1)/(u^2 + v^2 + 1)) - The LaTeX output:: + The LaTeX output of that embedding is:: sage: latex(Phi.display(c_xy, c_cart)) \begin{array}{llcl} \Phi:& S^2 & \longrightarrow & \RR^3 @@ -1055,45 +1071,39 @@ def _display_expression(self, chart1, chart2, result): r""" Helper function for :meth:`display`. """ - from sage.misc.latex import latex try: + # get coordinate expression coord_func = self.coord_functions(chart1, chart2) expression = coord_func.expr() - coords1 = chart1[:] - if len(coords1) == 1: - coords1 = coords1[0] - coords2 = chart2[:] - if len(coords2) == 1: - coords2 = coords2[0] - if chart1._domain == self._domain: - result._txt += " " - result._latex += " & " - else: - result._txt += "on " + chart1._domain._name + ": " - result._latex += r"\mbox{on}\ " + latex(chart1._domain) + \ - r": & " - result._txt += repr(coords1) + " |--> " - result._latex += latex(coords1) + r"& \longmapsto & " - if chart2 == chart1: - if len(expression) == 1: - result._txt += repr(expression[0]) + "\n" - result._latex += latex(coord_func[0]) + r"\\" - else: - result._txt += repr(expression) + "\n" - result._latex += latex(coord_func) + r"\\" - else: - if len(expression) == 1: - result._txt += repr(coords2[0]) + " = " + \ - repr(expression[0]) + "\n" - result._latex += latex(coords2[0]) + " = " + \ - latex(coord_func[0]) + r"\\" - else: - result._txt += repr(coords2) + " = " + \ - repr(expression) + "\n" - result._latex += latex(coords2) + " = " + \ - latex(coord_func) + r"\\" except (TypeError, ValueError): - pass + return + # if that succeeds, proceed: + coords1 = chart1[:] + if len(coords1) == 1: + coords1 = coords1[0] + coords2 = chart2[:] + if len(coords2) == 1: + coords2 = coords2[0] + if len(expression) == 1: + expression = expression[0] + coord_func = coord_func[0] + if chart1._domain == self._domain: + result._txt += " " + result._latex += " & " + else: + result._txt += "on " + chart1._domain._name + ": " + result._latex += r"\mbox{on}\ " + latex(chart1._domain) + \ + r": & " + result._txt += repr(coords1) + " |--> " + result._latex += latex(coords1) + r"& \longmapsto & " + if chart2 == chart1: + result._txt += repr(expression) + "\n" + result._latex += latex(coord_func) + r"\\" + else: + result._txt += repr(coords2) + " = " + \ + repr(expression) + "\n" + result._latex += latex(coords2) + " = " + \ + latex(coord_func) + r"\\" result = FormattedExpansion() if self._name is None: @@ -1450,7 +1460,9 @@ def set_expr(self, chart1, chart2, coord_functions): sage: U = M.open_subset('U', coord_def={c_xy: (y!=0, x<0)}) # the complement of the segment y=0 and x>0 sage: c_cart = c_xy.restrict(U) # Cartesian coordinates on U sage: c_spher. = U.chart(r'r:(0,+oo) ph:(0,2*pi):\phi') # spherical coordinates on U - sage: # Links between spherical coordinates and Cartesian ones: + + Links between spherical coordinates and Cartesian ones:: + sage: ch_cart_spher = c_cart.transition_map(c_spher, ....: [sqrt(x*x+y*y), atan2(y,x)]) sage: ch_cart_spher.set_inverse(r*cos(ph), r*sin(ph)) @@ -1872,7 +1884,9 @@ def __invert__(self): sage: M = Manifold(2, 'R^2', latex_name=r'\RR^2', structure='topological') sage: c_cart. = M.chart() - sage: # A pi/3 rotation around the origin: + + A pi/3 rotation around the origin:: + sage: rot = M.homeomorphism(M, ((x - sqrt(3)*y)/2, (sqrt(3)*x + y)/2), ....: name='R') sage: rot.inverse() diff --git a/src/sage/manifolds/differentiable/affine_connection.py b/src/sage/manifolds/differentiable/affine_connection.py index c312ed137d6..44a48b4d3bd 100644 --- a/src/sage/manifolds/differentiable/affine_connection.py +++ b/src/sage/manifolds/differentiable/affine_connection.py @@ -18,7 +18,7 @@ - [ONe1983]_ """ -#****************************************************************************** +# ***************************************************************************** # Copyright (C) 2015 Eric Gourgoulhon # Copyright (C) 2015 Michal Bejger # Copyright (C) 2015 Marco Mancini @@ -27,10 +27,11 @@ # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ -#****************************************************************************** +# ***************************************************************************** from sage.rings.integer import Integer from sage.structure.sage_object import SageObject +from sage.misc.cachefunc import cached_method from sage.manifolds.differentiable.manifold import DifferentiableManifold from sage.parallel.decorate import parallel from sage.parallel.parallelism import Parallelism @@ -250,7 +251,6 @@ class AffineConnection(SageObject): ....: for j in M.irange(): ....: for k in M.irange(): ....: nab.add_coef(eV)[i,j,k] = nab.coef(eVW)[i,j,k,c_uvW].expr() - ....: At this stage, the connection is fully defined on all the manifold:: @@ -301,7 +301,6 @@ class AffineConnection(SageObject): ....: for j in M.irange(): ....: for k in M.irange(): ....: nab.add_coef(eV)[i,j,k] = nab.coef(eVW)[i,j,k,c_uvW].expr() - ....: At this stage, the connection is fully defined on all the manifold:: @@ -332,6 +331,39 @@ class AffineConnection(SageObject): + (u**3/16 - u**2*v/16 - u**2/8 - u*v**2/16 + v**3/16 + v**2/8 - 1) d/dv*du + (-u**3/16 + u**2*v/16 - u**2/8 + u*v**2/16 - v**3/16 + v**2/8) d/dv*dv + To make affine connections hashable, they have to be set immutable before:: + + sage: nab.is_immutable() + False + sage: nab.set_immutable() + sage: nab.is_immutable() + True + + Immutable connections cannot be changed anymore:: + + sage: nab.set_coef(eU) + Traceback (most recent call last): + ... + AssertionError: the coefficients of an immutable element cannot be + changed + + However, they can now be used as keys for dictionaries:: + + sage: {nab: 1}[nab] + 1 + + The immutability process cannot be made undone. If a connection is + needed to be changed again, a copy has to be created:: + + sage: nab_copy = nab.copy('nablo'); nab_copy + Affine connection nablo on the 2-dimensional differentiable manifold M + sage: nab_copy is nab + False + sage: nab_copy == nab + True + sage: nab_copy.is_immutable() + False + """ def __init__(self, domain, name, latex_name=None): r""" @@ -354,6 +386,7 @@ def __init__(self, domain, name, latex_name=None): if not isinstance(domain, DifferentiableManifold): raise TypeError("the first argument must be a differentiable " + "manifold") + self._is_immutable = False self._domain = domain self._name = name if latex_name is None: @@ -363,7 +396,7 @@ def __init__(self, domain, name, latex_name=None): self._coefficients = {} # dict. of connection coefficients, with the # vector frames as keys # Initialization of derived quantities: - AffineConnection._init_derived(self) + self._init_derived() def _repr_(self): r""" @@ -428,7 +461,6 @@ def _init_derived(self): # (key: vector frame) self._curvature_forms = {} # dict. of dict. of curvature 2-forms # (key: vector frame) - self._hash = -1 def _del_derived(self): r""" @@ -749,6 +781,9 @@ class :class:`~sage.tensor.modules.comp.Components`; if such To keep them, use the method :meth:`add_coef` instead. """ + if self.is_immutable(): + raise AssertionError("the coefficients of an immutable element " + "cannot be changed") if frame is None: frame = self._domain._def_frame if frame not in self._coefficients: @@ -838,6 +873,9 @@ class :class:`~sage.tensor.modules.comp.Components`; if such """ + if self.is_immutable(): + raise AssertionError("the coefficients of an immutable element " + "cannot be changed") if frame is None: frame = self._domain._def_frame if frame not in self._coefficients: @@ -902,6 +940,127 @@ def del_other_coef(self, frame=None): for other_frame in to_be_deleted: del self._coefficients[other_frame] + def set_immutable(self): + r""" + Set ``self`` and all restrictions of ``self`` immutable. + + EXAMPLES: + + An affine connection can be set immutable:: + + sage: M = Manifold(2, 'M', start_index=1) + sage: X. = M.chart() + sage: U = M.open_subset('U', coord_def={X: x^2+y^2<1}) + sage: nab = M.affine_connection('nabla', latex_name=r'\nabla') + sage: eX = X.frame() + sage: nab.set_coef(eX)[1,2,1] = x*y + sage: nab.is_immutable() + False + sage: nab.set_immutable() + sage: nab.is_immutable() + True + + The coefficients of immutable elements cannot be changed:: + + sage: nab.add_coef(eX)[2,1,1] = x+y + Traceback (most recent call last): + ... + AssertionError: the coefficients of an immutable element cannot + be changed + + The restriction are set immutable as well:: + + sage: nabU = nab.restrict(U) + sage: nabU.is_immutable() + True + + """ + for rst in self._restrictions.values(): + rst.set_immutable() + self._is_immutable = True + + def is_immutable(self): + r""" + Return ``True`` if this object is immutable, i.e. its coefficients + cannot be chanced, and ``False`` if it is not. + + To set an affine connection immutable, use :meth:`set_immutable`. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', start_index=1) + sage: X. = M.chart() + sage: nab = M.affine_connection('nabla', latex_name=r'\nabla') + sage: nab.is_immutable() + False + sage: nab.set_immutable() + sage: nab.is_immutable() + True + + """ + return self._is_immutable + + def is_mutable(self): + r""" + Return ``True`` if this object is mutable, i.e. its coefficients can + be changed, and ``False`` if it is not. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', start_index=1) + sage: X. = M.chart() + sage: nab = M.affine_connection('nabla', latex_name=r'\nabla') + sage: nab.is_mutable() + True + sage: nab.set_immutable() + sage: nab.is_mutable() + False + + """ + return not self._is_immutable + + def copy(self, name, latex_name=None): + r""" + Return an exact copy of ``self``. + + INPUT: + + - ``name`` -- name given to the copy + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + copy; if none is provided, the LaTeX symbol is set to ``name`` + + .. NOTE:: + + The name and the derived quantities are not copied. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', start_index=1) + sage: X. = M.chart() + sage: nab = M.affine_connection('nabla', latex_name=r'\nabla') + sage: eX = X.frame() + sage: nab.set_coef(eX)[1,2,1] = x*y + sage: nab.set_coef(eX)[1,2,2] = x+y + sage: nab.display() + Gam^x_yx = x*y + Gam^x_yy = x + y + sage: nab_copy = nab.copy(name='nabla_1', latex_name=r'\nabla_1') + sage: nab is nab_copy + False + sage: nab == nab_copy + True + sage: nab_copy.display() + Gam^x_yx = x*y + Gam^x_yy = x + y + + """ + copy = type(self)(self._domain, name, latex_name=latex_name) + for dom, rst in self._restrictions.items(): + copy._restrictions[dom] = rst.copy(name, latex_name=latex_name) + for frame, coef in self._coefficients.items(): + copy._coefficients[frame] = coef.copy() + return copy + def __getitem__(self, args): r""" Return the connection coefficient w.r.t. some frame corresponding to @@ -1267,6 +1426,7 @@ def restrict(self, subdomain): resu._riemann = self._riemann.restrict(subdomain) if self._ricci is not None: resu._ricci = self._ricci.restrict(subdomain) + resu.set_immutable() # restrictions must be immutable, too self._restrictions[subdomain] = resu return self._restrictions[subdomain] @@ -1452,7 +1612,7 @@ def _derive_paral(self, tensor): # Component computation in the common frame: tc = tensor._components[frame] gam = self._coefficients[frame] - if tensor._sym == [] and tensor._antisym == []: + if not tensor._sym and not tensor._antisym: resc = Components(tdom.scalar_field_algebra(), frame, tensor._tensor_rank+1, start_index=self._domain._sindex, @@ -1635,7 +1795,6 @@ def torsion(self): ....: for j in M.irange(): ....: for k in M.irange(): ....: nab.add_coef(eV)[i,j,k] = nab.coef(eVW)[i,j,k,c_uvW].expr() - ....: sage: t = nab.torsion() ; t Tensor field of type (1,2) on the 2-dimensional differentiable manifold M @@ -1759,7 +1918,6 @@ def riemann(self): ....: for j in M.irange(): ....: for k in M.irange(): ....: nab.add_coef(eV)[i,j,k] = nab.coef(eVW)[i,j,k,c_uvW].expr() - ....: sage: r = nab.riemann() ; r Tensor field of type (1,3) on the 2-dimensional differentiable manifold M @@ -1790,7 +1948,6 @@ def riemann(self): ....: for j in M.irange(): ....: for k in M.irange(): ....: nab.add_coef(eV)[i,j,k] = nab.coef(eVW)[i,j,k,c_uvW].expr() - ....: sage: r = nab.riemann() ; r Tensor field of type (1,3) on the 2-dimensional differentiable manifold M @@ -1991,9 +2148,10 @@ def connection_form(self, i, j, frame=None): sage: nab.connection_form(1,1,e).comp(e)[:] [x*y^2*z, (x^2*y + 1)*z/y, -x*y*z] - Check of the formula `\omega^i_{\ \, j} = \Gamma^i_{\ \, jk} e^k`:: + Check of the formula `\omega^i_{\ \, j} = \Gamma^i_{\ \, jk} e^k`: + + First on the manifold's default frame (d/dx, d/dy, d:dz):: - sage: #... on the manifold's default frame (d/dx, d/dy, d:dz) sage: dx = M.default_frame().coframe() ; dx Coordinate coframe (M, (dx,dy,dz)) sage: check = [] @@ -2001,10 +2159,11 @@ def connection_form(self, i, j, frame=None): ....: for j in M.irange(): ....: check.append( nab.connection_form(i,j) == \ ....: sum( nab[[i,j,k]]*dx[k] for k in M.irange() ) ) - ....: sage: check [True, True, True, True, True, True, True, True, True] - sage: #... on the frame e + + Then on the frame e:: + sage: ef = e.coframe() ; ef Coframe (M, (e^1,e^2,e^3)) sage: check = [] @@ -2012,7 +2171,6 @@ def connection_form(self, i, j, frame=None): ....: for j in M.irange(): ....: s = nab.connection_form(i,j,e).comp(c_xyz.frame(), from_basis=e) ....: check.append( nab.connection_form(i,j,e) == sum( nab.coef(e)[[i,j,k]]*ef[k] for k in M.irange() ) ) - ....: sage: check [True, True, True, True, True, True, True, True, True] @@ -2135,11 +2293,9 @@ def torsion_form(self, i, frame=None): sage: for i in M.irange(): # long time ....: nab.torsion_form(i, e) == ef[i].exterior_derivative() + \ ....: sum(nab.connection_form(i,j,e).wedge(ef[j]) for j in M.irange()) - ....: True True True - """ if frame is None: frame = self._domain._def_frame @@ -2245,7 +2401,6 @@ def curvature_form(self, i, j, frame=None): ....: check.append( nab.curvature_form(i,j,e) == \ ....: omega(i,j,e).exterior_derivative() + \ ....: sum( omega(i,k,e).wedge(omega(k,j,e)) for k in M.irange()) ) - ....: sage: check # long time [True, True, True, True, True, True, True, True, True] @@ -2323,24 +2478,44 @@ def set_calc_order(self, symbol, order, truncate=False): coef[ind].simplify() self._del_derived() + @cached_method def __hash__(self): r""" Hash function. TESTS:: - sage: M = Manifold(2, 'M') + sage: M = Manifold(2, 'M', start_index=1) sage: X. = M.chart() - sage: nab = M.affine_connection('nabla', latex_name=r'\nabla') - sage: hash(nab) == nab.__hash__() + sage: eX = X.frame() + sage: nab1 = M.affine_connection('nabla1', latex_name=r'\nabla_1') + sage: nab1.set_coef(eX)[1,2,1] = x*y + sage: nab2 = M.affine_connection('nabla2', latex_name=r'\nabla_2') + sage: nab2.set_coef(eX)[1,2,1] = x*y + sage: nab1.set_immutable(); nab2.set_immutable() + sage: nab1 == nab2 + True + sage: hash(nab1) == hash(nab2) True - Let us check that ``nab`` can be used as a dictionary key:: + Let us check that affine connections can be used as dictionary keys:: - sage: {nab: 1}[nab] + sage: M = Manifold(2, 'M', start_index=1) + sage: X. = M.chart() + sage: eX = X.frame() + sage: nab1 = M.affine_connection('nabla1', latex_name=r'\nabla_1') + sage: nab1.set_coef(eX)[1,2,1] = x*y + sage: nab2 = M.affine_connection('nabla2', latex_name=r'\nabla_2') + sage: nab2.set_coef(eX)[1,2,1] = x^2 + sage: nab1.set_immutable(); nab2.set_immutable() + sage: d = {nab1: 1, nab2: 2} + sage: d[nab1] 1 + sage: d[nab2] + 2 """ - if self._hash == -1: - self._hash = hash(repr(self)) - return self._hash + if self.is_mutable(): + raise ValueError('element must be immutable in order to be ' + 'hashable') + return hash((type(self).__name__, self._domain)) diff --git a/src/sage/manifolds/differentiable/automorphismfield.py b/src/sage/manifolds/differentiable/automorphismfield.py index 0ee55d75c8c..b7f2584d008 100644 --- a/src/sage/manifolds/differentiable/automorphismfield.py +++ b/src/sage/manifolds/differentiable/automorphismfield.py @@ -209,7 +209,7 @@ def _repr_(self): """ description = "Field of tangent-space " - if self._is_identity: + if self is self.parent().one(): description += "identity maps " else: description += "automorphisms " @@ -304,20 +304,20 @@ class :class:`~sage.tensor.modules.comp.Components`; if such ValueError: no basis could be found for computing the components in the Coordinate frame (V, (d/du,d/dv)) - Since the identity map is a special element, its components cannot be - changed:: + Since the identity map is an immutable element, its components + cannot be changed:: sage: id = M.tangent_identity_field() sage: id.add_comp(e)[0,1] = u*v Traceback (most recent call last): ... - AssertionError: the components of the identity map cannot be changed + AssertionError: the components of an immutable element cannot be + changed """ - if self._is_identity: - raise AssertionError("the components of the identity map cannot be " - "changed") - return TensorField._set_comp_unsafe(self, basis=basis) + comp = super().set_comp(basis=basis) + self._is_identity = False # a priori + return comp def add_comp(self, basis=None): r""" @@ -384,13 +384,13 @@ class :class:`~sage.tensor.modules.comp.Components`; sage: id.add_comp(e)[0,1] = u*v Traceback (most recent call last): ... - AssertionError: the components of the identity map cannot be changed + AssertionError: the components of an immutable element cannot be + changed """ - if self._is_identity: - raise AssertionError("the components of the identity map cannot be " - "changed") - return TensorField._add_comp_unsafe(self, basis=basis) + comp = super().add_comp(basis=basis) + self._is_identity = False # a priori + return comp def _new_instance(self): r""" @@ -526,6 +526,41 @@ def __call__(self, *arg): # Case of 2 arguments: return TensorField.__call__(self, *arg) + def copy(self, name=None, latex_name=None): + r""" + Return an exact copy of the automorphism field ``self``. + + INPUT: + + - ``name`` -- (default: ``None``) name given to the copy + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + copy; if none is provided, the LaTeX symbol is set to ``name`` + + .. NOTE:: + + The name and the derived quantities are not copied. + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: U = M.open_subset('U') ; V = M.open_subset('V') + sage: M.declare_union(U,V) # M is the union of U and V + sage: c_xy. = U.chart() ; c_uv. = V.chart() + sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y), + ....: intersection_name='W', restrictions1= x>0, + ....: restrictions2= u+v>0) + sage: uv_to_xy = xy_to_uv.inverse() + sage: Id = M.tangent_identity_field(); Id + Field of tangent-space identity maps on the 2-dimensional + differentiable manifold M + sage: one = Id.copy('1'); one + Field of tangent-space automorphisms 1 on the 2-dimensional + differentiable manifold M + + """ + copy = super().copy(name=name, latex_name=latex_name) + copy._is_identity = self._is_identity + return copy #### MultiplicativeGroupElement methods #### @@ -638,7 +673,7 @@ def _mul_(self, other): OUTPUT: - the automorphism resulting from the composition of ``other`` and - ``self`` + ``self`` TESTS:: @@ -868,9 +903,9 @@ def restrict(self, subdomain, dest_map=None): if subdomain == self._domain: return self if subdomain not in self._restrictions: - if not self._is_identity: + if self is not self.parent().one(): return TensorField.restrict(self, subdomain, dest_map=dest_map) - # Special case of the identity map: + # Special case of the immutable identity map: if not subdomain.is_subset(self._domain): raise ValueError("the provided domain is not a subset of " + "the field's domain") @@ -1035,7 +1070,7 @@ def _repr_(self): """ description = "Field of tangent-space " - if self._is_identity: + if self is self.parent().one(): description += "identity maps " else: description += "automorphisms " diff --git a/src/sage/manifolds/differentiable/automorphismfield_group.py b/src/sage/manifolds/differentiable/automorphismfield_group.py index 4cc248e67dd..d9a0245d67a 100644 --- a/src/sage/manifolds/differentiable/automorphismfield_group.py +++ b/src/sage/manifolds/differentiable/automorphismfield_group.py @@ -208,12 +208,18 @@ def _element_constructor_(self, comp=[], frame=None, name=None, a = (x^2 + 1) d/dx*dx + (y^2 + 1) d/dy*dy """ - if comp == 1: + if hasattr(comp, 'is_trivial_zero'): + if (comp - 1).is_trivial_zero(): + return self.one() + elif comp == 1: return self.one() + if not isinstance(comp, (list, tuple)): + raise TypeError("cannot convert the {} ".format(comp) + + "to an element of {}".format(self)) # standard construction resu = self.element_class(self._vmodule, name=name, latex_name=latex_name) - if comp != []: + if comp: resu.set_comp(frame)[:] = comp return resu @@ -308,8 +314,9 @@ def one(self): if dom.is_manifestly_parallelizable(): fmodule = dom.vector_field_module() resu._restrictions[dom] = fmodule.identity_map(name='Id', - latex_name=r'\mathrm{Id}') + latex_name=r'\mathrm{Id}') resu._is_identity = True + resu.set_immutable() return resu #### End of monoid methods #### diff --git a/src/sage/manifolds/differentiable/bundle_connection.py b/src/sage/manifolds/differentiable/bundle_connection.py index eaf60146e13..7d4f7980bf6 100644 --- a/src/sage/manifolds/differentiable/bundle_connection.py +++ b/src/sage/manifolds/differentiable/bundle_connection.py @@ -30,17 +30,20 @@ - Michael Jung (2019) : initial version """ -#****************************************************************************** +# ****************************************************************************** # Copyright (C) 2019 Michael Jung # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ -#****************************************************************************** +# ****************************************************************************** from sage.structure.sage_object import SageObject -from sage.manifolds.differentiable.vector_bundle import DifferentiableVectorBundle +from sage.rings.integer import Integer +from sage.manifolds.differentiable.vector_bundle import \ + DifferentiableVectorBundle + class BundleConnection(SageObject): r""" @@ -58,8 +61,8 @@ class BundleConnection(SageObject): EXAMPLES: - Define a bundle connection on a rank 2 vector bundle over some 3-dimensional - smooth manifold:: + Define a bundle connection on a rank 2 vector bundle over some + 3-dimensional smooth manifold:: sage: M = Manifold(3, 'M', start_index=1) sage: X. = M.chart() @@ -69,41 +72,102 @@ class BundleConnection(SageObject): Bundle connection nabla on the Differentiable real vector bundle E -> M of rank 2 over the base space 3-dimensional differentiable manifold M - The bundle connection is specified by the connection 1-forms:: + First, let us initialize all connection 1-forms w.r.t. the frame ``e`` to + zero:: + + sage: nab[e, :] = [[0, 0], [0, 0]] + + This line can be shortened by the following:: + + sage: nab[e, :] = 0 # initialize to zero + + Now, we want to specify some non-zero entries:: + + sage: nab[e, 1, 2] = [x*z, y*z, z^2] + sage: nab[e, 2, 1] = [x, x^2, x^3] + sage: nab[e, 1, 2].display() + connection (1,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x*z dx + y*z dy + z^2 dz + sage: nab[e, 2, 1].display() + connection (2,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x dx + x^2 dy + x^3 dz - sage: a = M.one_form([x*z, y*z, z^2], name='a') - sage: b = M.one_form([x, x^2, x^3], name='b') - sage: nab.set_connection_form(1, 2, a) - sage: nab.set_connection_form(1, 1, b) + The other entries remain zero:: - From this, the connection 2-forms can be derived:: + sage: nab[e, 1, 1].display() + connection (1,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = 0 + sage: nab[e, 2, 2].display() + connection (2,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = 0 + + Notice, when we omit the frame, the default frame of the vector bundle is + assumed (in this case ``e``):: + + sage: nab[2, 2].display() + connection (2,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = 0 + The same holds for the assignment:: + + sage: nab[:] = 0 + sage: nab[e, 1, 2].display() + connection (1,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = 0 + + We can also use :meth:`set_connection_form` to specify the connection + 1-forms:: + + sage: nab[:] = 0 # re-initialize to zero + sage: nab.set_connection_form(1, 2)[:] = [x*z, y*z, z^2] + sage: nab.set_connection_form(2, 1)[:] = [x, x^2, x^3] + sage: nab[1, 2].display() + connection (1,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x*z dx + y*z dy + z^2 dz + sage: nab[2, 1].display() + connection (2,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x dx + x^2 dy + x^3 dz + + .. NOTE:: + + Notice that list assignments and :meth:`set_connection_form` delete + the connection 1-forms w.r.t. other frames for consistency reasons. To + avoid this behavior, :meth:`add_connection_form` must be used instead. + + After the connection has been specified, the curvature 2-forms can be + derived:: + + sage: Omega = nab.curvature_form sage: for i in E.irange(): ....: for j in E.irange(): - ....: print(nab.curvature_form(i ,j).display()) + ....: print(Omega(i ,j, e).display()) curvature (1,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = -(x^3 - x*y)*z dx/\dy + (-x^4*z + x*z^2) dx/\dz + + (-x^3*y*z + x^2*z^2) dy/\dz + curvature (1,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = -x dx/\dz - y dy/\dz + curvature (2,1) of bundle connection nabla w.r.t. Local frame (E|_M, (e_1,e_2)) = 2*x dx/\dy + 3*x^2 dx/\dz - curvature (1,2) of bundle connection nabla w.r.t. Local frame - (E|_M, (e_1,e_2)) = (x^3 - x*y)*z dx/\dy + (x^4*z - x*z^2 - x) dx/\dz + - (x^3*y*z - x^2*z^2 - y) dy/\dz - curvature (2,1) of bundle connection nabla w.r.t. Local frame - (E|_M, (e_1,e_2)) = 0 - curvature (2,2) of bundle connection nabla w.r.t. Local frame - (E|_M, (e_1,e_2)) = 0 + curvature (2,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = (x^3 - x*y)*z dx/\dy + (x^4*z - x*z^2) dx/\dz + + (x^3*y*z - x^2*z^2) dy/\dz - They certainly obey the structure equation:: + The derived forms certainly obey the structure equations, see + :meth:`curvature_form` for details:: sage: omega = nab.connection_form sage: check = [] sage: for i in E.irange(): # long time ....: for j in E.irange(): - ....: check.append(nab.curvature_form(i,j,e) == \ + ....: check.append(Omega(i,j,e) == \ ....: omega(i,j,e).exterior_derivative() + \ - ....: sum(omega(k,j,e).wedge(omega(i,k,e)) for k in E.irange())) + ....: sum(omega(k,j,e).wedge(omega(i,k,e)) + ....: for k in E.irange())) sage: check # long time [True, True, True, True] """ + def __init__(self, vbundle, name, latex_name=None): r""" Construct a bundle connection. @@ -112,7 +176,8 @@ def __init__(self, vbundle, name, latex_name=None): sage: M = Manifold(3, 'M') sage: E = M.vector_bundle(2, 'E') - sage: from sage.manifolds.differentiable.bundle_connection import BundleConnection + sage: from sage.manifolds.differentiable.bundle_connection \ + ....: import BundleConnection sage: nab = BundleConnection(E, 'nabla', latex_name=r'\nabla') sage: nab Bundle connection nabla on the Differentiable real vector bundle @@ -120,8 +185,8 @@ def __init__(self, vbundle, name, latex_name=None): manifold M sage: X. = M.chart() sage: e = E.local_frame('e') - sage: a = M.one_form([x*z, y*z, z^2], name='a') - sage: nab.set_connection_form(1, 0, a) + sage: nab[:] = 0 + sage: nab.set_connection_form(1, 0)[:] = [x*z, y*z, z^2] sage: TestSuite(nab).run() """ @@ -135,8 +200,7 @@ def __init__(self, vbundle, name, latex_name=None): self._latex_name = self._name else: self._latex_name = latex_name - self._connection_forms = {} # dict. of connection coefficients, with - # the local frames as keys + self._connection_forms = {} # dict. of con. forms, with frames as keys self._coefficients = self._connection_forms # Initialization of derived quantities: self._init_derived() @@ -201,7 +265,7 @@ def _init_derived(self): """ self._curvature_forms = {} # dict. of dict. of curvature forms - # (key: local frame) + # (key: local frame) self._hash = -1 def _del_derived(self): @@ -235,21 +299,21 @@ def __eq__(self, other): sage: M = Manifold(2, 'M') sage: X. = M.chart() sage: E = M.vector_bundle(2, 'E') - sage: e = E.local_frame('e') # standard frame for E + sage: e = E.local_frame('e') # standard frame for E sage: nab = E.bundle_connection('nabla', latex_name=r'\nabla') - sage: a = M.one_form([x^2, x]) - sage: b = M.one_form([y^2, y]) - sage: nab.set_connection_form(0, 1, a) - sage: nab.set_connection_form(1, 0, b) + sage: nab[:] = 0 + sage: nab[0, 1][:] = [x^2, x] + sage: nab[1, 0][:] = [y^2, y] sage: nab1 = E.bundle_connection('nabla', latex_name=r'\nabla') + sage: nab1[:] = 0 sage: (nab1 == nab) or (nab == nab1) False - sage: nab1.set_connection_form(1, 0, a) - sage: nab1.set_connection_form(0, 1, b) + sage: nab1[0, 1][:] = [x, x^2] + sage: nab1[1, 0][:] = [y, y^2] sage: (nab1 == nab) or (nab == nab1) False - sage: nab1.set_connection_form(0, 1, a) - sage: nab1.set_connection_form(1, 0, b) + sage: nab1[0, 1][:] = [x^2, x] + sage: nab1[1, 0][:] = [y^2, y] sage: (nab1 == nab) and (nab == nab1) True @@ -289,21 +353,21 @@ def __ne__(self, other): sage: M = Manifold(2, 'M') sage: X. = M.chart() sage: E = M.vector_bundle(2, 'E') - sage: e = E.local_frame('e') + sage: e = E.local_frame('e') # standard frame for E sage: nab = E.bundle_connection('nabla', latex_name=r'\nabla') - sage: a = M.one_form([x^2, x]) - sage: b = M.one_form([y^2, y]) - sage: nab.set_connection_form(0, 1, a) - sage: nab.set_connection_form(1, 0, b) + sage: nab[:] = 0 + sage: nab[0, 1][:] = [x^2, x] + sage: nab[1, 0][:] = [y^2, y] sage: nab1 = E.bundle_connection('nabla', latex_name=r'\nabla') + sage: nab1[:] = 0 sage: (nab1 != nab) and (nab != nab1) True - sage: nab1.set_connection_form(1, 0, a) - sage: nab1.set_connection_form(0, 1, b) + sage: nab1[0, 1][:] = [x, x^2] + sage: nab1[1, 0][:] = [y, y^2] sage: (nab1 != nab) and (nab != nab1) True - sage: nab1.set_connection_form(0, 1, a) - sage: nab1.set_connection_form(1, 0, b) + sage: nab1[0, 1][:] = [x^2, x] + sage: nab1[1, 0][:] = [y^2, y] sage: (nab1 != nab) or (nab != nab1) False @@ -344,18 +408,32 @@ def _new_forms(self, frame): sage: E = M.vector_bundle(2, 'E') sage: e = E.local_frame('e') sage: nab = E.bundle_connection('nabla', latex_name=r'\nabla') - sage: nab._new_forms(e) # random - {(1, 1): 1-form zero on the 2-dimensional differentiable manifold M, - (1, 2): 1-form zero on the 2-dimensional differentiable manifold M, - (2, 1): 1-form zero on the 2-dimensional differentiable manifold M, - (2, 2): 1-form zero on the 2-dimensional differentiable manifold M} + sage: forms = nab._new_forms(e) + sage: [forms[k] for k in sorted(forms)] + [1-form connection (1,1) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M, + 1-form connection (1,2) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M, + 1-form connection (2,1) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M, + 1-form connection (2,2) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M] """ dom = frame._domain forms_dict = {} for i in self._vbundle.irange(): for j in self._vbundle.irange(): - forms_dict[(i,j)] = dom.diff_form_module(1).zero() + # set names: + name = "connection ({},{}) of bundle ".format(i, j) + name += "connection " + self._name + " w.r.t. {}".format(frame) + latex_name = r"\omega^" + str(j) + r"_{\ \, " + str(i) + "}" + form = dom.diff_form(1, name=name, latex_name=latex_name) + forms_dict[(i, j)] = form return forms_dict def connection_forms(self, frame=None): @@ -372,6 +450,9 @@ def connection_forms(self, frame=None): consisting of one forms is called *connection matrix of* `\nabla` *with respect to* `e`. + If the connection coefficients are not known already, they are computed + from the above formula. + INPUT: - ``frame`` -- (default: ``None``) local frame relative to which the @@ -380,30 +461,36 @@ def connection_forms(self, frame=None): OUTPUT: - - connection forms relative to the frame ``frame``, as a dictionary with - tuples `(i, j)` as key and one forms as instances of + - connection forms relative to the frame ``frame``, as a dictionary + with tuples `(i, j)` as key and one forms as instances of :class:`~sage.manifolds.differentiable.diff_form` as value representing the matrix entries. EXAMPLES: - Connection forms of a bundle connection on a rank 2 vector bundle over a - 3-dimensional manifold:: + Connection forms of a bundle connection on a rank 2 vector bundle + over a 3-dimensional manifold:: sage: M = Manifold(3, 'M', start_index=1) sage: c_xyz. = M.chart() sage: E = M.vector_bundle(2, 'E') sage: e = E.local_frame('e') sage: nab = E.bundle_connection('nabla', r'\nabla') - sage: a = M.one_form([x^2, z, x], name='a') - sage: b = M.one_form([y^2, z, y], name='b') - sage: nab.set_connection_form(1, 1, a) - sage: nab.set_connection_form(2, 2, b) - sage: nab.connection_forms() # random - {(1, 1): 1-form a on the 3-dimensional differentiable manifold M, - (1, 2): 1-form zero on the 3-dimensional differentiable manifold M, - (2, 1): 1-form zero on the 3-dimensional differentiable manifold M, - (2, 2): 1-form b on the 3-dimensional differentiable manifold M} + sage: nab[:] = 0 # initialize curvature forms + sage: forms = nab.connection_forms() + sage: [forms[k] for k in sorted(forms)] + [1-form connection (1,1) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 3-dimensional differentiable + manifold M, + 1-form connection (1,2) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 3-dimensional differentiable + manifold M, + 1-form connection (2,1) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 3-dimensional differentiable + manifold M, + 1-form connection (2,2) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 3-dimensional differentiable + manifold M] """ if frame is None: @@ -426,8 +513,7 @@ def connection_forms(self, frame=None): break else: # TODO: Compute coefficients out of known ones - self._connection_forms[frame] = self._new_forms(frame) - + pass return self._connection_forms[frame] def connection_form(self, i, j, frame=None): @@ -435,16 +521,20 @@ def connection_form(self, i, j, frame=None): Return the connection 1-form corresponding to the given index and local frame. + .. SEEALSO:: + + Consult :meth:`connection_forms` for detailed information. + INPUT: - - ``i``, ``j`` -- indices identifying the 1-form `\omega^i_j` + - ``i``, ``j`` -- indices identifying the 1-form `\omega^j_i` - ``frame`` -- (default: ``None``) local frame relative to which the connection 1-forms are defined; if ``None``, the default frame of the vector bundle's corresponding section module is assumed. OUTPUT: - - the 1-form `\omega^i_{\ \, j}`, as an instance of + - the 1-form `\omega^j_i`, as an instance of :class:`~sage.manifolds.differentiable.diff_form.DiffForm` EXAMPLES:: @@ -454,22 +544,22 @@ def connection_form(self, i, j, frame=None): sage: E = M.vector_bundle(2, 'E') sage: e = E.local_frame('e') # standard frame for E sage: nab = E.bundle_connection('nabla', latex_name=r'\nabla') - sage: a = M.one_form([x^2, x], name='a') - sage: b = M.one_form([y^2, y], name='b') - sage: nab.set_connection_form(0, 1, a) - sage: nab.set_connection_form(1, 0, b) - sage: nab.connection_form(0, 1) - 1-form a on the 2-dimensional differentiable manifold M - sage: nab.connection_form(0, 0) - 1-form zero on the 2-dimensional differentiable manifold M + sage: nab.set_connection_form(0, 1)[:] = [x^2, x] + sage: nab.set_connection_form(1, 0)[:] = [y^2, y] + sage: nab.connection_form(0, 1).display() + connection (0,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_0,e_1)) = x^2 dx + x dy + sage: nab.connection_form(1, 0).display() + connection (1,0) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_0,e_1)) = y^2 dx + y dy """ - return self.connection_forms(frame)[(i,j)] + return self.connection_forms(frame)[(i, j)] - def add_connection_form(self, i, j, form, frame=None): + def add_connection_form(self, i, j, form=None, frame=None): r""" - Assign the connection 1-form corresponding to the given index and - local frame. + Return the connection form `\omega^j_i` in a given frame for + assignment. See method :meth:`connection_forms` for details about the definition of the connection forms. @@ -479,10 +569,10 @@ def add_connection_form(self, i, j, form, frame=None): INPUT: - - ``i``, ``j`` -- indices identifying the 1-form `\omega^i_j` + - ``i``, ``j`` -- indices identifying the 1-form `\omega^j_i` - ``frame`` -- (default: ``None``) local frame in which the connection - 1-form is defined; if ``None``, the default frame of the vector bundle - is assumed. + 1-form is defined; if ``None``, the default frame of the vector + bundle is assumed. .. WARNING:: @@ -492,10 +582,10 @@ def add_connection_form(self, i, j, form, frame=None): OUTPUT: - - connection 1-form `\omega^i_j` in the given frame, as an instance of + - connection 1-form `\omega^j_i` in the given frame, as an instance of the class :class:`~sage.manifolds.differentiable.diff_form.DiffForm`; - if such connection 1-form did not exist previously, it is created. See - method :meth:`connection_forms` for the storage convention of the + if such connection 1-form did not exist previously, it is created. + See method :meth:`connection_forms` for the storage convention of the connection 1-forms. EXAMPLES:: @@ -505,30 +595,32 @@ def add_connection_form(self, i, j, form, frame=None): sage: E = M.vector_bundle(2, 'E') sage: e = E.local_frame('e') # standard frame for E sage: nab = E.bundle_connection('nabla', latex_name=r'\nabla') - sage: a = M.one_form([x^2, x], name='a') - sage: b = M.one_form([y^2, y], name='b') - sage: nab.add_connection_form(0, 1, a, frame=e) - sage: nab.connection_form(0, 1) - 1-form a on the 2-dimensional differentiable manifold M + sage: nab.add_connection_form(0, 1, frame=e)[:] = [x^2, x] + sage: nab[e, 0, 1].display() + connection (0,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_0,e_1)) = x^2 dx + x dy Since ``e`` is the vector bundle's default local frame, its mention may be omitted:: - sage: nab.add_connection_form(1, 0, b) - sage: nab.connection_form(1, 0) - 1-form b on the 2-dimensional differentiable manifold M + sage: nab.add_connection_form(1, 0)[:] = [y^2, y] + sage: nab[1, 0].display() + connection (1,0) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_0,e_1)) = y^2 dx + y dy Adding connection 1-forms w.r.t. to another local frame:: sage: f = E.local_frame('f') - sage: nab.add_connection_form(1, 1, a+b, frame=f) - sage: nab.connection_form(1, 1, frame=f) - 1-form a+b on the 2-dimensional differentiable manifold M + sage: nab.add_connection_form(1, 1, frame=f)[:] = [x, y] + sage: nab[f, 1, 1].display() + connection (1,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (f_0,f_1)) = x dx + y dy The forms w.r.t. the frame ``e`` have been kept:: - sage: nab.connection_form(0, 1, frame=e) - 1-form a on the 2-dimensional differentiable manifold M + sage: nab[e, 0, 1].display() + connection (0,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_0,e_1)) = x^2 dx + x dy To delete them, use the method :meth:`set_connection_form` instead. @@ -538,25 +630,26 @@ def add_connection_form(self, i, j, form, frame=None): frame = smodule.default_frame() if frame is None: raise ValueError("a frame must be provided!") - ### - # Certainly, the form must be a differential form, otherwise try to - # convert: - dom = frame._domain - dmodule = dom.diff_form_module(1) - form = dmodule(form) - ### # Are the components already known? if frame not in self._connection_forms: if frame not in self._vbundle._frames: raise ValueError("the {} is not".format(frame) + " a frame on the {}".format(self._base_space)) self._connection_forms[frame] = self._new_forms(frame) - self._del_derived() # deletes the derived quantities - self._connection_forms[frame][(i,j)] = form - - def set_connection_form(self, i, j, form, frame=None): + self._del_derived() # deletes the derived quantities + if form: + # TODO: Remove input `form` in Sage 9.3 + from sage.misc.superseded import deprecation + msg = "the input 'form' is outdated and will be removed in a " + msg += "future version of Sage" + deprecation(30208, msg) + self._connection_forms[frame][(i, j)] = form.copy() + return self._connection_forms[frame][(i, j)] + + def set_connection_form(self, i, j, form=None, frame=None): r""" - Return the connection coefficients in a given frame for assignment. + Return the connection form `\omega^j_i` in a given frame for + assignment. See method :meth:`connection_forms` for details about the definition of the connection forms. @@ -567,10 +660,18 @@ def set_connection_form(self, i, j, form, frame=None): INPUT: - - ``i``, ``j`` -- indices identifying the 1-form `\omega^i_j` + - ``i``, ``j`` -- indices identifying the 1-form `\omega^j_i` - ``frame`` -- (default: ``None``) local frame in which the connection - 1-form is defined; if ``None``, the default frame of the vector bundle - is assumed. + 1-form is defined; if ``None``, the default frame of the vector + bundle is assumed. + + OUTPUT: + + - connection 1-form `\omega^j_i` in the given frame, as an instance of + the class :class:`~sage.manifolds.differentiable.diff_form.DiffForm`; + if such connection 1-form did not exist previously, it is created. + See method :meth:`connection_forms` for the storage convention of the + connection 1-forms. EXAMPLES: @@ -582,36 +683,46 @@ def set_connection_form(self, i, j, form, frame=None): sage: E = M.vector_bundle(2, 'E') sage: e = E.local_frame('e') # standard frame for E sage: nab = E.bundle_connection('nabla', latex_name=r'\nabla') - sage: a = M.one_form([x^2, x], name='a') - sage: b = M.one_form([y^2, y], name='b') - sage: nab.set_connection_form(0, 1, a, frame=e) - sage: nab.connection_form(0, 1) - 1-form a on the 2-dimensional differentiable manifold M + sage: nab.set_connection_form(0, 1)[:] = [x^2, x] + sage: nab[0, 1].display() + connection (0,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_0,e_1)) = x^2 dx + x dy Since ``e`` is the vector bundle's default local frame, its mention may be omitted:: - sage: nab.set_connection_form(1, 0, b) - sage: nab.connection_form(1, 0) - 1-form b on the 2-dimensional differentiable manifold M + sage: nab.set_connection_form(1, 0)[:] = [y^2, y] + sage: nab[1, 0].display() + connection (1,0) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_0,e_1)) = y^2 dx + y dy Setting connection 1-forms w.r.t. to another local frame:: sage: f = E.local_frame('f') - sage: nab.set_connection_form(1, 1, a+b, frame=f) - sage: nab.connection_form(1, 1, frame=f) - 1-form a+b on the 2-dimensional differentiable manifold M + sage: nab.set_connection_form(1, 1, frame=f)[:] = [x, y] + sage: nab[f, 1, 1].display() + connection (1,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (f_0,f_1)) = x dx + y dy - The forms w.r.t. the frame ``e`` have been resetted:: + The forms w.r.t. the frame ``e`` have been deleted:: - sage: nab.connection_form(0, 1, frame=f) - 1-form zero on the 2-dimensional differentiable manifold M + sage: nab[e, 0, 1].display() + Traceback (most recent call last): + ... + KeyError: Local frame (E|_M, (e_0,e_1)) To keep them, use the method :meth:`add_connection_form` instead. """ - self.add_connection_form(i, j, form, frame=frame) + if form: + # TODO: Remove input `form` in Sage 9.3 + from sage.misc.superseded import deprecation + msg = "the input 'form' is outdated and will be removed in a " + msg += "future version of Sage" + deprecation(30208, msg) + omega = self.add_connection_form(i, j, form=form, frame=frame) self.del_other_forms(frame) + return omega def del_other_forms(self, frame=None): r""" @@ -625,25 +736,38 @@ def del_other_forms(self, frame=None): EXAMPLES: - We first create two sets of connection coefficients:: + We first create two sets of connection forms:: sage: M = Manifold(2, 'M', start_index=1) sage: X. = M.chart() sage: E = M.vector_bundle(2, 'E') sage: nab = E.bundle_connection('nabla', latex_name=r'\nabla') sage: e = E.local_frame('e') - sage: a = M.one_form([x^2, x], name='a') - sage: b = M.one_form([y^2, y], name='b') - sage: nab.set_connection_form(1, 1, a, frame=e) - sage: f = M.vector_frame('f') - sage: nab.add_connection_form(1, 1, b, frame=e) - - Let us reset the connection coefficients w.r.t. all frames except for + sage: nab.set_connection_form(1, 1, frame=e)[:] = [x^2, x] + sage: f = E.local_frame('f') + sage: nab.add_connection_form(1, 1, frame=f)[:] = [y^2, y] + sage: nab[e, 1, 1].display() + connection (1,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x^2 dx + x dy + sage: nab[f, 1, 1].display() + connection (1,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (f_1,f_2)) = y^2 dx + y dy + + Let us delete the connection forms w.r.t. all frames except for frame ``e``:: sage: nab.del_other_forms(e) - sage: nab.connection_form(1, 1, frame=f) - 1-form zero on the 2-dimensional differentiable manifold M + sage: nab[e, 1, 1].display() + connection (1,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x^2 dx + x dy + + The connection forms w.r.t. frame ``e`` have indeed been + deleted:: + + sage: nab[f, :] + Traceback (most recent call last): + ... + KeyError: Local frame (E|_M, (f_1,f_2)) """ if frame is None: @@ -667,7 +791,7 @@ def curvature_form(self, i, j, frame=None): frame. The *curvature 2-forms* with respect to the frame `e` are the 2-forms - `\Omega^i_j` given by the formula + `\Omega^j_i` given by the formula .. MATH:: @@ -676,14 +800,14 @@ def curvature_form(self, i, j, frame=None): INPUT: - - ``i``, ``j`` -- indices identifying the 2-form `\Omega^i_j` + - ``i``, ``j`` -- indices identifying the 2-form `\Omega^j_i` - ``frame`` -- (default: ``None``) local frame relative to which the curvature 2-forms are defined; if ``None``, the default frame of the vector bundle is assumed. OUTPUT: - - the 2-form `\Omega^i_j`, as an instance of + - the 2-form `\Omega^j_i`, as an instance of :class:`~sage.manifolds.differentiable.diff_form.DiffForm` EXAMPLES:: @@ -693,8 +817,7 @@ def curvature_form(self, i, j, frame=None): sage: E = M.vector_bundle(1, 'E') sage: nab = E.bundle_connection('nabla', latex_name=r'\nabla') sage: e = E.local_frame('e') - sage: a = M.one_form([x^2, x], name='a') - sage: nab.set_connection_form(1, 1, a) + sage: nab.set_connection_form(1, 1)[:] = [x^2, x] sage: curv = nab.curvature_form(1, 1); curv 2-form curvature (1,1) of bundle connection nabla w.r.t. Local frame (E|_M, (e_1)) on the 2-dimensional differentiable manifold M @@ -711,14 +834,14 @@ def curvature_form(self, i, j, frame=None): if frame not in self._curvature_forms: self._curvature_forms[frame] = {} if (i, j) not in self._curvature_forms[frame]: - name = "curvature ({},{}) of bundle connection ".format(i,j) + \ + name = "curvature ({},{}) of bundle connection ".format(i, j) + \ self._name + " w.r.t. {}".format(frame) latex_name = r"\Omega^" + str(i) + r"_{\ \, " + \ - str(j) + "}" + str(j) + "}" omega = self.connection_form - curv_form = omega(i, j, frame).exterior_derivative() + \ - sum(omega(k, j, frame).wedge(omega(i, k, frame)) - for k in self._vbundle.irange()) + curv_form = omega(i, j, frame).exterior_derivative() + curv_form += sum(omega(k, j, frame).wedge(omega(i, k, frame)) + for k in self._vbundle.irange()) curv_form.set_name(name=name, latex_name=latex_name) self._curvature_forms[frame][(i, j)] = curv_form return self._curvature_forms[frame][(i, j)] @@ -745,3 +868,195 @@ def __hash__(self): if self._hash == -1: self._hash = hash(repr(self)) return self._hash + + def __getitem__(self, args): + r""" + Return a component of ``self`` with respect to some frame. + + INPUT: + + - ``args`` -- list of indices defining the component; if ``[:]`` is + provided, all the components are returned + + TESTS:: + + sage: M = Manifold(2, 'M', start_index=1) + sage: X. = M.chart() + sage: E = M.vector_bundle(2, 'E') + sage: nab = E.bundle_connection('nabla') + sage: e = E.local_frame('e') + sage: nab[:] = 0 + sage: nab[1, 2][:] = [x^2, x] + sage: nab[1, 1][:] = [y, y] + sage: nab[1, 1].display() + connection (1,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = y dx + y dy + sage: nab[e, 1, 2].display() + connection (1,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x^2 dx + x dy + sage: nab[e, :] + [[1-form connection (1,1) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M, + 1-form connection (1,2) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M], + [1-form connection (2,1) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M, + 1-form connection (2,2) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M]] + sage: nab[:] + [[1-form connection (1,1) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M, + 1-form connection (1,2) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M], + [1-form connection (2,1) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M, + 1-form connection (2,2) of bundle connection nabla w.r.t. Local + frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable + manifold M]] + + """ + # extract frame from first index: + vb = self._vbundle + if isinstance(args, (int, Integer, slice)): + smodule = vb.section_module(domain=self._base_space) + frame = smodule.default_frame() + elif not isinstance(args[0], (int, Integer, slice)): + frame = args[0] + args = args[1:] + else: + smodule = vb.section_module(domain=self._base_space) + frame = smodule.default_frame() + # indexing: + if isinstance(args, slice): + indices = args + elif isinstance(args[0], slice): + indices = args[0] + else: + indices = args + if isinstance(indices, slice): + if indices.start is None and indices.stop is None: + return [[self.connection_form(i, j, frame=frame) + for j in vb.irange()] for i in vb.irange()] + else: + raise NotImplementedError("[start:stop] syntax not " + "implemented") + if len(indices) != 2: + raise ValueError("index must be a pair of integers") + (i, j) = indices + if i not in vb.irange() or j not in vb.irange(): + raise IndexError("index out of range") + form = self.connection_form(indices[0], indices[1], frame=frame) + return form + + def __setitem__(self, args, value): + r""" + Sets the components of ``self`` corresponding to the given indices. + + INPUT: + + - ``args`` -- list of indices (usually a pair of integers); if ``[:]`` + is provided, all the components are set + - ``value`` -- the value to be set or a list of values if + ``args = [:]`` + + TESTS:: + + sage: M = Manifold(2, 'M', start_index=1) + sage: X. = M.chart() + sage: E = M.vector_bundle(2, 'E') + sage: nab = E.bundle_connection('nabla') + sage: e = E.local_frame('e') + sage: a = M.one_form([x^2, x], name='a') + sage: a.display() + a = x^2 dx + x dy + sage: nab[:] = 0 + sage: nab[1, 1] = a + sage: nab[1, 1].display() + connection (1,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x^2 dx + x dy + sage: nab[e, 2, 2] = a + sage: nab[e, 2, 2].display() + connection (2,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x^2 dx + x dy + sage: nab[:] = [[0, 0], [a, a]] + sage: nab[e, 2, 1].display() + connection (2,1) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x^2 dx + x dy + sage: nab[e, :] = [[a, a], [0, 0]] + sage: nab[e, 1, 2].display() + connection (1,2) of bundle connection nabla w.r.t. Local frame + (E|_M, (e_1,e_2)) = x^2 dx + x dy + + """ + # extract frame from first index: + vb = self._vbundle + if isinstance(args, (int, Integer, slice)): + smodule = vb.section_module(domain=self._base_space) + frame = smodule.default_frame() + elif not isinstance(args[0], (int, Integer, slice)): + frame = args[0] + args = args[1:] + else: + smodule = vb.section_module(domain=self._base_space) + frame = smodule.default_frame() + # determine indices: + if isinstance(args, slice): + indices = args + elif isinstance(args[0], slice): + indices = args[0] + else: + indices = args + # set values: + if isinstance(indices, (list, tuple)): + if len(indices) != 2: + raise IndexError("a tuple of integers must be provided") + (i, j) = indices + if i not in vb.irange() or j not in vb.irange(): + raise IndexError("index out of range") + omega = self.set_connection_form(i, j, frame=frame) + dom = frame.domain() + dmodule = dom.diff_form_module(1) + try: + form = dmodule(value) + except (TypeError, ValueError): + msg = "{} must be convertible ".format(value) + msg += "into an element of {}".format(dmodule) + raise TypeError(msg) + omega.copy_from(form) # copy all components from value + if isinstance(indices, slice): + if indices.start is None and indices.stop is None: + # check types: + if isinstance(value, (int, Integer)) and value == 0: + for i in vb.irange(): + for j in vb.irange(): + self[frame, i, j] = 0 + elif not isinstance(value, (list, tuple)): + raise TypeError("in case of [:] syntax, zero or a " + "list/tuple as value should be provided") + elif any(not isinstance(row, (list, tuple)) for row in value): + raise TypeError("in case of [:] syntax, the list/tuple " + "of value must contain lists/tuples") + else: + # check lenghts: + rk = vb._rank + if len(value) != rk: + raise ValueError("value must have " + "length {}".format(rk)) + if any(len(row) != rk for row in value): + raise ValueError("lists in value must have length " + "{}".format(rk)) + # perform designation: + sind = vb._base_space._sindex + for i in vb.irange(): + for j in vb.irange(): + self[frame, i, j] = value[i - sind][j - sind] + else: + raise NotImplementedError("[start:stop] syntax not " + "implemented") diff --git a/src/sage/manifolds/differentiable/characteristic_class.py b/src/sage/manifolds/differentiable/characteristic_class.py index 0f9d7db8486..4f07c3b6d1f 100644 --- a/src/sage/manifolds/differentiable/characteristic_class.py +++ b/src/sage/manifolds/differentiable/characteristic_class.py @@ -1,18 +1,18 @@ r""" Characteristic Classes -Let `E \to M` be a topological vector bundle of rank `n` over a topological -manifold `M` and `R` be any ring. -A *characteristic class* `c(E)` is an element of the cohomology ring -`H^{*}(M, R)` such that for any continuous map `f: M \to N`, where `N` is -another topological manifold, the *naturality condition* is satisfied: +A *characteristic class* `\kappa` is a natural transformation that +associates to each vector bundle `E \to M` a cohomology class +`\kappa(E) \in H^*(M;R)` such that for any continuous map `f\colon N \to M` +from another topological manifold `N`, the *naturality condition* is +satisfied: .. MATH:: - c(f^*E) = f^*c(E) . + f^*\kappa(E) = \kappa(f^* E) \in H^*(N;R) -Roughly speaking, characteristic classes measure the non-triviality of the -vector bundle `E`. +Roughly speaking, characteristic classes measure the non-triviality of +vector bundles. One way to obtain and compute characteristic classes in the de Rham cohomology with coefficients in the ring `\CC` is via the so-called *Chern-Weil theory* @@ -70,9 +70,10 @@ \left[\mathrm{tr}\left( f\left( \frac{\Omega}{2 \pi i} \right) \right)\right] \in H^{2*}_{\mathrm{dR}}(M, \CC) -the *additive Chern f-genus*. +the *additive characteristic class associated to* `f` of the complex vector +bundle `E`. -Important and predefined additive Chern genera are: +Important and predefined additive classes are: - *Chern Character* with `f(x) = \exp(x)` @@ -84,7 +85,8 @@ \left[\mathrm{tr}\left( \frac{1}{2} g\left( -\frac{\Omega^2}{4 \pi^2} \right) \right)\right] \in H^{4*}_{\mathrm{dR}}(M, \CC) -the *additive Pontryagin g-genus*. +the *additive characteristic class associated to* `g` of the **real** vector +bundle `E`. EXAMPLES: @@ -103,10 +105,10 @@ sage: nab = E.bundle_connection('nabla^E', latex_name=r'\nabla^E') sage: omega = M.one_form(name='omega') sage: A = function('A') - sage: omega[1] = I*A(t) - sage: omega.display() - omega = I*A(t) dx - sage: nab.set_connection_form(0, 0, omega) + sage: nab.set_connection_form(0, 0)[1] = I*A(t) + sage: nab[0, 0].display() + connection (0,0) of bundle connection nabla^E w.r.t. Local frame + (E|_M, (e_0)) = I*A(t) dx The Chern character is then given by:: @@ -130,9 +132,10 @@ \left[\det\left( f\left( \frac{\Omega}{2 \pi i} \right) \right)\right] \in H^{2*}_{\mathrm{dR}}(M, \CC) -the *multiplicative Chern f-genus*. +the *multiplicative characteristic class associated to* `f` of the complex +vector bundle `E`. -Important and predefined multiplicative Chern genera are: +Important and predefined multiplicative classes on complex vector bundles are: - *Chern class* with `f(x) = 1+x` - *Todd class* with `f(x) = \frac{x}{1-\exp(-x)}` @@ -145,9 +148,10 @@ \left[\det\left( \sqrt{ g \left( -\frac{\Omega^2}{4 \pi^2} \right) } \right) \right] \in H^{4*}_{\mathrm{dR}}(M, \CC) -the *multiplicative Pontryagin g-genus*. +the *multiplicative characteristic class associated to* `g` on the **real** +vector bundle `E`. -Important and predefined multiplicative Pontryagin genera are: +Important and predefined multiplicative classes on real vector bundles are: - *Pontryagin class* with `g(x) = 1+x` - `\hat{A}` *class* with `g(x) = \frac{\sqrt{x}/2}{\sinh(\sqrt{x}/2)}` @@ -177,7 +181,7 @@ sage: nab = E.bundle_connection('nabla') sage: omega = U.one_form(name='omega') sage: omega[c_comp.frame(),1,c_comp] = zbar/(1+z*zbar) - sage: nab.set_connection_form(1, 1, omega, frame=e) + sage: nab[e, 1, 1] = omega Now, the Chern class can be constructed:: @@ -251,7 +255,7 @@ sage: g[eV,1,1], g[eV,2,2] = 4/(1+u^2+v^2)^2, 4/(1+u^2+v^2)^2 sage: nab = g.connection() -In case of the the Euler class, skew-symmetric curvature matrices are needed +In case of the Euler class, skew-symmetric curvature matrices are needed for the Pfaffian. For this, we need to define the curvature matrices by hand:: @@ -284,6 +288,7 @@ (V, (d/du,d/dv)) = -4/(u^4 + v^4 + 2*(u^2 + 1)*v^2 + 2*u^2 + 1) du/\dv curvature (2,2) of connection nabla_g w.r.t. Coordinate frame (V, (d/du,d/dv)) = 0 + sage: nab.set_immutable() # make nab immutable Now the representative of the Euler class with respect to the connection `\nabla_g` induced by the standard metric can be computed:: @@ -665,10 +670,10 @@ def get_form(self, connection, cmatrices=None): sage: nab = E.bundle_connection('nabla^E', latex_name=r'\nabla^E') sage: omega = M.one_form(name='omega') sage: A = function('A') - sage: omega[1] = I*A(t) - sage: omega.display() - omega = I*A(t) dx - sage: nab.set_connection_form(0, 0, omega) + sage: nab.set_connection_form(0, 0)[1] = I*A(t) + sage: nab[0, 0].display() + connection (0,0) of bundle connection nabla^E w.r.t. Local frame + (E|_M, (e_0)) = I*A(t) dx The Chern character is then given by:: diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index de8720283f5..789a2bb774c 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -22,21 +22,21 @@ - Chap. 1 of [Lee2013]_ """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2015 Eric Gourgoulhon # Copyright (C) 2015 Michal Bejger # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.manifolds.chart import Chart, RealChart, CoordChange from sage.manifolds.differentiable.vectorframe import CoordFrame + class DiffChart(Chart): r""" Chart on a differentiable manifold. @@ -488,7 +488,7 @@ def coframe(self): sage: ey = c_xy.frame()[1] ; ey Vector field d/dy on the 2-dimensional differentiable manifold M sage: dx(ex).display() - dx(d/dx): M --> R + dx(d/dx): M --> R (x, y) |--> 1 sage: dx(ey).display() dx(d/dy): M --> R @@ -646,8 +646,8 @@ def symbolic_velocities(self, left='D', right=None): # is not a string # If the argument of 'var' contains only one word, for - # instance: - # sage: var('Dt') + # instance:: + # var('Dt') # then 'var' does not return a tuple containing one symbolic # expression, but the symbolic expression itself. # This is taken into account below in order to return a list diff --git a/src/sage/manifolds/differentiable/curve.py b/src/sage/manifolds/differentiable/curve.py index 2260198f29f..35181d23f9d 100644 --- a/src/sage/manifolds/differentiable/curve.py +++ b/src/sage/manifolds/differentiable/curve.py @@ -245,6 +245,108 @@ class DifferentiableCurve(DiffMap): R --> R t |--> cos(t)^2 + .. RUBRIC:: Curvature and torsion of a curve in a Riemannian manifold + + Let us consider a helix `C` in the Euclidean space `\mathbb{E}^3` + parametrized by its arc length `s`:: + + sage: E. = EuclideanSpace() + sage: R. = RealLine() + sage: C = E.curve((2*cos(s/3), 2*sin(s/3), sqrt(5)*s/3), (s, 0, 6*pi), + ....: name='C') + + Its tangent vector field is:: + + sage: T = C.tangent_vector_field() + sage: T.display() + C' = -2/3*sin(1/3*s) e_x + 2/3*cos(1/3*s) e_y + 1/3*sqrt(5) e_z + + Since `C` is parametrized by its arc length `s`, `T` is a unit vector (with + respect to the Euclidean metric of `\mathbb{E}^3`):: + + sage: norm(T) + Scalar field |C'| on the Real interval (0, 6*pi) + sage: norm(T).display() + |C'|: (0, 6*pi) --> R + s |--> 1 + + Vector fields along `C` are defined by the method + :meth:`~sage.manifolds.differentiable.manifold.DifferentiableManifold.vector_field` + of the domain of `C` with the keyword argument ``dest_map`` set to `C`. For + instance the derivative vector `T'=\mathrm{d}T/\mathrm{d}s` is:: + + sage: I = C.domain(); I + Real interval (0, 6*pi) + sage: Tp = I.vector_field([diff(T[i], s) for i in E.irange()], dest_map=C, + ....: name="T'") + sage: Tp.display() + T' = -2/9*cos(1/3*s) e_x - 2/9*sin(1/3*s) e_y + + The norm of `T'` is the curvature of the helix:: + + sage: kappa = norm(Tp) + sage: kappa + Scalar field |T'| on the Real interval (0, 6*pi) + sage: kappa.expr() + 2/9 + + The unit normal vector along `C` is:: + + sage: N = Tp / kappa + sage: N.display() + -cos(1/3*s) e_x - sin(1/3*s) e_y + + while the binormal vector along `C` is `B = T \times N`:: + + sage: B = T.cross_product(N) + sage: B + Vector field along the Real interval (0, 6*pi) with values on the + Euclidean space E^3 + sage: B.display() + 1/3*sqrt(5)*sin(1/3*s) e_x - 1/3*sqrt(5)*cos(1/3*s) e_y + 2/3 e_z + + The three vector fields `(T, N, B)` form the **Frenet-Serret frame** along + `C`:: + + sage: FS = I.vector_frame(('T', 'N', 'B'), (T, N, B), + ....: symbol_dual=('t', 'n', 'b')) + sage: FS + Vector frame ((0, 6*pi), (T,N,B)) with values on the Euclidean space E^3 + + The Frenet-Serret frame is orthonormal:: + + sage: matrix([[u.dot(v).expr() for v in FS] for u in FS]) + [1 0 0] + [0 1 0] + [0 0 1] + + The derivative vectors `N'` and `B'`:: + + sage: Np = I.vector_field([diff(N[i], s) for i in E.irange()], + ....: dest_map=C, name="N'") + sage: Np.display() + N' = 1/3*sin(1/3*s) e_x - 1/3*cos(1/3*s) e_y + sage: Bp = I.vector_field([diff(B[i], s) for i in E.irange()], + ....: dest_map=C, name="B'") + sage: Bp.display() + B' = 1/9*sqrt(5)*cos(1/3*s) e_x + 1/9*sqrt(5)*sin(1/3*s) e_y + + The Frenet-Serret formulas:: + + sage: for v in (Tp, Np, Bp): + ....: v.display(FS) + ....: + T' = 2/9 N + N' = -2/9 T + 1/9*sqrt(5) B + B' = -1/9*sqrt(5) N + + The torsion of `C` is obtained as the third component of `N'` in the + Frenet-Serret frame:: + + sage: tau = Np[FS, 3] + sage: tau + 1/9*sqrt(5) + """ def __init__(self, parent, coord_expression=None, name=None, latex_name=None, is_isomorphism=False, is_identity=False): @@ -363,7 +465,9 @@ def coord_expr(self, chart=None): sage: U = M.open_subset('U', coord_def={c_xy: (y!=0, x<0)}) # the complement of the segment y=0 and x>0 sage: c_cart = c_xy.restrict(U) # Cartesian coordinates on U sage: c_spher. = U.chart(r'r:(0,+oo) ph:(0,2*pi):\phi') # spherical coordinates on U - sage: # Links between spherical coordinates and Cartesian ones: + + Links between spherical coordinates and Cartesian ones:: + sage: ch_cart_spher = c_cart.transition_map(c_spher, [sqrt(x*x+y*y), atan2(y,x)]) sage: ch_cart_spher.set_inverse(r*cos(ph), r*sin(ph)) Check of the inverse coordinate transformation: @@ -902,12 +1006,10 @@ def _graphics(self, plot_curve, ambient_coords, thickness=1, True """ - from sage.plot.graphics import Graphics from sage.plot.line import line from sage.manifolds.utilities import set_axes_labels - # # The plot # diff --git a/src/sage/manifolds/differentiable/degenerate_submanifold.py b/src/sage/manifolds/differentiable/degenerate_submanifold.py index 80fd852d3ce..2aada97535b 100644 --- a/src/sage/manifolds/differentiable/degenerate_submanifold.py +++ b/src/sage/manifolds/differentiable/degenerate_submanifold.py @@ -75,6 +75,67 @@ degenerate hypersurface H embedded in 4-dimensional differentiable manifold M +A `2`-dimensional degenerate submanifold of a Lorentzian manifold:: + + sage: M = Manifold(4, 'M', structure="Lorentzian") + sage: X. = M.chart() + sage: S = Manifold(2, 'S', ambient=M, structure='degenerate_metric') + sage: S + 2-dimensional degenerate submanifold S embedded in 4-dimensional + differentiable manifold M + sage: X_S. = S.chart() + sage: Phi = S.diff_map(M, {(X_S, X): [u, u, u, v]}, + ....: name='Phi', latex_name=r'\Phi'); + sage: Phi_inv = M.diff_map(S, {(X, X_S): [x,y]}, name='Phi_inv', + ....: latex_name=r'\Phi^{-1}'); + sage: S.set_immersion(Phi, inverse=Phi_inv); S.declare_embedding() + sage: g = M.metric() + sage: g[0,0], g[1,1], g[2,2], g[3,3] = -1,1,1,1 + sage: S.set_transverse(rigging=[x,y]) + sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 + sage: V = M.vector_field(); V[3] = 1 + sage: Sc = S.screen('Sc', V, xi) + + sage: S.default_screen() + screen distribution Sc along the 2-dimensional degenerate submanifold + S embedded in 4-dimensional differentiable manifold M mapped into + the 4-dimensional Lorentzian manifold M + + sage: S.ambient_metric() + Lorentzian metric g on the 4-dimensional Lorentzian manifold M + + sage: S.induced_metric() + degenerate metric gamma on the 2-dimensional degenerate submanifold S + embedded in 4-dimensional differentiable manifold M + + sage: S.first_fundamental_form() + Field of symmetric bilinear forms g|S along the 2-dimensional + degenerate submanifold S embedded in 4-dimensional differentiable manifold M + with values on the 4-dimensional Lorentzian manifold M + + sage: S.adapted_frame() + Vector frame (S, (vv_0,vv_1,vv_2,vv_3)) with values on the 4-dimensional Lorentzian manifold M + + sage: S.projection(V) + Tensor field of type (1,0) along the 2-dimensional degenerate submanifold S + embedded in 4-dimensional differentiable manifold M + with values on the 4-dimensional Lorentzian manifold M + + sage: S.weingarten_map() # long time + Tensor field nabla_g(xi)|X(S) of type (1,1) along the 2-dimensional + degenerate submanifold S embedded in 4-dimensional differentiable manifold M + with values on the 4-dimensional Lorentzian manifold M + + sage: SO = S.shape_operator() # long time + sage: SO.display() # long time + A^* = 0 + + sage: S.is_tangent(xi.along(Phi)) + True + sage: v = M.vector_field(); v[1] = 1 + sage: S.is_tangent(v.along(Phi)) + False + AUTHORS: - Hans Fotsing Tetsing (2019) : initial version @@ -104,9 +165,9 @@ from sage.manifolds.differentiable.vectorfield_module import VectorFieldModule from sage.rings.infinity import infinity from sage.matrix.constructor import matrix -from sage.misc.cachefunc import cached_method from sage.symbolic.expression import Expression + class DegenerateSubmanifold(DegenerateManifold, DifferentiableSubmanifold): r""" Degenerate submanifolds @@ -165,7 +226,7 @@ class DegenerateSubmanifold(DegenerateManifold, DifferentiableSubmanifold): :mod:`~sage.manifolds.manifold` and :mod:`~sage.manifolds.differentiable.differentiable_submanifold` - """ + """ def __init__(self, n, name, ambient=None, metric_name='g', signature=None, base_manifold=None, diff_degree=infinity, latex_name=None, metric_latex_name=None, start_index=0, category=None, @@ -173,7 +234,7 @@ def __init__(self, n, name, ambient=None, metric_name='g', signature=None, r""" Construct a pseudo-Riemannian submanifold. - EXAMPLES:: + EXAMPLES: A `2`-dimensional degenerate submanifold of a Lorentzian manifold:: @@ -233,7 +294,7 @@ def _repr_(self): If no ambient manifold is specified, the submanifold is considered as a manifold. - TESTS:: + TESTS: A `2`-dimensional degenerate submanifold of a Lorentzian manifold:: @@ -317,13 +378,12 @@ def default_screen(self): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi) - sage: S.default_screen() + sage: Sc = S.screen('Sc', (U,V), xi) # long time + sage: S.default_screen() # long time screen distribution Sc along the degenerate hypersurface S embedded in 4-dimensional differentiable manifold M mapped into the 4-dimensional Lorentzian manifold M - """ return self._default_screen @@ -354,8 +414,8 @@ def list_of_screens(self): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi) - sage: S.list_of_screens() + sage: Sc = S.screen('Sc', (U,V), xi) # long time + sage: S.list_of_screens() # long time {'Sc': screen distribution Sc along the degenerate hypersurface S embedded in 4-dimensional differentiable manifold M mapped into the 4-dimensional Lorentzian manifold M} @@ -365,7 +425,7 @@ def list_of_screens(self): def set_transverse(self, rigging=None, normal=None): r""" - For setting a transversal disttribution of the degenerate submanifold. + For setting a transversal distribution of the degenerate submanifold. according to the type of the submanifold amoung the 4 possible types, one must enter a list of normal transversal vector fields and/or a list of transversal and not normal vector fields spanning a transverse @@ -485,11 +545,10 @@ def screen(self, name, screen, rad, latex_name=None): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); Sc + sage: Sc = S.screen('Sc', (U,V), xi); Sc # long time screen distribution Sc along the degenerate hypersurface S embedded in 4-dimensional differentiable manifold M mapped into the 4-dimensional Lorentzian manifold M - """ if isinstance(screen, (list, tuple)): screen = [elt for elt in screen] @@ -562,7 +621,7 @@ def induced_metric(self): sage: S.set_immersion(Phi, inverse=Phi_inv); S.declare_embedding() sage: g = M.metric() sage: g[0,0], g[1,1], g[2,2], g[3,3] = -1,1,1,1 - sage: h = S.induced_metric(); h + sage: h = S.induced_metric(); h # long time degenerate metric gamma on the 2-dimensional degenerate submanifold S embedded in 4-dimensional differentiable manifold M @@ -604,8 +663,8 @@ def first_fundamental_form(self): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: h = S.first_fundamental_form() + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: h = S.first_fundamental_form() # long time """ if self._first_fundamental_form is None: @@ -618,11 +677,11 @@ def first_fundamental_form(self): def _ambient_decomposition(self, screen=None): r""" - Return a list ``[screen, rad, normal, rig]`` where `screen` + Return a list ``[screen, rad, normal, rig]`` where ``screen`` is a list a vector fields on the ambient manifold `M` spanning - the giving screen distribution, `rad` a list of vector fields - spanning radical distribution, `normal` list of normal transversal - vector fields, and `rig` list of rigging vector fields + the giving screen distribution, ``rad`` a list of vector fields + spanning radical distribution, ``normal`` list of normal transversal + vector fields, and ``rig`` list of rigging vector fields corresponding to the giving screen. INPUT: @@ -662,8 +721,8 @@ def _ambient_decomposition(self, screen=None): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: S._ambient_decomposition() + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: S._ambient_decomposition() # long time [[Vector field on the 4-dimensional Lorentzian manifold M, Vector field on the 4-dimensional Lorentzian manifold M], [Vector field on the 4-dimensional Lorentzian manifold M], @@ -733,8 +792,8 @@ def _adapted_frame_(self, screen=None): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: T = S._adapted_frame_(); + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: T = S._adapted_frame_(); # long time """ @@ -827,8 +886,8 @@ def adapted_frame(self, screen=None): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: T = S.adapted_frame(); T + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: T = S.adapted_frame(); T # long time Vector frame (S, (vv_0,vv_1,vv_2,vv_3)) with values on the 4-dimensional Lorentzian manifold M @@ -895,9 +954,9 @@ def second_fundamental_form(self, screen=None): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: B = S.second_fundamental_form(); - sage: B.display() + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: B = S.second_fundamental_form(); # long time + sage: B.display() # long time B = 0 """ @@ -957,8 +1016,8 @@ def projection(self, tensor, screen=None): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: U1 = S.projection(U) + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: U1 = S.projection(U) # long time """ if tensor.tensor_type()[0]!=1: @@ -1000,8 +1059,8 @@ def screen_projection(self, tensor, screen=None): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: U1 = S.screen_projection(U); + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: U1 = S.screen_projection(U); # long time """ if tensor.tensor_type()[0]!=1: @@ -1067,9 +1126,9 @@ def weingarten_map(self, screen=None): sage: S.set_transverse(rigging=v) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: W = S.weingarten_map(); - sage: W.display() + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: W = S.weingarten_map(); # long time + sage: W.display() # long time nabla_g(xi)|X(S) = 0 """ @@ -1124,9 +1183,9 @@ def shape_operator(self, screen=None): sage: S.set_transverse(rigging=v) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: SO = S.shape_operator(); - sage: SO.display() + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: SO = S.shape_operator(); # long time + sage: SO.display() # long time A^* = 0 """ @@ -1180,9 +1239,9 @@ def gauss_curvature(self, screen=None): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: K = S.gauss_curvature(); - sage: K.display() + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: K = S.gauss_curvature(); # long time + sage: K.display() # long time S --> R (u, v, w) |--> 0 @@ -1235,9 +1294,9 @@ def principal_directions(self, screen=None): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); T = S.adapted_frame() - sage: PD = S.principal_directions() - sage: PD[2][0].display(T) + sage: Sc = S.screen('Sc', (U,V), xi); T = S.adapted_frame() # long time + sage: PD = S.principal_directions() # long time + sage: PD[2][0].display(T) # long time e_2 = xi """ @@ -1301,11 +1360,11 @@ def mean_curvature(self, screen=None): sage: S.set_transverse(rigging=x) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: m = S.mean_curvature(); m + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: m = S.mean_curvature(); m # long time Scalar field on the degenerate hypersurface S embedded in 4-dimensional differentiable manifold M - sage: m.display() + sage: m.display() # long time S --> R (u, v, w) |--> 0 @@ -1355,10 +1414,10 @@ def is_tangent(self, v): sage: S.set_transverse(rigging=v) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: S.is_tangent(xi.along(Phi)) + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: S.is_tangent(xi.along(Phi)) # long time True - sage: S.is_tangent(v.along(Phi)) + sage: S.is_tangent(v.along(Phi)) # long time False """ @@ -1428,7 +1487,7 @@ class Screen(VectorFieldModule): A screen distribution for the Schwarzschild black hole horizon:: sage: H.set_transverse(rigging=v) - sage: S = H.screen('S', [e1, e2], (xi)); S + sage: S = H.screen('S', [e1, e2], (xi)); S # long time screen distribution S along the degenerate hypersurface H embedded in 4-dimensional differentiable manifold M mapped into the 4-dimensional Lorentzian manifold M @@ -1436,14 +1495,14 @@ class Screen(VectorFieldModule): The corresponding normal tangent null vector field and null transversal vector field:: - sage: xi = S.normal_tangent_vector(); xi.display() + sage: xi = S.normal_tangent_vector(); xi.display() # long time xi = -d/dt - sage: N = S.rigging(); N.display() + sage: N = S.rigging(); N.display() # long time N = d/dt - d/dr Those vector fields are normalized by `g(\xi,N)=1`:: - sage: g.along(Phi)(xi, N).display() + sage: g.along(Phi)(xi, N).display() # long time g(xi,N): H --> R (ht, hth, hph) |--> 1 @@ -1526,12 +1585,12 @@ def __getitem__(self, i): INPUT: - ``i`` -- index of the coordinate; if the slice ``[:]``, then all - the coordinates are returned + the coordinates are returned OUTPUT: - a vector field on the ambient manifold that belong to - the screen distribution + the screen distribution TESTS:: @@ -1588,8 +1647,8 @@ def normal_tangent_vector(self): sage: S.set_transverse(rigging=v) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: Rad = Sc.normal_tangent_vector(); Rad.display() + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: Rad = Sc.normal_tangent_vector(); Rad.display() # long time xi = d/dt + d/dx """ @@ -1632,8 +1691,8 @@ def rigging(self): sage: S.set_transverse(rigging=v) sage: xi = M.vector_field(); xi[0] = 1; xi[1] = 1 sage: U = M.vector_field(); U[2] = 1; V = M.vector_field(); V[3] = 1 - sage: Sc = S.screen('Sc', (U,V), xi); - sage: rig = Sc.rigging(); rig.display() + sage: Sc = S.screen('Sc', (U,V), xi); # long time + sage: rig = Sc.rigging(); rig.display() # long time N = -1/2 d/dt + 1/2 d/dx """ diff --git a/src/sage/manifolds/differentiable/diff_form.py b/src/sage/manifolds/differentiable/diff_form.py index 49c44503b2e..417183adf62 100644 --- a/src/sage/manifolds/differentiable/diff_form.py +++ b/src/sage/manifolds/differentiable/diff_form.py @@ -163,6 +163,12 @@ class DiffForm(TensorField): sage: da.display(eV) da = -du/\dv + The exterior derivative can also be obtained by applying the function + ``diff`` to a differentiable form:: + + sage: diff(a) is a.exterior_derivative() + True + Another 1-form defined by its components in ``eU``:: sage: b = M.one_form(1+x*y, x^2, frame=eU, name='b') @@ -275,7 +281,7 @@ def __init__(self, vector_field_module, degree, name=None, latex_name=None): TESTS: Construction via ``parent.element_class``, and not via a direct call - to ``DiffForm`, to fit with the category framework:: + to ``DiffForm``, to fit with the category framework:: sage: M = Manifold(2, 'M') sage: U = M.open_subset('U') ; V = M.open_subset('V') @@ -422,19 +428,16 @@ def exterior_derivative(self): True Instead of invoking the method :meth:`exterior_derivative`, one may - use the global function - :func:`~sage.manifolds.utilities.exterior_derivative` - or its alias :func:`~sage.manifolds.utilities.xder`:: + use the global function ``diff``:: - sage: from sage.manifolds.utilities import xder - sage: xder(a) is a.exterior_derivative() + sage: diff(a) is a.exterior_derivative() True Let us check Cartan's identity:: sage: v = M.vector_field({e_xy: [-y, x]}, name='v') sage: v.add_comp_by_continuation(e_uv, U.intersection(V), c_uv) - sage: a.lie_der(v) == v.contract(xder(a)) + xder(a(v)) # long time + sage: a.lie_der(v) == v.contract(diff(a)) + diff(a(v)) # long time True """ @@ -449,6 +452,9 @@ def exterior_derivative(self): resu._restrictions[dom] = rst.exterior_derivative() return resu + derivative = exterior_derivative # allows one to use functional notation, + # e.g. diff(a) for a.exterior_derivative() + def wedge(self, other): r""" Exterior product with another differential form. @@ -1046,7 +1052,7 @@ class DiffFormParal(FreeModuleAltForm, TensorFieldParal): no symmetry; antisymmetry: (0, 1) The exterior derivative of a differential form is obtained by means - of the :meth:`exterior_derivative`:: + of the method :meth:`exterior_derivative`:: sage: da = a.exterior_derivative() ; da 2-form dA on the 3-dimensional differentiable manifold R3 @@ -1059,6 +1065,11 @@ class DiffFormParal(FreeModuleAltForm, TensorFieldParal): sage: dab = ab.exterior_derivative() ; dab 3-form d(A/\B) on the 3-dimensional differentiable manifold R3 + or by applying the function ``diff`` to the differential form:: + + sage: diff(a) is a.exterior_derivative() + True + As a 3-form over a 3-dimensional manifold, ``d(A/\B)`` is necessarily proportional to the volume 3-form:: @@ -1319,12 +1330,9 @@ def exterior_derivative(self): True Instead of invoking the method :meth:`exterior_derivative`, one may - use the global function - :func:`~sage.manifolds.utilities.exterior_derivative` - or its alias :func:`~sage.manifolds.utilities.xder`:: + use the global function ``diff``:: - sage: from sage.manifolds.utilities import xder - sage: xder(a) is a.exterior_derivative() + sage: diff(a) is a.exterior_derivative() True The exterior derivative is nilpotent:: @@ -1339,7 +1347,7 @@ def exterior_derivative(self): Let us check Cartan's identity:: sage: v = M.vector_field(-y, x, t, z, name='v') - sage: a.lie_der(v) == v.contract(xder(a)) + xder(a(v)) # long time + sage: a.lie_der(v) == v.contract(diff(a)) + diff(a(v)) # long time True """ @@ -1399,6 +1407,9 @@ def exterior_derivative(self): resu._components[frame] = dc return resu + derivative = exterior_derivative # allows one to use functional notation, + # e.g. diff(a) for a.exterior_derivative() + def wedge(self, other): r""" Exterior product of ``self`` with another differential form. diff --git a/src/sage/manifolds/differentiable/diff_form_module.py b/src/sage/manifolds/differentiable/diff_form_module.py index 07d216a936c..31e6a3a450b 100644 --- a/src/sage/manifolds/differentiable/diff_form_module.py +++ b/src/sage/manifolds/differentiable/diff_form_module.py @@ -38,9 +38,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.categories.modules import Modules -from sage.symbolic.ring import ZZ from sage.tensor.modules.ext_pow_free_module import ExtPowerDualFreeModule -from sage.manifolds.scalarfield import ScalarField from sage.manifolds.differentiable.diff_form import DiffForm, DiffFormParal from sage.manifolds.differentiable.tensorfield import TensorField from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal @@ -332,8 +330,12 @@ def _element_constructor_(self, comp=[], frame=None, name=None, True """ - if comp in ZZ and comp == 0: - return self.zero() + try: + if comp.is_trivial_zero(): + return self.zero() + except AttributeError: + if comp == 0: + return self.zero() if isinstance(comp, (DiffForm, DiffFormParal)): # coercion by domain restriction if (self._degree == comp._tensor_type[1] @@ -356,14 +358,13 @@ def _element_constructor_(self, comp=[], frame=None, name=None, else: raise TypeError("cannot convert the {} ".format(tensor) + "to an element of {}".format(self)) - if isinstance(comp, ScalarField): - # since the degree of self is >= 1, we cannot coerce scalar fields: + if not isinstance(comp, (list, tuple)): raise TypeError("cannot convert the {} ".format(comp) + "to an element of {}".format(self)) # standard construction resu = self.element_class(self._vmodule, self._degree, name=name, latex_name=latex_name) - if comp != []: + if comp: resu.set_comp(frame)[:] = comp return resu @@ -446,9 +447,10 @@ def zero(self): zero = self._element_constructor_(name='zero', latex_name='0') for frame in self._domain._frames: if self._dest_map.restrict(frame._domain) == frame._dest_map: - zero._add_comp_unsafe(frame) + zero.add_comp(frame) # (since new components are initialized to zero) zero._is_zero = True # This element is certainly zero + zero.set_immutable() return zero #### End of Parent methods @@ -802,8 +804,12 @@ def _element_constructor_(self, comp=[], frame=None, name=None, False """ - if comp in ZZ and comp == 0: - return self.zero() + try: + if comp.is_trivial_zero(): + return self.zero() + except AttributeError: + if comp == 0: + return self.zero() if isinstance(comp, (DiffForm, DiffFormParal)): # coercion by domain restriction if (self._degree == comp._tensor_type[1] @@ -826,13 +832,13 @@ def _element_constructor_(self, comp=[], frame=None, name=None, else: raise TypeError("cannot convert the {} ".format(tensor) + "to an element of {}".format(self)) - if isinstance(comp, ScalarField): - # since the degree of self is >= 1, we cannot coerce scalar fields: + if not isinstance(comp, (list, tuple)): raise TypeError("cannot convert the {} ".format(comp) + "to an element of {}".format(self)) + # standard construction resu = self.element_class(self._fmodule, self._degree, name=name, latex_name=latex_name) - if comp != []: + if comp: resu.set_comp(frame)[:] = comp return resu diff --git a/src/sage/manifolds/differentiable/euclidean.py b/src/sage/manifolds/differentiable/euclidean.py index 755adf60f06..9246f58b406 100644 --- a/src/sage/manifolds/differentiable/euclidean.py +++ b/src/sage/manifolds/differentiable/euclidean.py @@ -412,6 +412,9 @@ from sage.functions.trig import cos, sin, atan2 from sage.functions.other import sqrt from sage.misc.latex import latex +from sage.rings.real_mpfr import RR +from sage.categories.manifolds import Manifolds +from sage.categories.metric_spaces import MetricSpaces from sage.manifolds.differentiable.pseudo_riemannian import \ PseudoRiemannianManifold @@ -612,10 +615,12 @@ class EuclideanSpace(PseudoRiemannianManifold): sage: latex(E) \mathbb{E}^{4} - ``E`` is a real smooth manifold of dimension `4`:: + ``E`` is both a real smooth manifold of dimension `4` and a complete metric + space:: sage: E.category() - Category of smooth manifolds over Real Field with 53 bits of precision + Join of Category of smooth manifolds over Real Field with 53 bits of + precision and Category of complete metric spaces sage: dim(E) 4 @@ -742,10 +747,8 @@ def __init__(self, n, name=None, latex_name=None, an Euclidean space; the created object is then an open subset of ``base_manifold`` - ``category`` -- (default: ``None``) to specify the category; - if ``None``, ``Manifolds(RR).Differentiable()`` (or - ``Manifolds(RR).Smooth()`` if ``diff_degree`` = ``infinity``) - is assumed (see the category - :class:`~sage.categories.manifolds.Manifolds`) + if ``None``, + ``Manifolds(RR).Smooth() & MetricSpaces().Complete()`` is assumed - ``init_coord_methods`` -- (default: ``None``) dictionary of methods to initialize the various type of coordinates, with each key being a string describing the type of coordinates; to be @@ -772,6 +775,10 @@ def __init__(self, n, name=None, latex_name=None, name = 'E^{}'.format(n) if latex_name is None: latex_name = r'\mathbb{E}^{' + str(n) + '}' + if category is None: + category = Manifolds(RR).Smooth() & MetricSpaces().Complete() + # NB: RR is a proxy for the field of real numbers, until + # Trac #24456 is ready PseudoRiemannianManifold.__init__(self, n, name, metric_name=metric_name, signature=n, base_manifold=base_manifold, latex_name=latex_name, @@ -948,6 +955,39 @@ def cartesian_frame(self): # we simply return this frame: return self._cartesian_chart.frame() + def dist(self, p, q): + r""" + Euclidean distance between two points. + + INPUT: + + - ``p`` -- an element of ``self`` + - ``q`` -- an element of ``self`` + + OUTPUT: + + - the Euclidean distance `d(p, q)` + + EXAMPLES:: + + sage: E. = EuclideanSpace() + sage: p = E((1,0)) + sage: q = E((0,2)) + sage: E.dist(p, q) + sqrt(5) + sage: p.dist(q) # indirect doctest + sqrt(5) + + """ + chart = self.cartesian_coordinates() + coords_p = chart(p) + coords_q = chart(q) + d2 = 0 + for xp, xq in zip(coords_p, coords_q): + dx = xp - xq + d2 += dx*dx + return sqrt(d2) + ############################################################################### class EuclideanPlane(EuclideanSpace): @@ -998,9 +1038,7 @@ class EuclideanPlane(EuclideanSpace): - ``base_manifold`` -- (default: ``None``) if not ``None``, must be an Euclidean plane; the created object is then an open subset of ``base_manifold`` - ``category`` -- (default: ``None``) to specify the category; if ``None``, - ``Manifolds(RR).Differentiable()`` (or ``Manifolds(RR).Smooth()`` - if ``diff_degree`` = ``infinity``) is assumed (see the category - :class:`~sage.categories.manifolds.Manifolds`) + ``Manifolds(RR).Smooth() & MetricSpaces().Complete()`` is assumed - ``names`` -- (default: ``None``) unused argument, except if ``symbols`` is not provided; it must then be a tuple containing the coordinate symbols (this is guaranteed if the shortcut operator @@ -1025,10 +1063,12 @@ class EuclideanPlane(EuclideanSpace): sage: E. = EuclideanSpace(); E Euclidean plane E^2 - ``E`` is a real smooth manifold of dimension 2:: + ``E`` is both a real smooth manifold of dimension `2` and a complete metric + space:: sage: E.category() - Category of smooth manifolds over Real Field with 53 bits of precision + Join of Category of smooth manifolds over Real Field with 53 bits of + precision and Category of complete metric spaces sage: dim(E) 2 @@ -1518,9 +1558,7 @@ class Euclidean3dimSpace(EuclideanSpace): Euclidean 3-space; the created object is then an open subset of ``base_manifold`` - ``category`` -- (default: ``None``) to specify the category; if ``None``, - ``Manifolds(RR).Differentiable()`` (or ``Manifolds(RR).Smooth()`` - if ``diff_degree`` = ``infinity``) is assumed (see the category - :class:`~sage.categories.manifolds.Manifolds`) + ``Manifolds(RR).Smooth() & MetricSpaces().Complete()`` is assumed - ``names`` -- (default: ``None``) unused argument, except if ``symbols`` is not provided; it must then be a tuple containing the coordinate symbols (this is guaranteed if the shortcut operator @@ -1553,10 +1591,12 @@ class Euclidean3dimSpace(EuclideanSpace): sage: type(E) - ``E`` is a real smooth manifold of dimension 3:: + ``E`` is both a real smooth manifold of dimension `3` and a complete metric + space:: sage: E.category() - Category of smooth manifolds over Real Field with 53 bits of precision + Join of Category of smooth manifolds over Real Field with 53 bits of + precision and Category of complete metric spaces sage: dim(E) 3 diff --git a/src/sage/manifolds/differentiable/levi_civita_connection.py b/src/sage/manifolds/differentiable/levi_civita_connection.py index 2776ab98592..24851381732 100644 --- a/src/sage/manifolds/differentiable/levi_civita_connection.py +++ b/src/sage/manifolds/differentiable/levi_civita_connection.py @@ -435,7 +435,9 @@ def coef(self, frame=None): [[[0, 0, 0], [0, -r, 0], [0, 0, -r*sin(th)^2]], [[0, 1/r, 0], [1/r, 0, 0], [0, 0, -cos(th)*sin(th)]], [[0, 0, 1/r], [0, 0, cos(th)/sin(th)], [1/r, cos(th)/sin(th), 0]]] - sage: # The only non-zero Christoffel symbols: + + The only non-zero Christoffel symbols:: + sage: gam[1,2,2], gam[1,3,3] (-r, -r*sin(th)^2) sage: gam[2,1,2], gam[2,3,3] @@ -455,14 +457,15 @@ def coef(self, frame=None): [[[0, 0, 0], [0, -1/r, 0], [0, 0, -1/r]], [[0, 1/r, 0], [0, 0, 0], [0, 0, -cos(th)/(r*sin(th))]], [[0, 0, 1/r], [0, 0, cos(th)/(r*sin(th))], [0, 0, 0]]] - sage: # The only non-zero connection coefficients: + + The only non-zero connection coefficients:: + sage: gam_e[1,2,2], gam_e[2,1,2] (-1/r, 1/r) sage: gam_e[1,3,3], gam_e[3,1,3] (-1/r, 1/r) sage: gam_e[2,3,3], gam_e[3,2,3] (-cos(th)/(r*sin(th)), cos(th)/(r*sin(th))) - """ from sage.manifolds.differentiable.vectorframe import CoordFrame if frame is None: diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index 261ece118c1..d28c761e2a8 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -2520,8 +2520,7 @@ def automorphism_field(self, *comp, **kwargs): resu._init_components(*comp, **kwargs) return resu - def tangent_identity_field(self, name='Id', latex_name=None, - dest_map=None): + def tangent_identity_field(self, dest_map=None): r""" Return the field of identity maps in the tangent spaces on ``self``. @@ -2589,7 +2588,7 @@ def tangent_identity_field(self, name='Id', latex_name=None, """ vmodule = self.vector_field_module(dest_map) - return vmodule.identity_map(name=name, latex_name=latex_name) + return vmodule.identity_map() def default_frame(self): r""" diff --git a/src/sage/manifolds/differentiable/metric.py b/src/sage/manifolds/differentiable/metric.py index 463c25d9aee..51290da04b8 100644 --- a/src/sage/manifolds/differentiable/metric.py +++ b/src/sage/manifolds/differentiable/metric.py @@ -39,7 +39,6 @@ # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ # ***************************************************************************** -from six.moves import range from sage.rings.integer import Integer from sage.manifolds.differentiable.tensorfield import TensorField @@ -96,7 +95,9 @@ class PseudoRiemannianMetric(TensorField): Standard metric on the sphere `S^2`:: sage: M = Manifold(2, 'S^2', start_index=1) - sage: # The two open domains covered by stereographic coordinates (North and South): + + The two open domains covered by stereographic coordinates (North and South):: + sage: U = M.open_subset('U') ; V = M.open_subset('V') sage: M.declare_union(U,V) # S^2 is the union of U and V sage: c_xy. = U.chart() ; c_uv. = V.chart() # stereographic coord @@ -751,7 +752,9 @@ def connection(self, name=None, latex_name=None, init_coef=True): `\RR^3`:: sage: M = Manifold(3, 'R^3', start_index=1) - sage: # Let us use spherical coordinates on R^3: + + Let us use spherical coordinates on `\RR^3`:: + sage: U = M.open_subset('U') # the complement of the half-plane (y=0, x>=0) sage: c_spher. = U.chart(r'r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\phi') sage: g = U.metric('g') diff --git a/src/sage/manifolds/differentiable/mixed_form.py b/src/sage/manifolds/differentiable/mixed_form.py index 4fabf3111ed..1118dea2999 100644 --- a/src/sage/manifolds/differentiable/mixed_form.py +++ b/src/sage/manifolds/differentiable/mixed_form.py @@ -27,7 +27,6 @@ from sage.misc.cachefunc import cached_method from sage.structure.element import AlgebraElement from sage.rings.integer import Integer -from sage.structure.richcmp import richcmp class MixedForm(AlgebraElement): r""" @@ -582,15 +581,21 @@ def _richcmp_(self, other, op): sage: G.set_restriction(F.restrict(V)) sage: F == G # True now True + sage: H = M.mixed_form([f, 0, 0]) + sage: F != H # this is fixed by ticket #30108 + True sage: F.parent().zero() == 0 True """ - # Compare all elements separately: - for j in self.irange(): - if not richcmp(self[j], other[j], op): - return False - return True + from sage.structure.richcmp import op_NE, op_EQ + if op == op_NE: + return not self == other + elif op == op_EQ: + # Compare all elements separately: + return all(self[j] == other[j] for j in self.irange()) + # Fall back on default implementation: + return super()._richcmp_(self, other, op) def _add_(self, other): r""" @@ -896,11 +901,17 @@ def _lmul_(self, other): y/\(x/\F) = [0] + [x^2*y^2 dx] + [0] """ - # Simple checks: - if other.is_trivial_zero(): - return self.parent().zero() - elif (other - 1).is_trivial_zero(): - return self + try: + if other.is_trivial_zero(): + return self.parent().zero() + if (other - 1).is_trivial_zero(): + return self + except AttributeError: + # in case base ring is not SR: + if other == 0: + return self.parent().zero() + if other == 1: + return self resu = self._new_instance() resu[:] = [other * form for form in self._comp] # Compose name: diff --git a/src/sage/manifolds/differentiable/mixed_form_algebra.py b/src/sage/manifolds/differentiable/mixed_form_algebra.py index f8e5a524781..5edb7b22955 100644 --- a/src/sage/manifolds/differentiable/mixed_form_algebra.py +++ b/src/sage/manifolds/differentiable/mixed_form_algebra.py @@ -27,14 +27,14 @@ from sage.structure.parent import Parent from sage.categories.graded_algebras import GradedAlgebras from sage.structure.unique_representation import UniqueRepresentation -from sage.symbolic.ring import ZZ, SR +from sage.symbolic.ring import SR from sage.manifolds.differentiable.mixed_form import MixedForm class MixedFormAlgebra(Parent, UniqueRepresentation): r""" An instance of this class represents the graded algebra of mixed form. That - is, if `\varphi: M \to N` is a differentiable map between two differentiable - manifolds `M` and `N`, the *graded algebra of mixed forms* + is, if `\varphi: M \to N` is a differentiable map between two + differentiable manifolds `M` and `N`, the *graded algebra of mixed forms* `\Omega^*(M,\varphi)` *along* `\varphi` is defined via the direct sum `\bigoplus^{n}_{j=0} \Omega^j(M,\varphi)` consisting of differential form modules @@ -112,8 +112,8 @@ class MixedFormAlgebra(Parent, UniqueRepresentation): sage: U = M.open_subset('U'); U Open subset U of the 3-dimensional differentiable manifold M sage: OmegaU = U.mixed_form_algebra(); OmegaU - Graded algebra Omega^*(U) of mixed differential forms on the Open subset - U of the 3-dimensional differentiable manifold M + Graded algebra Omega^*(U) of mixed differential forms on the Open + subset U of the 3-dimensional differentiable manifold M sage: OmegaU.has_coerce_map_from(Omega) True @@ -137,8 +137,8 @@ def __init__(self, vector_field_module): ....: intersection_name='W', restrictions1= x>0, ....: restrictions2= u+v>0) sage: inv = transf.inverse() - sage: from sage.manifolds.differentiable.mixed_form_algebra import ( - ....: MixedFormAlgebra) + sage: from sage.manifolds.differentiable.mixed_form_algebra \ + ....: import MixedFormAlgebra sage: A = MixedFormAlgebra(M.vector_field_module()) sage: TestSuite(A).run() @@ -174,7 +174,7 @@ def __init__(self, vector_field_module): self._vmodule = vector_field_module self._max_deg = vector_field_module._ambient_domain.dim() - def _element_constructor_(self, comp=None, name=None, latex_name=None): + def _element_constructor_(self, comp, name=None, latex_name=None): r""" Construct a mixed form. @@ -190,34 +190,38 @@ def _element_constructor_(self, comp=None, name=None, latex_name=None): manifold M """ + try: + if comp.is_trivial_zero(): + return self.zero() + elif (comp - 1).is_trivial_zero(): + return self.one() + except AttributeError: + if comp == 0: + return self.zero() + if comp == 1: + return self.one() res = self.element_class(self, name=name, latex_name=latex_name) - if comp is None: - return res - elif comp in ZZ and comp == 0: - return self.zero() - elif comp in ZZ and comp == 1: - return self.one() - elif isinstance(comp, (tuple, list)): + if isinstance(comp, (tuple, list)): if len(comp) != self._max_deg + 1: raise IndexError("input list must have " "length {}".format(self._max_deg + 1)) if isinstance(comp, tuple): comp = list(comp) res[:] = comp[:] - elif isinstance(comp, self.Element): - res[:] = comp[:] else: for d in self.irange(): - dmodule = self._domain.diff_form_module(d, dest_map=self._dest_map) + dom = self._domain + dmodule = dom.diff_form_module(d, dest_map=self._dest_map) if dmodule.has_coerce_map_from(comp.parent()): deg = d break else: raise TypeError("cannot convert {} into an element of " "the {}".format(comp, self)) - res[:] = [0] * (self._max_deg + 1) # fill up with zeroes... - res[deg] = comp # ...and set comp at deg of res; - # the coercion is performed here + # fill up with zeroes: + res[:] = [0] * (self._max_deg + 1) + # set comp where it belongs: + res[deg] = comp # coercion is performed here # In case, no other name is given, use name of comp: if name is None: if hasattr(comp, '_name'): @@ -244,7 +248,8 @@ def _an_element_(self): """ res = self.element_class(self) - res[:] = [self._domain.diff_form_module(j, self._dest_map)._an_element_() + dom = self._domain + res[:] = [dom.diff_form_module(j, self._dest_map)._an_element_() for j in self.irange()] return res @@ -277,7 +282,7 @@ def _coerce_map_from_(self, S): if isinstance(S, type(self)): # coercion by domain restriction if (self._domain.is_subset(S._domain) and - self._ambient_domain.is_subset(S._ambient_domain)): + self._ambient_domain.is_subset(S._ambient_domain)): return True # Still, there could be a coerce map if self.irange() != S.irange(): @@ -291,8 +296,9 @@ def _coerce_map_from_(self, S): # Each degree is coercible so there must be a coerce map: return True # Let us check for each degree consecutively: - return any(self._domain.diff_form_module(deg, - self._dest_map).has_coerce_map_from(S) + dom = self._domain + return any(dom.diff_form_module(deg, + self._dest_map).has_coerce_map_from(S) for deg in self.irange()) @cached_method @@ -371,12 +377,13 @@ def _repr_(self): 3-dimensional differentiable manifold M """ - desc = ("Graded algebra " + self._name + " of mixed differential forms ") + desc = "Graded algebra " + self._name + desc += " of mixed differential forms " if self._dest_map is self._domain.identity_map(): desc += "on the {}".format(self._domain) else: - desc += "along the {} mapped into the {} ".format(self._domain, - self._ambient_domain) + desc += "along the {} mapped ".format(self._domain) + desc += "into the {} ".format(self._ambient_domain) if self._dest_map._name is None: dm_name = "unnamed map" else: diff --git a/src/sage/manifolds/differentiable/multivector_module.py b/src/sage/manifolds/differentiable/multivector_module.py index d6b7c6a9c3b..bae717ca2b2 100644 --- a/src/sage/manifolds/differentiable/multivector_module.py +++ b/src/sage/manifolds/differentiable/multivector_module.py @@ -36,7 +36,6 @@ from sage.misc.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent -from sage.rings.integer import Integer from sage.categories.modules import Modules from sage.tensor.modules.ext_pow_free_module import ExtPowerFreeModule from sage.manifolds.differentiable.multivectorfield import ( @@ -301,8 +300,12 @@ def _element_constructor_(self, comp=[], frame=None, name=None, True """ - if isinstance(comp, (int, Integer)) and comp == 0: - return self.zero() + try: + if comp.is_trivial_zero(): + return self.zero() + except AttributeError: + if comp == 0: + return self.zero() if isinstance(comp, (MultivectorField, MultivectorFieldParal)): # coercion by domain restriction if (self._degree == comp._tensor_type[0] @@ -313,6 +316,9 @@ def _element_constructor_(self, comp=[], frame=None, name=None, else: raise TypeError("cannot convert the {} ".format(comp) + "to an element of {}".format(self)) + if not isinstance(comp, (list, tuple)): + raise TypeError("cannot convert the {} ".format(comp) + + "to an element of {}".format(self)) # standard construction resu = self.element_class(self._vmodule, self._degree, name=name, latex_name=latex_name) @@ -393,8 +399,10 @@ def zero(self): zero = self._element_constructor_(name='zero', latex_name='0') for frame in self._domain._frames: if self._dest_map.restrict(frame._domain) == frame._dest_map: - zero._add_comp_unsafe(frame) + zero.add_comp(frame) # (since new components are initialized to zero) + zero._is_zero = True # This element is certainly zero + zero.set_immutable() return zero #### End of Parent methods @@ -722,8 +730,12 @@ def _element_constructor_(self, comp=[], frame=None, name=None, True """ - if isinstance(comp, (int, Integer)) and comp == 0: - return self.zero() + try: + if comp.is_trivial_zero(): + return self.zero() + except AttributeError: + if comp == 0: + return self.zero() if isinstance(comp, (MultivectorField, MultivectorFieldParal)): # coercion by domain restriction if (self._degree == comp._tensor_type[0] @@ -734,6 +746,9 @@ def _element_constructor_(self, comp=[], frame=None, name=None, else: raise TypeError("cannot convert the {} ".format(comp) + "to a multivector field in {}".format(self)) + if not isinstance(comp, (list, tuple)): + raise TypeError("cannot convert the {} ".format(comp) + + "to an element of {}".format(self)) # standard construction resu = self.element_class(self._fmodule, self._degree, name=name, latex_name=latex_name) diff --git a/src/sage/manifolds/differentiable/multivectorfield.py b/src/sage/manifolds/differentiable/multivectorfield.py index 4ac136fb147..867345c3eed 100644 --- a/src/sage/manifolds/differentiable/multivectorfield.py +++ b/src/sage/manifolds/differentiable/multivectorfield.py @@ -162,7 +162,7 @@ def __init__(self, vector_field_module, degree, name=None, latex_name=None): TESTS: Construction via ``parent.element_class``, and not via a direct call - to ``MultivectorField`, to fit with the category framework:: + to ``MultivectorField``, to fit with the category framework:: sage: M = Manifold(2, 'M') sage: U = M.open_subset('U') ; V = M.open_subset('V') @@ -1049,7 +1049,21 @@ def wedge(self, other): sage: s[1,2,3] == a[1]*b[2,3] + a[2]*b[3,1] + a[3]*b[1,2] True + Exterior product with a scalar field:: + + sage: f = M.scalar_field(x, name='f') + sage: s = b.wedge(f); s + 2-vector field f*b on the 3-dimensional differentiable manifold M + sage: s.display() + f*b = x*y^2 d/dx/\d/dy + (x^2 + x*z) d/dx/\d/dz + x*z^2 d/dy/\d/dz + sage: s == f*b + True + sage: s == f.wedge(b) + True + """ + if other._tensor_rank == 0: # wedge product with a scalar field + return self * other if self._domain.is_subset(other._domain): if not self._ambient_domain.is_subset(other._ambient_domain): raise ValueError("incompatible ambient domains for exterior " + diff --git a/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py b/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py index 9df173b42bb..df1746788ab 100644 --- a/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +++ b/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py @@ -724,8 +724,7 @@ def calc_normal(chart): n_comp = (n_form.contract(*args) / factorial(self._dim)).contract( self.ambient_metric().inverse().along(self._immersion)) if self._ambient._dim - self._dim == 1: - n_comp = n_comp / n_comp.norm( - self.ambient_metric().along(self._immersion)) + n_comp = n_comp / n_comp.norm(self.ambient_metric()) norm_rst = self._normal.restrict(chart.domain()) norm_rst.add_comp(max_frame.restrict(chart.domain()))[:] = n_comp[:] diff --git a/src/sage/manifolds/differentiable/scalarfield.py b/src/sage/manifolds/differentiable/scalarfield.py index 3116731150e..58b643bed5f 100644 --- a/src/sage/manifolds/differentiable/scalarfield.py +++ b/src/sage/manifolds/differentiable/scalarfield.py @@ -392,9 +392,9 @@ class DiffScalarField(ScalarField): field:: sage: s = f + 1 ; s - Scalar field on the 2-dimensional differentiable manifold M + Scalar field f+1 on the 2-dimensional differentiable manifold M sage: s.display() - M --> R + f+1: M --> R on U: (x, y) |--> (x^2 + y^2 + 2)/(x^2 + y^2 + 1) on V: (u, v) |--> (2*u^2 + 2*v^2 + 1)/(u^2 + v^2 + 1) sage: (f+1)-1 == f @@ -739,6 +739,12 @@ def differential(self): sage: f.differential() is df True + Instead of invoking the method :meth:`differential`, one may apply the + function ``diff`` to the scalar field:: + + sage: diff(f) is f.differential() + True + Since the exterior derivative of a scalar field (considered a 0-form) is nothing but its differential, ``exterior_derivative()`` is an alias of ``differential()``:: @@ -750,15 +756,6 @@ def differential(self): sage: latex(df) \mathrm{d}f - One may also use the function - :func:`~sage.manifolds.utilities.exterior_derivative` - or its alias :func:`~sage.manifolds.utilities.xder` instead - of the method ``exterior_derivative()``:: - - sage: from sage.manifolds.utilities import xder - sage: xder(f) is f.exterior_derivative() - True - Differential computed on a chart that is not the default one:: sage: c_uvw. = M.chart() @@ -808,7 +805,10 @@ def differential(self): diff_func[i, chart] = func.diff(i) return self._differential - exterior_derivative = differential + exterior_derivative = differential # a scalar field being a 0-form + derivative = differential # allows one to use functional notation, + # e.g. diff(f) for f.differential() + def lie_derivative(self, vector): r""" diff --git a/src/sage/manifolds/differentiable/scalarfield_algebra.py b/src/sage/manifolds/differentiable/scalarfield_algebra.py index 818b081be2a..c0a0a3b59fa 100644 --- a/src/sage/manifolds/differentiable/scalarfield_algebra.py +++ b/src/sage/manifolds/differentiable/scalarfield_algebra.py @@ -406,6 +406,8 @@ def _coerce_map_from_(self, other): sage: CM = M.scalar_field_algebra() sage: CM._coerce_map_from_(SR) True + sage: CM._coerce_map_from_(X.function_ring()) + True sage: U = M.open_subset('U', coord_def={X: x>0}) sage: CU = U.scalar_field_algebra() sage: CM._coerce_map_from_(CU) @@ -414,6 +416,8 @@ def _coerce_map_from_(self, other): True """ + from sage.manifolds.chart_func import ChartFunctionRing + if other is SR: return True # coercion from the base ring (multiplication by the # algebra unit, i.e. self.one()) @@ -421,6 +425,8 @@ def _coerce_map_from_(self, other): # the coercion map elif isinstance(other, DiffScalarFieldAlgebra): return self._domain.is_subset(other._domain) + elif isinstance(other, ChartFunctionRing): + return self._domain.is_subset(other._chart._domain) else: return False diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index e652950a0dc..142ad21557f 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -30,7 +30,8 @@ - Florentin Jaffredo (2018) : series expansion with respect to a given parameter - Michael Jung (2019): improve treatment of the zero element; add method - ``copy_from`` + :meth:`TensorField.copy_from` +- Eric Gourgoulhon (2020): add method :meth:`TensorField.apply_map` REFERENCES: @@ -51,15 +52,14 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** from __future__ import print_function -from six import itervalues, string_types from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ -from sage.structure.element import ModuleElement +from sage.structure.element import ModuleElementWithMutability from sage.tensor.modules.free_module_tensor import FreeModuleTensor from sage.tensor.modules.tensor_with_indices import TensorWithIndices -class TensorField(ModuleElement): +class TensorField(ModuleElementWithMutability): r""" Tensor field along a differentiable manifold. @@ -361,6 +361,35 @@ class TensorField(ModuleElement): sage: s.restrict(U) == f.restrict(U) * t.restrict(U) True + Notice that the zero tensor field is immutable, and therefore its + components cannot be changed:: + + sage: zer = M.tensor_field_module((1, 1)).zero() + sage: zer.is_immutable() + True + sage: zer.set_comp() + Traceback (most recent call last): + ... + AssertionError: the components of an immutable element cannot be + changed + + Other tensor fields can be declared immutable, too:: + + sage: t.is_immutable() + False + sage: t.set_immutable() + sage: t.is_immutable() + True + sage: t.set_comp() + Traceback (most recent call last): + ... + AssertionError: the components of an immutable element cannot be + changed + sage: t.set_name('b') + Traceback (most recent call last): + ... + AssertionError: the name of an immutable element cannot be changed + """ def __init__(self, vector_field_module, tensor_type, name=None, latex_name=None, sym=None, antisym=None, parent=None): @@ -409,7 +438,7 @@ def __init__(self, vector_field_module, tensor_type, name=None, """ if parent is None: parent = vector_field_module.tensor_module(*tensor_type) - ModuleElement.__init__(self, parent) + ModuleElementWithMutability.__init__(self, parent) self._vmodule = vector_field_module self._tensor_type = tuple(tensor_type) self._tensor_rank = self._tensor_type[0] + self._tensor_type[1] @@ -601,6 +630,9 @@ def set_name(self, name=None, latex_name=None): a """ + if self.is_immutable(): + raise AssertionError("the name of an immutable element " + "cannot be changed") if name is not None: self._name = name if latex_name is None: @@ -742,6 +774,15 @@ def _init_components(self, *comp, **kwargs): sage: t.display(Y) t = 2*u d/du*du + v^3 d/dv*du + (u + v) d/dv*dv + TESTS: + + Check that :trac:`29639` is fixed:: + + sage: v = M.vector_field() + sage: v._init_components(1/2, -1) + sage: v.display() + 1/2 d/dx - d/dy + """ comp0 = comp[0] self._is_zero = False # a priori @@ -752,11 +793,11 @@ def _init_components(self, *comp, **kwargs): # frame is actually a pair (frame, chart): frame, chart = frame self.add_comp(frame)[:, chart] = components - elif isinstance(comp0, string_types): + elif isinstance(comp0, str): # For compatibility with previous use of tensor_field(): self.set_name(comp0) else: - if hasattr(comp0, '__getitem__'): + if hasattr(comp0, '__len__') and hasattr(comp0, '__getitem__'): # comp0 is a list/vector of components # otherwise comp is the tuple of components in a specific frame comp = comp0 @@ -898,6 +939,26 @@ def symmetries(self): #### End of simple accessors ##### + def set_immutable(self): + r""" + Set ``self`` and all restrictions of ``self`` immutable. + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: U = M.open_subset('U', coord_def={X: x^2+y^2<1}) + sage: a = M.tensor_field(1, 1, [[1+y,x], [0,x+y]], name='a') + sage: aU = a.restrict(U) + sage: a.set_immutable() + sage: aU.is_immutable() + True + + """ + for rst in self._restrictions.values(): + rst.set_immutable() + super().set_immutable() + def set_restriction(self, rst): r""" Define a restriction of ``self`` to some subdomain. @@ -934,6 +995,9 @@ def set_restriction(self, rst): True """ + if self.is_immutable(): + raise AssertionError("the restrictions of an immutable element " + "cannot be changed") if not isinstance(rst, TensorField): raise TypeError("the argument must be a tensor field") if not rst._domain.is_subset(self._domain): @@ -1107,7 +1171,8 @@ def restrict(self, subdomain, dest_map=None): res._restrictions.update(rst._restrictions) res._restrictions_graph.update(rst._restrictions_graph) rst._extensions_graph.update(res._extensions_graph) - + if self.is_immutable(): + res.set_immutable() # restrictions must be immutable, too self._restrictions[subdomain] = res self._restrictions_graph[subdomain] = res res._extensions_graph.update(self._extensions_graph) @@ -1233,17 +1298,18 @@ def set_comp(self, basis=None): ValueError: no basis could be found for computing the components in the Coordinate frame (V, (d/du,d/dv)) - Since zero is a special element, its components cannot be changed:: + Since zero is an immutable, its components cannot be changed:: sage: z = M.tensor_field_module((1, 1)).zero() sage: z.set_comp(e)[0,1] = u*v Traceback (most recent call last): ... - AssertionError: the components of the zero element cannot be changed + AssertionError: the components of an immutable element cannot be + changed """ - if self is self.parent().zero(): - raise AssertionError("the components of the zero element " + if self.is_immutable(): + raise AssertionError("the components of an immutable element " "cannot be changed") self._is_zero = False # a priori if basis is None: @@ -1365,14 +1431,15 @@ def add_comp(self, basis=None): Since zero is a special element, its components cannot be changed:: sage: z = M.tensor_field_module((1, 1)).zero() - sage: z.add_comp(e)[0,1] = u*v + sage: z.add_comp(e_uv)[1, 1] = u^2 Traceback (most recent call last): ... - AssertionError: the components of the zero element cannot be changed + AssertionError: the components of an immutable element cannot be + changed """ - if self is self.parent().zero(): - raise AssertionError("the components of the zero element " + if self.is_immutable(): + raise AssertionError("the components of an immutable element " "cannot be changed") self._is_zero = False # a priori if basis is None: @@ -1405,7 +1472,9 @@ def add_comp_by_continuation(self, frame, subdomain, chart=None): Components of a vector field on the sphere `S^2`:: sage: M = Manifold(2, 'S^2', start_index=1) - sage: # The two open subsets covered by stereographic coordinates (North and South): + + The two open subsets covered by stereographic coordinates (North and South):: + sage: U = M.open_subset('U') ; V = M.open_subset('V') sage: M.declare_union(U,V) # S^2 is the union of U and V sage: c_xy. = U.chart() ; c_uv. = V.chart() # stereographic coordinates @@ -1444,6 +1513,9 @@ def add_comp_by_continuation(self, frame, subdomain, chart=None): and `a` is defined on the entire manifold `S^2`. """ + if self.is_immutable(): + raise AssertionError("the components of an immutable element " + "cannot be changed") dom = frame._domain if not dom.is_subset(self._domain): raise ValueError("the vector frame is not defined on a subset " + @@ -1541,6 +1613,9 @@ def add_expr_from_subdomain(self, frame, subdomain): on V: (xp, yp) |--> 1/(xp^2 + yp^2) """ + if self.is_immutable(): + raise AssertionError("the expressions of an immutable element " + "cannot be changed") dom = frame._domain if not dom.is_subset(self._domain): raise ValueError("the vector frame is not defined on a subset " + @@ -1548,7 +1623,7 @@ def add_expr_from_subdomain(self, frame, subdomain): if frame not in self.restrict(frame.domain())._components: raise ValueError("the tensor doesn't have an expression in " "the frame"+frame._repr_()) - comp = self._add_comp_unsafe(frame) + comp = self._add_comp_unsafe(frame) # the components stay the same scomp = self.restrict(subdomain).comp(frame.restrict(subdomain)) for ind in comp.non_redundant_index_generator(): comp[[ind]]._express.update(scomp[[ind]]._express) @@ -1868,7 +1943,6 @@ def display_comp(self, frame=None, chart=None, coordinate_labels=True, only_nonzero=only_nonzero, only_nonredundant=only_nonredundant) - def __getitem__(self, args): r""" Return a component with respect to some frame. @@ -2030,6 +2104,9 @@ def copy_from(self, other): False """ + if self.is_immutable(): + raise AssertionError("the components of an immutable element " + "cannot be changed") if other not in self.parent(): raise TypeError("the original must be an element " + "of {}".format(self.parent())) @@ -2180,14 +2257,18 @@ def __eq__(self, other): False sage: t.parent().zero() == 0 True - """ + from .mixed_form import MixedForm + if other is self: return True if other in ZZ: # to compare with 0 if other == 0: return self.is_zero() return False + elif isinstance(other, MixedForm): + # use comparison of MixedForm: + return other == self elif not isinstance(other, TensorField): return False else: # other is another tensor field @@ -2410,7 +2491,7 @@ def _add_(self, other): resu_rst = {} for dom in self._common_subdomains(other): resu_rst[dom] = self._restrictions[dom] + other._restrictions[dom] - some_rst = next(itervalues(resu_rst)) + some_rst = next(iter(resu_rst.values())) resu_sym = some_rst._sym resu_antisym = some_rst._antisym resu = self._vmodule.tensor(self._tensor_type, sym=resu_sym, @@ -2479,7 +2560,7 @@ def _sub_(self, other): resu_rst = {} for dom in self._common_subdomains(other): resu_rst[dom] = self._restrictions[dom] - other._restrictions[dom] - some_rst = next(itervalues(resu_rst)) + some_rst = next(iter(resu_rst.values())) resu_sym = some_rst._sym resu_antisym = some_rst._antisym resu = self._vmodule.tensor(self._tensor_type, sym=resu_sym, @@ -2740,7 +2821,7 @@ def __truediv__(self, scalar): f: M --> R on U: (x, y) |--> 1/(x^2 + y^2 + 1) on V: (u, v) |--> 2/(u^2 + v^2 + 2) - sage: s = a.__div__(f); s + sage: s = a.__truediv__(f); s Tensor field of type (1,1) on the 2-dimensional differentiable manifold M sage: s.display(e_xy) @@ -2751,7 +2832,7 @@ def __truediv__(self, scalar): Division by a number:: - sage: s = a.__div__(2); s + sage: s = a.__truediv__(2); s Tensor field of type (1,1) on the 2-dimensional differentiable manifold M sage: s.display(e_xy) @@ -2770,8 +2851,6 @@ def __truediv__(self, scalar): resu._restrictions[dom] = rst / scalar return resu - __div__ = __truediv__ - def __call__(self, *args): r""" The tensor field acting on 1-forms and vector fields as a @@ -4458,3 +4537,128 @@ def set_calc_order(self, symbol, order, truncate=False): for rst in self._restrictions.values(): rst.set_calc_order(symbol, order, truncate) self._del_derived() + + def apply_map(self, fun, frame=None, chart=None, + keep_other_components=False): + r""" + Apply a function to the coordinate expressions of all components of + ``self`` in a given vector frame. + + This method allows operations like factorization, expansion, + simplification or substitution to be performed on all components of + ``self`` in a given vector frame (see examples below). + + INPUT: + + - ``fun`` -- function to be applied to the coordinate expressions of + the components + - ``frame`` -- (default: ``None``) vector frame defining the + components on which the operation ``fun`` is to be performed; if + ``None``, the default frame of the domain of ``self`` is assumed + - ``chart`` -- (default: ``None``) coordinate chart; if specified, the + operation ``fun`` is performed only on the coordinate expressions + with respect to ``chart`` of the components w.r.t. ``frame``; if + ``None``, the operation ``fun`` is performed on all available + coordinate expressions + - ``keep_other_components`` -- (default: ``False``) determine whether + the components with respect to vector frames distinct from ``frame`` + and having the same domain as ``frame`` are kept. If ``fun`` is + non-destructive, ``keep_other_components`` can be set to ``True``; + otherwise, it is advised to set it to ``False`` (the default) in + order to avoid any inconsistency between the various sets of + components + + EXAMPLES: + + Factorizing all components in the default frame of a vector field:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: a, b = var('a b') + sage: v = M.vector_field(x^2 - y^2, a*(b^2 - b)*x) + sage: v.display() + (x^2 - y^2) d/dx + (b^2 - b)*a*x d/dy + sage: v.apply_map(factor) + sage: v.display() + (x + y)*(x - y) d/dx + a*(b - 1)*b*x d/dy + + Performing a substitution in all components in the default frame:: + + sage: v.apply_map(lambda f: f.subs({a: 2})) + sage: v.display() + (x + y)*(x - y) d/dx + 2*(b - 1)*b*x d/dy + + Specifying the vector frame via the argument ``frame``:: + + sage: P. = M.chart() + sage: X_to_P = X.transition_map(P, [x + 1, y - 1]) + sage: P_to_X = X_to_P.inverse() + sage: v.display(P) + (p^2 - q^2 - 2*p - 2*q) d/dp + (-2*b^2 + 2*(b^2 - b)*p + 2*b) d/dq + sage: v.apply_map(lambda f: f.subs({b: pi}), frame=P.frame()) + sage: v.display(P) + (p^2 - q^2 - 2*p - 2*q) d/dp + (2*pi - 2*pi^2 - 2*(pi - pi^2)*p) d/dq + + Note that the required operation has been performed in all charts:: + + sage: v.display(P.frame(), P) + (p^2 - q^2 - 2*p - 2*q) d/dp + (2*pi - 2*pi^2 - 2*(pi - pi^2)*p) d/dq + sage: v.display(P.frame(), X) + (x + y)*(x - y) d/dp + 2*pi*(pi - 1)*x d/dq + + By default, the components of ``v`` in frames distinct from the + specified one have been deleted:: + + sage: X.frame() in v._components + False + + When requested, they are recomputed by change-of-frame formulas, + thereby enforcing the consistency between the representations in + various vector frames. In particular, we can check that the + substitution of ``b`` by ``pi``, which was asked in ``P.frame()``, + is effective in ``X.frame()`` as well:: + + sage: v.display(X.frame(), X) + (x + y)*(x - y) d/dx + 2*pi*(pi - 1)*x d/dy + + When the requested operation does not change the value of the tensor + field, one can use the keyword argument ``keep_other_components=True``, + in order to avoid the recomputation of the components in other frames:: + + sage: v.apply_map(factor, keep_other_components=True) + sage: v.display() + (x + y)*(x - y) d/dx + 2*pi*(pi - 1)*x d/dy + + The components with respect to ``P.frame()`` have been kept:: + + sage: P.frame() in v._components + True + + One can restrict the operation to expressions in a given chart, via + the argument ``chart``:: + + sage: v.display(X.frame(), P) + (p + q)*(p - q - 2) d/dx + 2*pi*(pi - 1)*(p - 1) d/dy + sage: v.apply_map(expand, chart=P) + sage: v.display(X.frame(), P) + (p^2 - q^2 - 2*p - 2*q) d/dx + (2*pi + 2*pi^2*p - 2*pi^2 - 2*pi*p) d/dy + sage: v.display(X.frame(), X) + (x + y)*(x - y) d/dx + 2*pi*(pi - 1)*x d/dy + + """ + # The dictionary of components w.r.t. frame: + if keep_other_components: + comps = self.comp(frame)._comp + else: + comps = self.set_comp(frame)._comp # set_comp() deletes the + # components in other frames + if chart: + for scalar in comps.values(): + scalar.add_expr(fun(scalar.expr(chart=chart)), chart=chart) + else: + for scalar in comps.values(): + cfunc_dict = {} # new dict of chart functions in order not to + # modify scalar._express while looping on it + for ch, fct in scalar._express.items(): + cfunc_dict[ch] = ch.function(fun(fct.expr())) + scalar._express = cfunc_dict diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index 91a3dc5e59e..ca22433e208 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -41,7 +41,6 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.categories.modules import Modules -from sage.rings.integer import Integer from sage.tensor.modules.tensor_free_module import TensorFreeModule from sage.manifolds.differentiable.tensorfield import TensorField from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal @@ -310,8 +309,12 @@ def _element_constructor_(self, comp=[], frame=None, name=None, True """ - if isinstance(comp, (int, Integer)) and comp == 0: - return self.zero() + try: + if comp.is_trivial_zero(): + return self.zero() + except AttributeError: + if comp == 0: + return self.zero() if isinstance(comp, DiffForm): # coercion of a p-form to a type-(0,p) tensor field: form = comp # for readability @@ -372,12 +375,14 @@ def _element_constructor_(self, comp=[], frame=None, name=None, else: raise TypeError("cannot convert the {}".format(comp) + " to an element of {}".format(self)) - + if not isinstance(comp, (list, tuple)): + raise TypeError("cannot convert the {} ".format(comp) + + "to an element of {}".format(self)) # standard construction resu = self.element_class(self._vmodule, self._tensor_type, name=name, latex_name=latex_name, sym=sym, antisym=antisym) - if comp != []: + if comp: resu.set_comp(frame)[:] = comp return resu @@ -575,9 +580,10 @@ def zero(self): resu = self._element_constructor_(name='zero', latex_name='0') for frame in self._domain._frames: if self._dest_map.restrict(frame._domain) == frame._dest_map: - resu._add_comp_unsafe(frame) + resu.add_comp(frame) # (since new components are initialized to zero) resu._is_zero = True # This element is certainly zero + resu.set_immutable() return resu #*********************************************************************** @@ -793,8 +799,12 @@ def _element_constructor_(self, comp=[], frame=None, name=None, True """ - if isinstance(comp, (int, Integer)) and comp == 0: - return self.zero() + try: + if comp.is_trivial_zero(): + return self.zero() + except AttributeError: + if comp == 0: + return self.zero() if isinstance(comp, DiffFormParal): # coercion of a p-form to a type-(0,p) tensor field: form = comp # for readability @@ -856,11 +866,14 @@ def _element_constructor_(self, comp=[], frame=None, name=None, else: raise TypeError("cannot convert the {}".format(comp) + " to an element of {}".format(self)) + if not isinstance(comp, (list, tuple)): + raise TypeError("cannot convert the {} ".format(comp) + + "to an element of {}".format(self)) # Standard construction resu = self.element_class(self._fmodule, self._tensor_type, name=name, latex_name=latex_name, sym=sym, antisym=antisym) - if comp != []: + if comp: resu.set_comp(frame)[:] = comp return resu diff --git a/src/sage/manifolds/differentiable/tensorfield_paral.py b/src/sage/manifolds/differentiable/tensorfield_paral.py index d1a791b6fc0..641f31784d2 100644 --- a/src/sage/manifolds/differentiable/tensorfield_paral.py +++ b/src/sage/manifolds/differentiable/tensorfield_paral.py @@ -192,7 +192,9 @@ sage: t = M.tensor_field(1, 1, name='T') # Let us restart sage: t[:] = [[1, -x], [x*y, 2]] # by first setting the components in the frame c_xy.frame() - sage: # We now set the components in the frame e with add_comp: + +We now set the components in the frame e with add_comp:: + sage: t.add_comp(e)[:] = [[x+y, 0], [y, -3*x]] The expansion of the tensor field in a given frame is obtained via the method @@ -411,7 +413,6 @@ class TensorFieldParal(FreeModuleTensor, TensorField): sage: for i in M.irange(): ....: for j in M.irange(): ....: t[i,j] = (i+1)**(j+1) - ....: sage: [[ t[i,j] for j in M.irange()] for i in M.irange()] [[1, 1, 1], [2, 4, 8], [3, 9, 27]] @@ -449,9 +450,13 @@ class TensorFieldParal(FreeModuleTensor, TensorField): sage: t = M.tensor_field(2, 0, name='T') # let us restart sage: t[0,0] = 2 # sets the components in the frame e - sage: # We now set the components in the frame f with add_comp: + + We now set the components in the frame f with add_comp:: + sage: t.add_comp(f)[0,0] = -3 - sage: # The components w.r.t. frame e have been kept: + + The components w.r.t. frame e have been kept:: + sage: t._components # random (dictionary output) {Vector frame (M, (e_0,e_1,e_2)): 2-indices components w.r.t. Vector frame (M, (e_0,e_1,e_2)), Vector frame (M, (f_0,f_1,f_2)): 2-indices components w.r.t. Vector frame (M, (f_0,f_1,f_2))} @@ -531,7 +536,9 @@ class TensorFieldParal(FreeModuleTensor, TensorField): no symmetry; no antisymmetry sage: s.symmetries() no symmetry; no antisymmetry - sage: # let us now make b symmetric: + + Let us now make b symmetric:: + sage: b = M.tensor_field(2, 0, sym=(0,1)) sage: b[0,0], b[1,1], b[2,2], b[0,2] = (4,5,6,7) sage: s = a + b diff --git a/src/sage/manifolds/differentiable/vector_bundle.py b/src/sage/manifolds/differentiable/vector_bundle.py index a06bdfa987f..4507d723694 100644 --- a/src/sage/manifolds/differentiable/vector_bundle.py +++ b/src/sage/manifolds/differentiable/vector_bundle.py @@ -220,8 +220,9 @@ def characteristic_class(self, func, **kwargs): Let us introduce the corresponding Levi-Civita connection:: sage: nab = g.connection(); nab - Levi-Civita connection nabla_g associated with the Lorentzian metric - g on the 4-dimensional Lorentzian manifold M + Levi-Civita connection nabla_g associated with the Lorentzian + metric g on the 4-dimensional Lorentzian manifold M + sage: nab.set_immutable() # make nab immutable Of course, `\nabla_g` is flat:: @@ -1461,7 +1462,7 @@ def ambient_domain(self): def destination_map(self): r""" - Return the the destination map. + Return the destination map. OUTPUT: diff --git a/src/sage/manifolds/differentiable/vectorfield.py b/src/sage/manifolds/differentiable/vectorfield.py index 448ad6b9cdb..a69dae09087 100644 --- a/src/sage/manifolds/differentiable/vectorfield.py +++ b/src/sage/manifolds/differentiable/vectorfield.py @@ -1128,10 +1128,44 @@ def dot_product(self, other, metric=None): h(u,v): E^2 --> R (x, y) |--> -(x^3*y - x*y^3)/((x^2 + 1)*y^2 + x^2 + 1) + Scalar product of two vector fields along a curve (a lemniscate of + Gerono):: + + sage: R. = RealLine() + sage: C = M.curve([sin(t), sin(2*t)/2], (t, 0, 2*pi), name='C') + sage: u = C.tangent_vector_field(name='u') + sage: u.display() + u = cos(t) e_x + (2*cos(t)^2 - 1) e_y + sage: I = C.domain(); I + Real interval (0, 2*pi) + sage: v = I.vector_field(cos(t), -1, dest_map=C, name='v') + sage: v.display() + v = cos(t) e_x - e_y + sage: s = u.dot_product(v); s + Scalar field u.v on the Real interval (0, 2*pi) + sage: s.display() + u.v: (0, 2*pi) --> R + t |--> sin(t)^2 + + Scalar product between a vector field along the curve and a vector + field on the ambient Euclidean plane:: + + sage: e_x = M.cartesian_frame()[1] + sage: s = u.dot_product(e_x); s + Scalar field u.e_x on the Real interval (0, 2*pi) + sage: s.display() + u.e_x: (0, 2*pi) --> R + t |--> cos(t) + """ default_metric = metric is None if default_metric: - metric = self._domain.metric() + metric = self._ambient_domain.metric() + dest_map = self.parent().destination_map() + if dest_map != metric.parent().base_module().destination_map(): + metric = metric.along(dest_map) + if dest_map != other.parent().destination_map(): + other = other.along(dest_map) resu = metric(self, other) # From the above operation the name of resu is "g(u,v')" where # g = metric._name, u = self._name, v = other._name @@ -1208,10 +1242,26 @@ def norm(self, metric=None): |v|_h: E^2 --> R (x, y) |--> sqrt((2*x^2 + 1)*y^2 + x^2)/(sqrt(x^2 + 1)*sqrt(y^2 + 1)) + Norm of the tangent vector field to a curve (a lemniscate of Gerono):: + + sage: R. = RealLine() + sage: C = M.curve([sin(t), sin(2*t)/2], (t, 0, 2*pi), name='C') + sage: v = C.tangent_vector_field() + sage: v.display() + C' = cos(t) e_x + (2*cos(t)^2 - 1) e_y + sage: s = v.norm(); s + Scalar field |C'| on the Real interval (0, 2*pi) + sage: s.display() + |C'|: (0, 2*pi) --> R + t |--> sqrt(4*cos(t)^4 - 3*cos(t)^2 + 1) + """ default_metric = metric is None if default_metric: - metric = self._domain.metric() + metric = self._ambient_domain.metric() + dest_map = self.parent().destination_map() + if dest_map != metric.parent().base_module().destination_map(): + metric = metric.along(dest_map) resu = metric(self, self).sqrt() if self._name is not None: if default_metric: @@ -1299,14 +1349,50 @@ def cross_product(self, other, metric=None): sage: w.display() -(x^2 + y^2)*sqrt(x^2 + 1)/(sqrt(y^2 + 1)*sqrt(z^2 + 1)) e_z + Cross product of two vector fields along a curve (arc of a helix):: + + sage: R. = RealLine() + sage: C = M.curve((cos(t), sin(t), t), (t, 0, 2*pi), name='C') + sage: u = C.tangent_vector_field() + sage: u.display() + C' = -sin(t) e_x + cos(t) e_y + e_z + sage: I = C.domain(); I + Real interval (0, 2*pi) + sage: v = I.vector_field(-cos(t), sin(t), 0, dest_map=C) + sage: v.display() + -cos(t) e_x + sin(t) e_y + sage: w = u.cross_product(v); w + Vector field along the Real interval (0, 2*pi) with values on the + Euclidean space E^3 + sage: w.parent().destination_map() + Curve C in the Euclidean space E^3 + sage: w.display() + -sin(t) e_x - cos(t) e_y + (2*cos(t)^2 - 1) e_z + + Cross product between a vector field along the curve and a vector field + on the ambient Euclidean space:: + + sage: e_x = M.cartesian_frame()[1] + sage: w = u.cross_product(e_x); w + Vector field C' x e_x along the Real interval (0, 2*pi) with values + on the Euclidean space E^3 + sage: w.display() + C' x e_x = e_y - cos(t) e_z + """ - if self._domain.dim() != 3: + if self._ambient_domain.dim() != 3: raise ValueError("the cross product is not defined in dimension " + "different from 3") default_metric = metric is None if default_metric: - metric = self._domain.metric() - eps = metric.volume_form(1) + metric = self._ambient_domain.metric() + dest_map = self.parent().destination_map() + if dest_map == metric.parent().base_module().destination_map(): + eps = metric.volume_form(1) + else: + eps = metric.volume_form(1).along(dest_map) + if dest_map != other.parent().destination_map(): + other = other.along(dest_map) resu = eps.contract(1, 2, self.wedge(other), 0, 1) / 2 # The result is named "u x v" only for a default metric: if (default_metric and self._name is not None and @@ -1432,7 +1518,6 @@ class VectorFieldParal(FiniteRankFreeModuleElement, MultivectorFieldParal, sage: f = M.vector_frame('f') sage: for i in range(3): ....: v.set_comp(f)[i] = (i+1)**3 * c_xyz[i] - ....: sage: v.comp(f)[2] 27*z sage: v[f, 2] # equivalent to above diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index a507ab01419..bc0353cb905 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -268,8 +268,12 @@ def _element_constructor_(self, comp=[], frame=None, name=None, True """ - if isinstance(comp, (int, Integer)) and comp == 0: - return self.zero() + try: + if comp.is_trivial_zero(): + return self.zero() + except AttributeError: + if comp == 0: + return self.zero() if isinstance(comp, VectorField): if (self._domain.is_subset(comp._domain) and self._ambient_domain.is_subset(comp._ambient_domain)): @@ -277,8 +281,12 @@ def _element_constructor_(self, comp=[], frame=None, name=None, else: raise ValueError("cannot convert the {} ".format(comp) + "to a vector field in {}".format(self)) + if not isinstance(comp, (list, tuple)): + raise TypeError("cannot convert the {} ".format(comp) + + "to an element of {}".format(self)) + # standard construction resu = self.element_class(self, name=name, latex_name=latex_name) - if comp != []: + if comp: resu.set_comp(frame)[:] = comp return resu @@ -983,7 +991,7 @@ def automorphism(self, name=None, latex_name=None): name=name, latex_name=latex_name) @cached_method - def identity_map(self, name='Id', latex_name=None): + def identity_map(self): r""" Construct the identity map on the vector field module. @@ -991,34 +999,33 @@ def identity_map(self, name='Id', latex_name=None): of tangent-space identity maps along the differentiable manifold `U` over which the vector field module is defined. - INPUT: - - - ``name`` -- (string; default: ``'Id'``) name given to the - identity map - - ``latex_name`` -- (string; optional) LaTeX symbol to denote - the identity map; if none is provided, the LaTeX symbol is - set to ``'\mathrm{Id}'`` if ``name`` is ``'Id'`` and - to ``name`` otherwise - OUTPUT: - instance of :class:`~sage.manifolds.differentiable.automorphismfield.AutomorphismField` - EXAMPLES:: + EXAMPLES: + + Get the identity map on a vector field module:: sage: M = Manifold(2, 'M') sage: XM = M.vector_field_module() - sage: XM.identity_map() + sage: Id = XM.identity_map(); Id Field of tangent-space identity maps on the 2-dimensional differentiable manifold M + If the identity should be renamed, one has to create a copy:: + + sage: Id.set_name('1') + Traceback (most recent call last): + ... + AssertionError: the name of an immutable element cannot be changed + sage: one = Id.copy('1'); one + Field of tangent-space automorphisms 1 on the 2-dimensional + differentiable manifold M + """ - resu = self.general_linear_group().one() - if latex_name is None: - latex_name = name - resu.set_name(name=name, latex_name=latex_name) - return resu + return self.general_linear_group().one() @cached_method def zero(self): @@ -1033,13 +1040,16 @@ def zero(self): sage: XM.zero() Vector field zero on the 2-dimensional differentiable manifold M + """ - elt = self.element_class(self, name='zero', latex_name='0') + zero = self.element_class(self, name='zero', latex_name='0') for frame in self._domain._frames: if self._dest_map.restrict(frame._domain) == frame._dest_map: - elt._add_comp_unsafe(frame) + zero.add_comp(frame) # (since new components are initialized to zero) - return elt + zero._is_zero = True # This element is certainly zero + zero.set_immutable() + return zero def metric(self, name, signature=None, latex_name=None): r""" @@ -1467,8 +1477,12 @@ def _element_constructor_(self, comp=[], basis=None, name=None, True """ - if isinstance(comp, (int, Integer)) and comp == 0: - return self.zero() + try: + if comp.is_trivial_zero(): + return self.zero() + except AttributeError: + if comp == 0: + return self.zero() if isinstance(comp, VectorField): if (self._domain.is_subset(comp._domain) and self._ambient_domain.is_subset(comp._ambient_domain)): @@ -1476,8 +1490,12 @@ def _element_constructor_(self, comp=[], basis=None, name=None, else: raise ValueError("cannot convert the {}".format(comp) + "to a vector field in {}".format(self)) + if not isinstance(comp, (list, tuple)): + raise TypeError("cannot convert the {} ".format(comp) + + "to an element of {}".format(self)) + # standard construction resu = self.element_class(self, name=name, latex_name=latex_name) - if comp != []: + if comp: resu.set_comp(basis=basis)[:] = comp return resu @@ -1700,11 +1718,17 @@ def tensor_module(self, k, l): for more examples and documentation. """ - from sage.manifolds.differentiable.tensorfield_module import \ + try: + return self._tensor_modules[(k,l)] + except KeyError: + if (k, l) == (1, 0): + T = self + else: + from sage.manifolds.differentiable.tensorfield_module import \ TensorFieldFreeModule - if (k,l) not in self._tensor_modules: - self._tensor_modules[(k,l)] = TensorFieldFreeModule(self, (k,l)) - return self._tensor_modules[(k,l)] + T = TensorFieldFreeModule(self, (k,l)) + self._tensor_modules[(k,l)] = T + return T def exterior_power(self, p): r""" @@ -1754,13 +1778,19 @@ def exterior_power(self, p): for more examples and documentation. """ - from sage.manifolds.differentiable.multivector_module import \ + try: + return self._exterior_powers[p] + except KeyError: + if p == 0: + L = self._ring + elif p == 1: + L = self + else: + from sage.manifolds.differentiable.multivector_module import \ MultivectorFreeModule - if p == 0: - return self._ring - if p not in self._exterior_powers: - self._exterior_powers[p] = MultivectorFreeModule(self, p) - return self._exterior_powers[p] + L = MultivectorFreeModule(self, p) + self._exterior_powers[p] = L + return L def dual_exterior_power(self, p): r""" @@ -1808,13 +1838,17 @@ def dual_exterior_power(self, p): for more examples and documentation. """ - from sage.manifolds.differentiable.diff_form_module import \ + try: + return self._dual_exterior_powers[p] + except KeyError: + if p == 0: + L = self._ring + else: + from sage.manifolds.differentiable.diff_form_module import \ DiffFormFreeModule - if p == 0: - return self._ring - if p not in self._dual_exterior_powers: - self._dual_exterior_powers[p] = DiffFormFreeModule(self, p) - return self._dual_exterior_powers[p] + L = DiffFormFreeModule(self, p) + self._dual_exterior_powers[p] = L + return L def general_linear_group(self): r""" diff --git a/src/sage/manifolds/local_frame.py b/src/sage/manifolds/local_frame.py index 507c37055c6..85c28d01850 100644 --- a/src/sage/manifolds/local_frame.py +++ b/src/sage/manifolds/local_frame.py @@ -119,7 +119,7 @@ Let us check that the coframe `(e^i)` is indeed the dual of the vector frame `(e_i)`:: - sage: e_dual[1](e[1]) # the linear form e^1 applied to the local section e_1 + sage: e_dual[1](e[1]) # linear form e^1 applied to local section e_1 Scalar field e^1(e_1) on the Open subset U of the 3-dimensional topological manifold M sage: e_dual[1](e[1]).expr() # the explicit expression of e^1(e_1) @@ -646,7 +646,7 @@ def __init__(self, section_module, symbol, latex_symbol=None, indices=None, smodule.set_default_frame(self) # Initialization of the zero element of the section module: if not isinstance(smodule, FiniteRankFreeModule): - smodule(0).add_comp(self) + smodule(0)._add_comp_unsafe(self) # (since new components are initialized to zero) ### # Add this frame to the list of frames of the overlying vector bundle: diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index c33a3eace7f..68668fb9a46 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -324,8 +324,6 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function -from __future__ import absolute_import from sage.categories.fields import Fields from sage.categories.manifolds import Manifolds @@ -2350,7 +2348,6 @@ def set_simplify_function(self, simplifying_func, method=None): sage: def simpl_trig(a): ....: return a.simplify_trig() - ....: sage: M.set_simplify_function(simpl_trig) sage: s = f + g sage: s.expr() @@ -2373,7 +2370,6 @@ def set_simplify_function(self, simplifying_func, method=None): sage: def simpl_trig_sympy(a): ....: return a.trigsimp() - ....: sage: M.set_simplify_function(simpl_trig_sympy, method='sympy') Then, it becomes active as soon as we change the calculus engine to diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py index 7cfdf7afea2..237da022b3e 100644 --- a/src/sage/manifolds/scalarfield.py +++ b/src/sage/manifolds/scalarfield.py @@ -40,14 +40,13 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** -from six import itervalues - -from sage.structure.element import CommutativeAlgebraElement +from sage.structure.element import (CommutativeAlgebraElement, + ModuleElementWithMutability) from sage.symbolic.expression import Expression from sage.manifolds.chart_func import ChartFunction +from sage.misc.cachefunc import cached_method - -class ScalarField(CommutativeAlgebraElement): +class ScalarField(CommutativeAlgebraElement, ModuleElementWithMutability): r""" Scalar field on a topological manifold. @@ -279,6 +278,48 @@ class ScalarField(CommutativeAlgebraElement): sage: zer is M.scalar_field_algebra().zero() True + The constant scalar fields zero and one are immutable, and therefore + their expressions cannot be changed:: + + sage: zer.is_immutable() + True + sage: zer.set_expr(x) + Traceback (most recent call last): + ... + AssertionError: the expressions of an immutable element cannot be + changed + sage: one = M.one_scalar_field() + sage: one.is_immutable() + True + sage: one.set_expr(x) + Traceback (most recent call last): + ... + AssertionError: the expressions of an immutable element cannot be + changed + + Other scalar fields can be declared immutable, too:: + + sage: c.is_immutable() + False + sage: c.set_immutable() + sage: c.is_immutable() + True + sage: c.set_expr(y^2) + Traceback (most recent call last): + ... + AssertionError: the expressions of an immutable element cannot be + changed + sage: c.set_name('b') + Traceback (most recent call last): + ... + AssertionError: the name of an immutable element cannot be changed + + Immutable elements are hashable and can therefore be used as keys for + dictionaries:: + + sage: {c: 1}[c] + 1 + By definition, a scalar field acts on the manifold's points, sending them to elements of the manifold's base field (real numbers in the present case):: @@ -393,9 +434,9 @@ class ScalarField(CommutativeAlgebraElement): field:: sage: s = f + 1 ; s - Scalar field on the 2-dimensional topological manifold M + Scalar field f+1 on the 2-dimensional topological manifold M sage: s.display() - M --> R + f+1: M --> R on U: (x, y) |--> (x^2 + y^2 + 2)/(x^2 + y^2 + 1) on V: (u, v) |--> (2*u^2 + 2*v^2 + 1)/(u^2 + v^2 + 1) sage: (f+1)-1 == f @@ -852,9 +893,9 @@ class ScalarField(CommutativeAlgebraElement): field:: sage: s = f + 1 ; s - Scalar field on the 2-dimensional topological manifold M + Scalar field f+1 on the 2-dimensional topological manifold M sage: s.display() - M --> R + f+1: M --> R on U: (x, y) |--> (x**2 + y**2 + 2)/(x**2 + y**2 + 1) on V: (u, v) |--> (2*u**2 + 2*v**2 + 1)/(u**2 + v**2 + 1) sage: (f+1)-1 == f @@ -1079,7 +1120,7 @@ def __init__(self, parent, coord_expression=None, chart=None, name=None, sage: TestSuite(f).run() """ - CommutativeAlgebraElement.__init__(self, parent) + super().__init__(parent) # both super classes have same signature domain = parent._domain self._domain = domain self._manifold = domain.manifold() @@ -1144,7 +1185,7 @@ def __bool__(self): if not self._express: # undefined scalar field return True - for funct in itervalues(self._express): + for funct in self._express.values(): if not funct.is_zero(): self._is_zero = False return True @@ -1213,6 +1254,63 @@ def is_trivial_zero(self): return True return all(func.is_trivial_zero() for func in self._express.values()) + def is_trivial_one(self): + r""" + Check if ``self`` is trivially equal to one without any + simplification. + + This method is supposed to be fast as compared with + ``self == 1`` and is intended to be used in library code where + trying to obtain a mathematically correct result by applying + potentially expensive rewrite rules is not desirable. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: f = M.scalar_field({X: 1}) + sage: f.is_trivial_one() + True + sage: f = M.scalar_field(1) + sage: f.is_trivial_one() + True + sage: M.one_scalar_field().is_trivial_one() + True + sage: f = M.scalar_field({X: x+y}) + sage: f.is_trivial_one() + False + + Scalar field defined by means of two charts:: + + sage: U1 = M.open_subset('U1'); X1. = U1.chart() + sage: U2 = M.open_subset('U2'); X2. = U2.chart() + sage: f = M.scalar_field({X1: 1, X2: 1}) + sage: f.is_trivial_one() + True + sage: f = M.scalar_field({X1: 0, X2: 1}) + sage: f.is_trivial_one() + False + + No simplification is attempted, so that ``False`` is returned for + non-trivial cases:: + + sage: f = M.scalar_field({X: cos(x)^2 + sin(x)^2}) + sage: f.is_trivial_one() + False + + On the contrary, the method + :meth:`~sage.structure.element.Element.is_zero` and the direct + comparison to one involve some simplification algorithms and + return ``True``:: + + sage: (f - 1).is_zero() + True + sage: f == 1 + True + + """ + return all(func.is_trivial_one() for func in self._express.values()) + # TODO: Remove this method as soon as ticket #28629 is solved? def is_unit(self): r""" @@ -1267,8 +1365,13 @@ def __eq__(self, other): True """ + from sage.manifolds.differentiable.mixed_form import MixedForm + if other is self: return True + if isinstance(other, MixedForm): + # use comparison of MixedForm: + return other == self if not isinstance(other, ScalarField): # We try a conversion of other to a scalar field, except if # other is None (since this would generate an undefined scalar @@ -1439,6 +1542,9 @@ def set_name(self, name=None, latex_name=None): \Phi """ + if self.is_immutable(): + raise AssertionError("the name of an immutable element " + "cannot be changed") if name is not None: self._name = name if latex_name is None: @@ -1720,17 +1826,19 @@ def set_expr(self, coord_expression, chart=None): sage: z.set_expr(3*y) Traceback (most recent call last): ... - AssertionError: the expressions of the element zero cannot be changed + AssertionError: the expressions of an immutable element cannot be + changed sage: one = M.one_scalar_field() sage: one.set_expr(3*y) Traceback (most recent call last): ... - AssertionError: the expressions of the element 1 cannot be changed + AssertionError: the expressions of an immutable element cannot be + changed """ - if self is self.parent().one() or self is self.parent().zero(): - raise AssertionError("the expressions of the element " - "{} cannot be changed".format(self._name)) + if self.is_immutable(): + raise AssertionError("the expressions of an immutable element " + "cannot be changed") if chart is None: chart = self._domain._def_chart self._express.clear() @@ -1782,17 +1890,19 @@ def add_expr(self, coord_expression, chart=None): sage: z.add_expr(cos(u)-sin(v), c_uv) Traceback (most recent call last): ... - AssertionError: the expressions of the element zero cannot be changed + AssertionError: the expressions of an immutable element cannot be + changed sage: one = M.one_scalar_field() sage: one.add_expr(cos(u)-sin(v), c_uv) Traceback (most recent call last): ... - AssertionError: the expressions of the element 1 cannot be changed + AssertionError: the expressions of an immutable element cannot be + changed """ - if self is self.parent().one() or self is self.parent().zero(): - raise AssertionError("the expressions of the element " - "{} cannot be changed".format(self._name)) + if self.is_immutable(): + raise AssertionError("the expressions of an immutable element " + "cannot be changed") if chart is None: chart = self._domain._def_chart self._express[chart] = chart.function(coord_expression) @@ -1860,6 +1970,9 @@ def add_expr_by_continuation(self, chart, subdomain): on V: (u, v) |--> arctan(1/(u^2 + v^2)) """ + if self.is_immutable(): + raise AssertionError("the expressions of an immutable element " + "cannot be changed") if not chart._domain.is_subset(self._domain): raise ValueError("the chart is not defined on a subset of " + "the scalar field domain") @@ -1895,6 +2008,9 @@ def set_restriction(self, rst): True """ + if self.is_immutable(): + raise AssertionError("the expressions of an immutable element " + "cannot be changed") if not isinstance(rst, ScalarField): raise TypeError("the argument must be a scalar field") if not rst._domain.is_subset(self._domain): @@ -1972,23 +2088,25 @@ def _display_expression(self, chart, result): Helper function for :meth:`display`. """ try: + # get coordinate expression expression = self.coord_function(chart) - coords = chart[:] - if len(coords) == 1: - coords = coords[0] - if chart._domain == self._domain: - if self._name is not None: - result._txt += " " - result._latex += " & " - else: - result._txt += "on " + chart._domain._name + ": " - result._latex += r"\mbox{on}\ " + latex(chart._domain) + \ - r": & " - result._txt += repr(coords) + " |--> " + repr(expression) + "\n" - result._latex += latex(coords) + r"& \longmapsto & " + \ - latex(expression) + r"\\" except (TypeError, ValueError): pass + # if that succeeds, proceed: + coords = chart[:] + if len(coords) == 1: + coords = coords[0] + if chart._domain == self._domain: + if self._name is not None: + result._txt += " " + result._latex += " & " + else: + result._txt += "on " + chart._domain._name + ": " + result._latex += r"\mbox{on}\ " + latex(chart._domain) + \ + r": & " + result._txt += repr(coords) + " |--> " + repr(expression) + "\n" + result._latex += latex(coords) + r"& \longmapsto & " + \ + latex(expression) + r"\\" # Name of the base field: field = self._domain.base_field() @@ -2121,6 +2239,8 @@ def restrict(self, subdomain): resu = type(self)(subdomain.scalar_field_algebra(), coord_expression=sexpress, name=self._name, latex_name=self._latex_name) + if self.is_immutable(): + resu.set_immutable() # restriction must be immutable, too self._restrictions[subdomain] = resu return self._restrictions[subdomain] @@ -2264,7 +2384,7 @@ def common_charts(self, other): if (chart2, chart1) in coord_changes: self.coord_function(chart2, from_chart=chart1) resu.append(chart2) - if resu == []: + if not resu: return None else: return resu @@ -2447,10 +2567,10 @@ def _add_(self, other): True """ - # Special cases: - if self._is_zero: + # Trivial cases: + if self.is_trivial_zero(): return other - if other._is_zero: + if other.is_trivial_zero(): return self # Generic case: com_charts = self.common_charts(other) @@ -2496,11 +2616,13 @@ def _sub_(self, other): True """ - # Special cases: - if self._is_zero: + # Trivial cases: + if self.is_trivial_zero(): return -other - if other._is_zero: + if other.is_trivial_zero(): return self + if self is other: + return self.parent().zero() # Generic case: com_charts = self.common_charts(other) if com_charts is None: @@ -2515,7 +2637,6 @@ def _sub_(self, other): result._latex_name = self._latex_name + '-' + other._latex_name return result - def _mul_(self, other): r""" Scalar field multiplication. @@ -2548,12 +2669,16 @@ def _mul_(self, other): True """ - from sage.tensor.modules.format_utilities import (format_mul_txt, - format_mul_latex) - # Special cases: - if self._is_zero or other._is_zero: + # Trivial cases: + if self.is_trivial_zero() or other.is_trivial_zero(): return self._domain.zero_scalar_field() + if self.is_trivial_one(): + return other + if other.is_trivial_one(): + return self # Generic case: + from sage.tensor.modules.format_utilities import (format_mul_txt, + format_mul_latex) com_charts = self.common_charts(other) if com_charts is None: raise ValueError("no common chart for the multiplication") @@ -2600,10 +2725,10 @@ def _div_(self, other): """ from sage.tensor.modules.format_utilities import format_mul_txt, \ format_mul_latex - # Special cases: - if other._is_zero: + # Trivial cases: + if other.is_trivial_zero(): raise ZeroDivisionError("division of a scalar field by zero") - if self._is_zero: + if self.is_trivial_zero(): return self._domain.zero_scalar_field() # Generic case: com_charts = self.common_charts(other) @@ -2665,8 +2790,19 @@ def _lmul_(self, number): True """ - if number == 0: - return self.parent().zero() + # Trivial cases: + try: + if number.is_trivial_zero(): + return self.parent().zero() + if (number - 1).is_trivial_zero(): + return self + except AttributeError: + # in case base ring is not SR: + if number == 0: + return self.parent().zero() + if number == 1: + return self + # Generic case: result = type(self)(self.parent()) if isinstance(number, Expression): var = number.variables() # possible symbolic variables in number @@ -2691,7 +2827,7 @@ def _lmul_(self, number): var_not_in_chart = [s for s in var if not s in chart_coords] any_in_other_chart = False - if var_not_in_chart != []: + if var_not_in_chart: for other_chart in self._domain.atlas(): other_chart_coords = other_chart[:] for s in var_not_in_chart: @@ -3447,3 +3583,59 @@ def set_calc_order(self, symbol, order, truncate=False): if truncate: expr.simplify() self._del_derived() + + def set_immutable(self): + r""" + Set ``self`` and all restrictions of ``self`` immutable. + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: U = M.open_subset('U', coord_def={X: x^2+y^2<1}) # disk + sage: V = M.open_subset('U', coord_def={X: x>0}) # half plane + sage: f = M.scalar_field(x^2, name='f') + sage: fU = f.restrict(U) + sage: f.set_immutable() + sage: fU.is_immutable() + True + sage: f.restrict(V).is_immutable() + True + + """ + for rst in self._restrictions.values(): + rst.set_immutable() + super().set_immutable() + + @cached_method + def __hash__(self): + r""" + Hash function. + + TESTS:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: f = M.scalar_field(x^2, name='f') + sage: f.set_immutable() + sage: g = M.scalar_field(x^2, name='g') + sage: g.set_immutable() + + Check whether equality implies equality of hash:: + + sage: f == g + True + sage: hash(f) == hash(g) + True + + Let us check that ``f`` can be used as a dictionary key:: + + sage: {f: 1}[f] + 1 + + """ + if self.is_mutable(): + raise ValueError('element must be immutable in order to be ' + 'hashable') + return hash((type(self).__name__, self._domain)) + diff --git a/src/sage/manifolds/scalarfield_algebra.py b/src/sage/manifolds/scalarfield_algebra.py index ba5ed70fd7d..3ee4b383f9d 100644 --- a/src/sage/manifolds/scalarfield_algebra.py +++ b/src/sage/manifolds/scalarfield_algebra.py @@ -451,12 +451,21 @@ def _element_constructor_(self, coord_expression=None, chart=None, (x, y) |--> y^2 + x """ + try: + if coord_expression.is_trivial_zero(): + return self.zero() + elif (coord_expression - 1).is_trivial_zero(): + return self.one() + except AttributeError: + if coord_expression == 0: + return self.zero() + if coord_expression == 1: + return self.one() if isinstance(coord_expression, ScalarField): if self._domain.is_subset(coord_expression._domain): # restriction of the scalar field to self._domain: return coord_expression.restrict(self._domain) else: - ### # Anything going wrong here should produce a readable error: try: # generic constructor: @@ -499,6 +508,8 @@ def _coerce_map_from_(self, other): sage: CM = M.scalar_field_algebra() sage: CM._coerce_map_from_(SR) True + sage: CM._coerce_map_from_(X.function_ring()) + True sage: U = M.open_subset('U', coord_def={X: x>0}) sage: CU = U.scalar_field_algebra() sage: CM._coerce_map_from_(CU) @@ -507,6 +518,7 @@ def _coerce_map_from_(self, other): True """ + from .chart_func import ChartFunctionRing if other is SR: return True # coercion from the base ring (multiplication by the # algebra unit, i.e. self.one()) @@ -514,6 +526,8 @@ def _coerce_map_from_(self, other): # the coercion map elif isinstance(other, ScalarFieldAlgebra): return self._domain.is_subset(other._domain) + elif isinstance(other, ChartFunctionRing): + return self._domain.is_subset(other._chart._domain) else: return False @@ -582,6 +596,7 @@ def zero(self): coord_expression=coord_express, name='zero', latex_name='0') zero._is_zero = True + zero.set_immutable() return zero @cached_method @@ -611,6 +626,7 @@ def one(self): """ coord_express = {chart: chart.one_function() for chart in self._domain.atlas()} - return self.element_class(self, - coord_expression=coord_express, - name='1', latex_name='1') + one = self.element_class(self, coord_expression=coord_express, + name='1', latex_name='1') + one.set_immutable() + return one diff --git a/src/sage/manifolds/section.py b/src/sage/manifolds/section.py index 9c95f4beb10..0d6f93272cc 100644 --- a/src/sage/manifolds/section.py +++ b/src/sage/manifolds/section.py @@ -20,13 +20,13 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #****************************************************************************** -from sage.structure.element import ModuleElement +from sage.structure.element import ModuleElementWithMutability from sage.tensor.modules.free_module_element import FiniteRankFreeModuleElement from sage.tensor.modules.tensor_with_indices import TensorWithIndices from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ -class Section(ModuleElement): +class Section(ModuleElementWithMutability): r""" Section in a vector bundle. @@ -182,6 +182,35 @@ class Section(ModuleElement): sage: c.restrict(U) == cU True + Notice that the zero section is immutable, and therefore its components + cannot be changed:: + + sage: zer = E.section_module().zero() + sage: zer.is_immutable() + True + sage: zer.set_comp() + Traceback (most recent call last): + ... + AssertionError: the components of an immutable element cannot be + changed + + Other sections can be declared immutable, too:: + + sage: c.is_immutable() + False + sage: c.set_immutable() + sage: c.is_immutable() + True + sage: c.set_comp() + Traceback (most recent call last): + ... + AssertionError: the components of an immutable element cannot be + changed + sage: c.set_name('b') + Traceback (most recent call last): + ... + AssertionError: the name of an immutable element cannot be changed + """ def __init__(self, section_module, name=None, latex_name=None): r""" @@ -211,11 +240,12 @@ def __init__(self, section_module, name=None, latex_name=None): sage: TestSuite(s).run() """ - ModuleElement.__init__(self, section_module) + ModuleElementWithMutability.__init__(self, section_module) self._smodule = section_module self._domain = section_module.domain() self._base_space = section_module.base_space() self._vbundle = section_module.vector_bundle() + self._is_zero = False # a priori self._name = name if latex_name is None: self._latex_name = self._name @@ -260,7 +290,13 @@ def __bool__(self): False """ - return any(bool(rst) for rst in self._restrictions.values()) + if self._is_zero: + return False + if any(bool(rst) for rst in self._restrictions.values()): + self._is_zero = False + return True + self._is_zero = True + return False __nonzero__ = __bool__ # For Python2 compatibility @@ -384,6 +420,9 @@ def set_name(self, name=None, latex_name=None): a """ + if self.is_immutable(): + raise AssertionError("the name of an immutable element " + "cannot be changed") if name is not None: self._name = name if latex_name is None: @@ -417,6 +456,32 @@ def _new_instance(self): """ return type(self)(self._smodule) + def _del_restrictions(self): + r""" + Delete the restrictions defined on ``self``. + + TESTS:: + + sage: M = Manifold(2, 'M') + sage: c_xy. = M.chart() + sage: U = M.open_subset('U', coord_def={c_xy: x<0}) + sage: E = M.vector_bundle(2, 'E') + sage: e = E.local_frame('e') + sage: s = E.section() + sage: t = s.restrict(U) + sage: s._restrictions + {Open subset U of the 2-dimensional differentiable manifold M: + Section on the Open subset U of the 2-dimensional differentiable + manifold M with values in the real vector bundle E of rank 2} + sage: s._del_restrictions() + sage: s._restrictions + {} + + """ + self._restrictions.clear() + self._extensions_graph = {self._domain: self} + self._restrictions_graph = {self._domain: self} + def _init_components(self, *comp, **kwargs): r""" Initialize the section components in some given local frames. @@ -458,6 +523,7 @@ def _init_components(self, *comp, **kwargs): """ comp0 = comp[0] + self._is_zero = False # a priori if isinstance(comp0, dict): for frame, components in comp0.items(): chart = None @@ -465,6 +531,9 @@ def _init_components(self, *comp, **kwargs): # frame is actually a pair (frame, chart): frame, chart = frame self.add_comp(frame)[:, chart] = components + elif isinstance(comp0, str): + # For consistency with tensor fields: + self.set_name(comp0) else: if hasattr(comp0, '__getitem__'): # comp0 is a list/vector of components @@ -555,9 +624,13 @@ def set_restriction(self, rst): True """ + if self.is_immutable(): + raise AssertionError("the restrictions of an immutable element " + "cannot be changed") self._restrictions[rst._domain] = rst.copy() self._restrictions[rst._domain].set_name(name=self._name, latex_name=self._latex_name) + self._is_zero = False # a priori def restrict(self, subdomain): r""" @@ -694,12 +767,89 @@ def restrict(self, subdomain): res._restrictions.update(rst._restrictions) res._restrictions_graph.update(rst._restrictions_graph) rst._extensions_graph.update(res._extensions_graph) + if self.is_immutable(): + res.set_immutable() # restrictions must be immutable, too self._restrictions[subdomain] = res self._restrictions_graph[subdomain] = res res._extensions_graph.update(self._extensions_graph) return self._restrictions[subdomain] + def _set_comp_unsafe(self, basis=None): + r""" + Return the components of ``self`` in a given local frame for + assignment. This private method invokes no security check. Use + this method at your own risk. + + The components with respect to other frames having the same domain + as the provided local frame are deleted, in order to avoid any + inconsistency. To keep them, use the method :meth:`_add_comp_unsafe` + instead. + + INPUT: + + - ``basis`` -- (default: ``None``) local frame in which the + components are defined; if none is provided, the components are + assumed to refer to the section domain's default frame + + OUTPUT: + + - components in the given frame, as a + :class:`~sage.tensor.modules.comp.Components`; if such + components did not exist previously, they are created + + EXAMPLES:: + + sage: S2 = Manifold(2, 'S^2', structure='top', start_index=1) + sage: U = S2.open_subset('U') ; V = S2.open_subset('V') # complement of the North and South pole, respectively + sage: S2.declare_union(U,V) + sage: stereoN. = U.chart() # stereographic coordinates from the North pole + sage: stereoS. = V.chart() # stereographic coordinates from the South pole + sage: xy_to_uv = stereoN.transition_map(stereoS, + ....: (x/(x^2+y^2), y/(x^2+y^2)), + ....: intersection_name='W', + ....: restrictions1= x^2+y^2!=0, + ....: restrictions2= u^2+v^2!=0) + sage: W = U.intersection(V) + sage: uv_to_xy = xy_to_uv.inverse() + sage: E = S2.vector_bundle(2, 'E') # define vector bundle + sage: phi_U = E.trivialization('phi_U', domain=U) # define trivializations + sage: phi_V = E.trivialization('phi_V', domain=V) + sage: transf = phi_U.transition_map(phi_V, [[0,x],[y,0]]) + sage: fN = phi_U.frame(); fS = phi_V.frame() # get induced frames + sage: s = E.section(name='s') + sage: s._set_comp_unsafe(fS) + 1-index components w.r.t. Trivialization frame (E|_V, ((phi_V^*e_1),(phi_V^*e_2))) + sage: s._set_comp_unsafe(fS)[1] = u+v + sage: s.display(fS) + s = (u + v) (phi_V^*e_1) + + Setting the components in a new frame (``e``):: + + sage: e = E.local_frame('e', domain=V) + sage: s._set_comp_unsafe(e) + 1-index components w.r.t. Local frame (E|_V, (e_1,e_2)) + sage: s._set_comp_unsafe(e)[1] = u*v + sage: s.display(e) + s = u*v e_1 + + Since the frames ``e`` and ``fS`` are defined on the same domain, the + components w.r.t. ``fS`` have been erased:: + + sage: s.display(phi_V.frame()) + Traceback (most recent call last): + ... + ValueError: no basis could be found for computing the components in + the Trivialization frame (E|_V, ((phi_V^*e_1),(phi_V^*e_2))) + + """ + if basis is None: + basis = self._smodule.default_frame() + if basis is None: # should be "is still None" ;-) + raise ValueError("a frame must be provided for the display") + rst = self.restrict(basis._domain) + return rst._set_comp_unsafe(basis) + def set_comp(self, basis=None): r""" Return the components of ``self`` in a given local frame for assignment. @@ -766,13 +916,85 @@ def set_comp(self, basis=None): """ + if self.is_immutable(): + raise AssertionError("the components of an immutable element " + "cannot be changed") if basis is None: basis = self._smodule.default_frame() if basis is None: # should be "is still None" ;-) raise ValueError("a frame must be provided for the display") rst = self.restrict(basis._domain) + self._is_zero = False # a priori return rst.set_comp(basis) + def _add_comp_unsafe(self, basis=None): + r""" + Return the components of ``self`` in a given local frame for + assignment. This private method invokes no security check. Use + this method at your own risk. + + The components with respect to other frames having the same domain + as the provided local frame are kept. To delete them, use the + method :meth:`_set_comp_unsafe` instead. + + INPUT: + + - ``basis`` -- (default: ``None``) local frame in which the + components are defined; if ``None``, the components are assumed + to refer to the section domain's default frame + + OUTPUT: + + - components in the given frame, as a + :class:`~sage.tensor.modules.comp.Components`; if such + components did not exist previously, they are created + + EXAMPLES:: + + sage: S2 = Manifold(2, 'S^2', structure='top', start_index=1) + sage: U = S2.open_subset('U') ; V = S2.open_subset('V') # complement of the North and South pole, respectively + sage: S2.declare_union(U,V) + sage: stereoN. = U.chart() # stereographic coordinates from the North pole + sage: stereoS. = V.chart() # stereographic coordinates from the South pole + sage: xy_to_uv = stereoN.transition_map(stereoS, (x/(x^2+y^2), y/(x^2+y^2)), + ....: intersection_name='W', restrictions1= x^2+y^2!=0, + ....: restrictions2= u^2+v^2!=0) + sage: W = U.intersection(V) + sage: uv_to_xy = xy_to_uv.inverse() + sage: E = S2.vector_bundle(2, 'E') # define vector bundle + sage: phi_U = E.trivialization('phi_U', domain=U) # define trivializations + sage: phi_V = E.trivialization('phi_V', domain=V) + sage: transf = phi_U.transition_map(phi_V, [[0,1],[1,0]]) + sage: fN = phi_U.frame(); fS = phi_V.frame() # get induced frames + sage: s = E.section(name='s') + sage: s._add_comp_unsafe(fS) + 1-index components w.r.t. Trivialization frame (E|_V, ((phi_V^*e_1),(phi_V^*e_2))) + sage: s._add_comp_unsafe(fS)[1] = u+v + sage: s.display(fS) + s = (u + v) (phi_V^*e_1) + + Setting the components in a new frame:: + + sage: e = E.local_frame('e', domain=V) + sage: s._add_comp_unsafe(e) + 1-index components w.r.t. Local frame (E|_V, (e_1,e_2)) + sage: s._add_comp_unsafe(e)[1] = u*v + sage: s.display(e) + s = u*v e_1 + + The components with respect to ``fS`` are kept:: + + sage: s.display(fS) + s = (u + v) (phi_V^*e_1) + + """ + if basis is None: + basis = self._smodule.default_frame() + if basis is None: # should be "is still None" ;-) + raise ValueError("a frame must be provided for the display") + rst = self.restrict(basis._domain) + return rst._add_comp_unsafe(basis) + def add_comp(self, basis=None): r""" Return the components of ``self`` in a given local frame for assignment. @@ -832,11 +1054,15 @@ def add_comp(self, basis=None): s = (u + v) (phi_V^*e_1) """ + if self.is_immutable(): + raise AssertionError("the components of an immutable element " + "cannot be changed") if basis is None: basis = self._smodule.default_frame() if basis is None: # should be "is still None" ;-) raise ValueError("a frame must be provided for the display") rst = self.restrict(basis._domain) + self._is_zero = False # a priori return rst.add_comp(basis) def add_comp_by_continuation(self, frame, subdomain, chart=None): @@ -1412,6 +1638,74 @@ def __setitem__(self, args, value): frame = self._smodule.default_frame() self.set_comp(frame)[args] = value + def copy_from(self, other): + r""" + Make ``self`` to a copy from ``other``. + + INPUT: + + - ``other`` -- other section in the very same module from which + ``self`` should be a copy of + + .. NOTE:: + + While the derived quantities are not copied, the name is kept. + + .. WARNING:: + + All previous defined components and restrictions will be deleted! + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='top') + sage: U = M.open_subset('U') ; V = M.open_subset('V') + sage: M.declare_union(U,V) # M is the union of U and V + sage: c_xy. = U.chart() ; c_uv. = V.chart() + sage: xy_to_uv = c_xy.transition_map(c_uv, (x+y, x-y), + ....: intersection_name='W', restrictions1= x>0, + ....: restrictions2= u+v>0) + sage: uv_to_xy = xy_to_uv.inverse() + sage: W = U.intersection(V) + sage: E = M.vector_bundle(2, 'E') # define vector bundle + sage: phi_U = E.trivialization('phi_U', domain=U) # define trivializations + sage: phi_V = E.trivialization('phi_V', domain=V) + sage: transf = phi_U.transition_map(phi_V, [[0,x],[x,0]]) + sage: fU = phi_U.frame(); fV = phi_V.frame() + sage: s = E.section(name='s') + sage: s[fU,:] = [2, 1-y] + sage: s.add_comp_by_continuation(fV, U.intersection(V), c_uv) + sage: t = E.section(name='t') + sage: t.copy_from(s) + sage: t.display(fU) + t = 2 (phi_U^*e_1) + (-y + 1) (phi_U^*e_2) + sage: s == t + True + + If the original section is modified, the copy is not:: + + sage: s[fU,0] = -1 + sage: s.display(fU) + s = -(phi_U^*e_1) + (-y + 1) (phi_U^*e_2) + sage: t.display(fU) + t = 2 (phi_U^*e_1) + (-y + 1) (phi_U^*e_2) + sage: s == t + False + + """ + if self.is_immutable(): + raise AssertionError("the components of an immutable element " + "cannot be changed") + if other not in self.parent(): + raise TypeError("the original must be an element " + + "of {}".format(self.parent())) + self._del_derived() + self._del_restrictions() # delete restrictions + name, latex_name = self._name, self._latex_name # keep names + for dom, rst in other._restrictions.items(): + self._restrictions[dom] = rst.copy() + self.set_name(name=name, latex_name=latex_name) + self._is_zero = other._is_zero + def copy(self, name=None, latex_name=None): r""" Return an exact copy of ``self``. @@ -1472,6 +1766,7 @@ def copy(self, name=None, latex_name=None): resu._restrictions[dom] = rst.copy() # Propagate names to all restrictions resu.set_name(name=name, latex_name=latex_name) + resu._is_zero = self._is_zero return resu def _common_subdomains(self, other): @@ -1803,6 +2098,12 @@ def _add_(self, other): True """ + # Case zero: + if self._is_zero: + return other + if other._is_zero: + return self + # Generic case: resu_rst = {} for dom in self._common_subdomains(other): resu_rst[dom] = self._restrictions[dom] + other._restrictions[dom] @@ -1866,6 +2167,12 @@ def _sub_(self, other): True """ + # Case zero: + if self._is_zero: + return -other + if other._is_zero: + return self + # Generic case: resu_rst = {} for dom in self._common_subdomains(other): resu_rst[dom] = self._restrictions[dom] - other._restrictions[dom] @@ -1965,6 +2272,30 @@ def _rmul_(self, scalar): ######### End of ModuleElement arithmetic operators ######## + def set_immutable(self): + r""" + Set ``self`` and all restrictions of ``self`` immutable. + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: U = M.open_subset('U', coord_def={X: x^2+y^2<1}) + sage: E = M.vector_bundle(2, 'E') + sage: e = E.local_frame('e') + sage: s = E.section([1+y,x], name='s') + sage: sU = s.restrict(U) + sage: s.set_immutable() + sage: s.is_immutable() + True + sage: sU.is_immutable() + True + + """ + for rst in self._restrictions.values(): + rst.set_immutable() + super().set_immutable() + #****************************************************************************** class TrivialSection(FiniteRankFreeModuleElement, Section): @@ -2079,6 +2410,7 @@ def __init__(self, section_module, name=None, latex_name=None): self._vbundle = section_module.vector_bundle() self._base_space = section_module.base_space() self._smodule = section_module + self._is_zero = False # a priori # Initialization of derived quantities: self._init_derived() @@ -2162,6 +2494,95 @@ def _new_instance(self): """ return type(self)(self._smodule) + def _set_comp_unsafe(self, basis=None): + r""" + Return the components of the section in a given local frame for + assignment. This private method invokes no security check. Use + this method at your own risk. + + The components with respect to other frames on the same domain are + deleted, in order to avoid any inconsistency. To keep them, use the + method :meth:`_add_comp_unsafe` instead. + + INPUT: + + - ``basis`` -- (default: ``None``) local frame in which the + components are defined; if none is provided, the components are + assumed to refer to the section module's default frame + + OUTPUT: + + - components in the given frame, as an instance of the + class :class:`~sage.tensor.modules.comp.Components`; if such + components did not exist previously, they are created + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='top') + sage: X. = M.chart() + sage: E = M.vector_bundle(2, 'E') + sage: e = E.local_frame('e') # makes E trivial + sage: s = E.section(name='s') + sage: s._set_comp_unsafe(e) + 1-index components w.r.t. Local frame (E|_M, (e_0,e_1)) + sage: s._set_comp_unsafe(e)[0] = 2 + sage: s.display(e) + s = 2 e_0 + + Setting components in a new frame (``f``):: + + sage: f = E.local_frame('f') + sage: s._set_comp_unsafe(f) + 1-index components w.r.t. Local frame (E|_M, (f_0,f_1)) + sage: s._set_comp_unsafe(f)[0] = x + sage: s.display(f) + s = x f_0 + + The components with respect to the frame ``e`` have be erased:: + + sage: s.display(e) + Traceback (most recent call last): + ... + ValueError: no basis could be found for computing the components + in the Local frame (E|_M, (e_0,e_1)) + + Setting components in a frame defined on a subdomain deletes + previously defined components as well:: + + sage: U = M.open_subset('U', coord_def={X: x>0}) + sage: g = E.local_frame('g', domain=U) + sage: s._set_comp_unsafe(g) + 1-index components w.r.t. Local frame (E|_U, (g_0,g_1)) + sage: s._set_comp_unsafe(g)[0] = 1+y + sage: s.display(g) + s = (y + 1) g_0 + sage: s.display(f) + Traceback (most recent call last): + ... + ValueError: no basis could be found for computing the components + in the Local frame (E|_M, (f_0,f_1)) + + """ + if basis is None: + basis = self._smodule.default_frame() + + if basis._domain == self._domain: + # Setting components on the section domain: + return FiniteRankFreeModuleElement._set_comp_unsafe(self, + basis=basis) + # Setting components on a subdomain: + # + # Creating or saving the restriction to the subdomain: + rst = self.restrict(basis._domain) + # Deleting all the components on self._domain and the derived + # quantities: + self._components.clear() + # Restoring the restriction to the subdomain (which has been + # deleted by _del_derived): + self._restrictions[basis._domain] = rst + # The set_comp operation is performed on the subdomain: + return rst._set_comp_unsafe(basis=basis) + def set_comp(self, basis=None): r""" Return the components of the section in a given local frame for @@ -2250,6 +2671,92 @@ class :class:`~sage.tensor.modules.comp.Components`; if such # The set_comp operation is performed on the subdomain: return rst.set_comp(basis=basis) + def _add_comp_unsafe(self, basis=None): + r""" + Return the components of the section in a given local frame for + assignment. + + The components with respect to other frames on the same domain are + kept. To delete them, use the method :meth:`_set_comp_unsafe` instead. + + INPUT: + + - ``basis`` -- (default: ``None``) local frame in which the + components are defined; if none is provided, the components are + assumed to refer to the section module's default frame + + OUTPUT: + + - components in the given frame, as an instance of the + class :class:`~sage.tensor.modules.comp.Components`; if such + components did not exist previously, they are created + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='top') + sage: X. = M.chart() + sage: E = M.vector_bundle(2, 'E') + sage: e = E.local_frame('e') # makes E trivial + sage: s = E.section(name='s') + sage: s._add_comp_unsafe(e) + 1-index components w.r.t. Local frame (E|_M, (e_0,e_1)) + sage: s._add_comp_unsafe(e)[0] = 2 + sage: s.display(e) + s = 2 e_0 + + Adding components with respect to a new frame (``f``):: + + sage: f = E.local_frame('f') + sage: s._add_comp_unsafe(f) + 1-index components w.r.t. Local frame (E|_M, (f_0,f_1)) + sage: s._add_comp_unsafe(f)[0] = x + sage: s.display(f) + s = x f_0 + + The components with respect to the frame ``e`` are kept:: + + sage: s.display(e) + s = 2 e_0 + + Adding components in a frame defined on a subdomain:: + + sage: U = M.open_subset('U', coord_def={X: x>0}) + sage: g = E.local_frame('g', domain=U) + sage: s._add_comp_unsafe(g) + 1-index components w.r.t. Local frame (E|_U, (g_0,g_1)) + sage: s._add_comp_unsafe(g)[0] = 1+y + sage: s.display(g) + s = (y + 1) g_0 + + The components previously defined are kept:: + + sage: s.display(e) + s = 2 e_0 + sage: s.display(f) + s = x f_0 + + """ + if basis is None: + basis = self._smodule.default_frame() + + if basis._domain == self._domain: + # Adding components on the tensor field domain: + # We perform a backup of the restrictions, since + # they are deleted by FreeModuleTensor.add_comp (which + # invokes del_derived()), and restore them afterwards + restrictions_save = self._restrictions.copy() + comp = FiniteRankFreeModuleElement._add_comp_unsafe(self, + basis=basis) + self._restrictions = restrictions_save + return comp + + # Adding components on a subdomain: + # + # Creating or saving the restriction to the subdomain: + rst = self.restrict(basis._domain) + # The add_comp operation is performed on the subdomain: + return rst._add_comp_unsafe(basis=basis) + def add_comp(self, basis=None): r""" Return the components of the section in a given local frame for diff --git a/src/sage/manifolds/section_module.py b/src/sage/manifolds/section_module.py index 6dc18f089c9..cb30e5ce406 100644 --- a/src/sage/manifolds/section_module.py +++ b/src/sage/manifolds/section_module.py @@ -32,7 +32,6 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.misc.cachefunc import cached_method -from sage.rings.integer import Integer from sage.categories.modules import Modules from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule from sage.manifolds.section import Section, TrivialSection @@ -70,35 +69,37 @@ class SectionModule(UniqueRepresentation, Parent): Module of sections on the Möbius bundle:: - sage: M = Manifold(1, 'S^1', structure='top', start_index=1) + sage: M = Manifold(1, 'RP^1', structure='top', start_index=1) sage: U = M.open_subset('U') # the complement of one point - sage: c_t. = U.chart('t:(0,2*pi)') # the standard angle coordinate - sage: V = M.open_subset('V') # the complement of the point t=pi - sage: M.declare_union(U,V) # S^1 is the union of U and V - sage: c_u. = V.chart('u:(0,2*pi)') # the angle t-pi - sage: t_to_u = c_t.transition_map(c_u, (t-pi,), intersection_name='W', - ....: restrictions1 = t!=pi, restrictions2 = u!=pi) - sage: u_to_t = t_to_u.inverse() + sage: c_u. = U.chart() # [1:u] in homogeneous coord. + sage: V = M.open_subset('V') # the complement of the point u=0 + sage: M.declare_union(U,V) # [v:1] in homogeneous coord. + sage: c_v. = V.chart() + sage: u_to_v = c_u.transition_map(c_v, (1/u), + ....: intersection_name='W', + ....: restrictions1 = u!=0, + ....: restrictions2 = v!=0) + sage: v_to_u = u_to_v.inverse() sage: W = U.intersection(V) sage: E = M.vector_bundle(1, 'E') sage: phi_U = E.trivialization('phi_U', latex_name=r'\varphi_U', ....: domain=U) sage: phi_V = E.trivialization('phi_V', latex_name=r'\varphi_V', ....: domain=V) - sage: transf = phi_U.transition_map(phi_V, [[-1]]) + sage: transf = phi_U.transition_map(phi_V, [[u]]) sage: C0 = E.section_module(); C0 - Module C^0(S^1;E) of sections on the 1-dimensional topological manifold - S^1 with values in the real vector bundle E of rank 1 + Module C^0(RP^1;E) of sections on the 1-dimensional topological manifold + RP^1 with values in the real vector bundle E of rank 1 - `C^0(S^1;E)` is a module over the algebra `C^0(S^1)`:: + `C^0(\RR P^1;E)` is a module over the algebra `C^0(\RR P^1)`:: sage: C0.category() Category of modules over Algebra of scalar fields on the 1-dimensional - topological manifold S^1 + topological manifold RP^1 sage: C0.base_ring() is M.scalar_field_algebra() True - However, `C^0(S^1;E)` is not a free module:: + However, `C^0(\RR P^1;E)` is not a free module:: sage: isinstance(C0, FiniteRankFreeModule) False @@ -118,7 +119,7 @@ class SectionModule(UniqueRepresentation, Parent): The zero element of the module:: sage: z = C0.zero() ; z - Section zero on the 1-dimensional topological manifold S^1 with values + Section zero on the 1-dimensional topological manifold RP^1 with values in the real vector bundle E of rank 1 sage: z.display(phi_U.frame()) zero = 0 @@ -132,10 +133,10 @@ class SectionModule(UniqueRepresentation, Parent): True sage: C0_U.coerce_map_from(C0) Coercion map: - From: Module C^0(S^1;E) of sections on the 1-dimensional topological - manifold S^1 with values in the real vector bundle E of rank 1 + From: Module C^0(RP^1;E) of sections on the 1-dimensional topological + manifold RP^1 with values in the real vector bundle E of rank 1 To: Free module C^0(U;E) of sections on the Open subset U of the - 1-dimensional topological manifold S^1 with values in the real vector + 1-dimensional topological manifold RP^1 with values in the real vector bundle E of rank 1 The conversion map is actually the restriction of sections defined @@ -220,16 +221,24 @@ def _element_constructor_(self, comp=[], frame=None, name=None, True """ - if isinstance(comp, (int, Integer)) and comp == 0: - return self.zero() + try: + if comp.is_trivial_zero(): + return self.zero() + except AttributeError: + if comp == 0: + return self.zero() if isinstance(comp, Section): if self._domain.is_subset(comp._domain): return comp.restrict(self._domain) else: raise ValueError("cannot convert the {} ".format(comp) + "to a local section in {}".format(self)) + if not isinstance(comp, (list, tuple)): + raise TypeError("cannot convert the {} ".format(comp) + + "to an element of {}".format(self)) + # standard construction resu = self.element_class(self, name=name, latex_name=latex_name) - if comp != []: + if comp: resu.set_comp(frame)[:] = comp return resu @@ -412,6 +421,7 @@ def zero(self): if frame._domain.is_subset(self._domain): res.add_comp(frame) # (since new components are initialized to zero) + res.set_immutable() return res def default_frame(self): @@ -635,16 +645,24 @@ def _element_constructor_(self, comp=[], basis=None, name=None, True """ - if isinstance(comp, (int, Integer)) and comp == 0: - return self.zero() + try: + if comp.is_trivial_zero(): + return self.zero() + except AttributeError: + if comp == 0: + return self.zero() if isinstance(comp, Section): if self._domain.is_subset(comp._domain): return comp.restrict(self._domain) else: raise ValueError("cannot convert the {}".format(comp) + "to a local section in {}".format(self)) + if not isinstance(comp, (list, tuple)): + raise TypeError("cannot convert the {} ".format(comp) + + "to an element of {}".format(self)) + # standard construction resu = self.element_class(self, name=name, latex_name=latex_name) - if comp != []: + if comp: resu.set_comp(basis)[:] = comp return resu diff --git a/src/sage/manifolds/trivialization.py b/src/sage/manifolds/trivialization.py index 43928224261..ac18adb9099 100644 --- a/src/sage/manifolds/trivialization.py +++ b/src/sage/manifolds/trivialization.py @@ -271,7 +271,11 @@ def domain(self): def frame(self): r""" - Return the standard frame induced by ``self``. + Return the standard frame induced by ``self``. If `\psi` is a + trivialization then the corresponding frame can be obtained by the maps + `p \mapsto \psi^{-1}(p,e_i)`, where `(e_1, \ldots, e_n)` is the standard + basis of `K^n`. We briefly denote `(\psi^*e_i)` instead of + `\psi^{-1}(\cdot,e_i)`. .. SEEALSO:: @@ -661,9 +665,9 @@ def matrix(self): sage: phi_U_to_phi_V = phi_U.transition_map(phi_V, [[0,1],[1,0]]) sage: matrix = phi_U_to_phi_V.matrix(); matrix [Scalar field zero on the Open subset W of the 2-dimensional - topological manifold S^2 Scalar field on the Open subset W of - the 2-dimensional topological manifold S^2] - [ Scalar field on the Open subset W of the 2-dimensional + topological manifold S^2 Scalar field 1 on the Open subset + W of the 2-dimensional topological manifold S^2] + [ Scalar field 1 on the Open subset W of the 2-dimensional topological manifold S^2 Scalar field zero on the Open subset W of the 2-dimensional topological manifold S^2] @@ -674,11 +678,11 @@ def matrix(self): (x, y) |--> 0 (u, v) |--> 0 sage: matrix[0,1].display() - W --> R + 1: W --> R (x, y) |--> 1 (u, v) |--> 1 sage: matrix[1,0].display() - W --> R + 1: W --> R (x, y) |--> 1 (u, v) |--> 1 sage: matrix[1,1].display() diff --git a/src/sage/manifolds/vector_bundle.py b/src/sage/manifolds/vector_bundle.py index 44f10f69b0f..df40ac7bba7 100644 --- a/src/sage/manifolds/vector_bundle.py +++ b/src/sage/manifolds/vector_bundle.py @@ -18,6 +18,11 @@ - Michael Jung (2019) : initial version +REFERENCES: + +- [Lee2013]_ +- [Mil1974]_ + """ #****************************************************************************** @@ -578,6 +583,8 @@ def section_module(self, domain=None, force_free=False): INPUT: + - ``domain`` -- (default: ``None``) the domain on which the module is + defined; if ``None`` the base space is assumed - ``force_free`` -- (default: ``False``) if set to ``True``, force the construction of a *free* module (this implies that `E` is trivial) @@ -587,44 +594,45 @@ def section_module(self, domain=None, force_free=False): :class:`~sage.manifolds.section_module.SectionModule` (or if `E` is trivial, a :class:`~sage.manifolds.section_module.SectionFreeModule`) - representing the module `\Gamma(E)` of continuous sections on - `M` taking values on `E` + representing the module of continuous sections on + `U` taking values in `E` EXAMPLES: - Module of sections on the Möbius bundle:: + Module of sections on the Möbius bundle over the real-projective space + `M=\RR P^1`:: - sage: M = Manifold(1, 'S^1', structure='top', start_index=1) + sage: M = Manifold(1, 'RP^1', structure='top', start_index=1) sage: U = M.open_subset('U') # the complement of one point - sage: c_t. = U.chart('t:(0,2*pi)') # the standard angle coordinate - sage: V = M.open_subset('V') # the complement of the point t=pi - sage: M.declare_union(U,V) # S^1 is the union of U and V - sage: c_u. = V.chart('u:(0,2*pi)') # the angle t-pi - sage: t_to_u = c_t.transition_map(c_u, (t-pi,), + sage: c_u. = U.chart() # [1:u] in homogeneous coord. + sage: V = M.open_subset('V') # the complement of the point u=0 + sage: M.declare_union(U,V) # [v:1] in homogeneous coord. + sage: c_v. = V.chart() + sage: u_to_v = c_u.transition_map(c_v, (1/u), ....: intersection_name='W', - ....: restrictions1 = t!=pi, - ....: restrictions2 = u!=pi) - sage: u_to_t = t_to_u.inverse() + ....: restrictions1 = u!=0, + ....: restrictions2 = v!=0) + sage: v_to_u = u_to_v.inverse() sage: W = U.intersection(V) sage: E = M.vector_bundle(1, 'E') sage: phi_U = E.trivialization('phi_U', latex_name=r'\varphi_U', ....: domain=U) sage: phi_V = E.trivialization('phi_V', latex_name=r'\varphi_V', ....: domain=V) - sage: transf = phi_U.transition_map(phi_V, [[-1]]) + sage: transf = phi_U.transition_map(phi_V, [[u]]) sage: C0 = E.section_module(); C0 - Module C^0(S^1;E) of sections on the 1-dimensional topological - manifold S^1 with values in the real vector bundle E of rank 1 + Module C^0(RP^1;E) of sections on the 1-dimensional topological + manifold RP^1 with values in the real vector bundle E of rank 1 - `C^0(S^1;E)` is a module over the algebra `C^0(M)`:: + `C^0(\RR P^1;E)` is a module over the algebra `C^0(\RR P^1)`:: sage: C0.category() - Category of modules over Algebra of scalar fields on the 1-dimensional - topological manifold S^1 + Category of modules over Algebra of scalar fields on the + 1-dimensional topological manifold RP^1 sage: C0.base_ring() is M.scalar_field_algebra() True - However, `C^0(S^1;E)` is not a free module:: + However, `C^0(\RR P^1;E)` is not a free module:: sage: isinstance(C0, FiniteRankFreeModule) False @@ -645,11 +653,10 @@ def section_module(self, domain=None, force_free=False): sage: C0_U.an_element() Section on the Open subset U of the 1-dimensional topological - manifold S^1 with values in the real vector bundle E of rank 1 + manifold RP^1 with values in the real vector bundle E of rank 1 sage: C0_U.an_element().display(phi_U.frame()) 2 (phi_U^*e_1) - """ if domain is None: domain = self._base_space diff --git a/src/sage/matrix/action.pyx b/src/sage/matrix/action.pyx index 12000c49764..14d2d5cc414 100644 --- a/src/sage/matrix/action.pyx +++ b/src/sage/matrix/action.pyx @@ -50,15 +50,15 @@ AUTHOR: - Robert Bradshaw (2007-09): Initial version. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Robert Bradshaw # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import operator @@ -517,9 +517,10 @@ cdef class PolymapMatrixAction(MatrixMulAction): """ return f._polymap_times_matrix_(mat, self._codomain) + cdef class MatrixSchemePointAction(MatrixMulAction): r""" - Action class for left multiplication of schemes points by matricies. + Action class for left multiplication of schemes points by matrices. """ def __init__(self, G, S): """ diff --git a/src/sage/matrix/all.py b/src/sage/matrix/all.py index cf4a4f30a7f..041624f3853 100644 --- a/src/sage/matrix/all.py +++ b/src/sage/matrix/all.py @@ -1,26 +1,7 @@ -""" -Test for deprecations of imports into global namespace:: - - sage: berlekamp_massey - doctest:warning...: - DeprecationWarning: - Importing berlekamp_massey from here is deprecated. If you need to use it, please import it directly from sage.matrix.berlekamp_massey - See https://trac.sagemath.org/27066 for details. - -""" -from __future__ import absolute_import - from sage.misc.lazy_import import lazy_import - from .matrix_space import MatrixSpace from .constructor import (matrix, Matrix, column_matrix, random_matrix, diagonal_matrix, identity_matrix, block_matrix, block_diagonal_matrix, jordan_block, zero_matrix, ones_matrix, elementary_matrix, companion_matrix) - -lazy_import("sage.matrix.berlekamp_massey", 'berlekamp_massey', - deprecation=27066) - Mat = MatrixSpace - -del absolute_import diff --git a/src/sage/matrix/args.pyx b/src/sage/matrix/args.pyx index 359a342a191..bf4692ad726 100644 --- a/src/sage/matrix/args.pyx +++ b/src/sage/matrix/args.pyx @@ -21,7 +21,9 @@ from cypari2.gen cimport Gen from cypari2.types cimport typ, t_MAT, t_VEC, t_COL, t_VECSMALL, t_LIST, t_STR, t_CLOSURE from .matrix_space import MatrixSpace -from sage.rings.all import ZZ, RDF, CDF +from sage.rings.integer_ring import ZZ +from sage.rings.real_double import RDF +from sage.rings.complex_double import CDF from sage.structure.coerce cimport (coercion_model, is_numpy_type, py_scalar_parent) from sage.structure.element cimport Element, RingElement, Vector diff --git a/src/sage/matrix/benchmark.py b/src/sage/matrix/benchmark.py index 8cd56a1dea8..4c7bd921ad0 100644 --- a/src/sage/matrix/benchmark.py +++ b/src/sage/matrix/benchmark.py @@ -20,7 +20,9 @@ from __future__ import absolute_import from .constructor import random_matrix, Matrix -from sage.rings.all import ZZ, QQ, GF +from sage.rings.integer_ring import ZZ +from sage.rings.rational_field import QQ +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.misc.misc import cputime from cysignals.alarm import AlarmInterrupt, alarm, cancel_alarm diff --git a/src/sage/matrix/compute_J_ideal.py b/src/sage/matrix/compute_J_ideal.py index a82548f3c2d..9cbb1d52b5e 100644 --- a/src/sage/matrix/compute_J_ideal.py +++ b/src/sage/matrix/compute_J_ideal.py @@ -481,7 +481,7 @@ def current_nu(self, p, t, pt_generators, prev_nu): """ import heapq - from sage.misc.misc import verbose + from sage.misc.verbose import verbose if not all((g(self._B) % p**t).is_zero() @@ -751,7 +751,7 @@ def p_minimal_polynomials(self, p, s_max=None): [HR2016]_, Algorithm 5. """ - from sage.misc.misc import verbose + from sage.misc.verbose import verbose from sage.rings.infinity import Infinity deg_mu = self.mu_B.degree() diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx index 080581b9008..4d7ff0fae18 100644 --- a/src/sage/matrix/constructor.pyx +++ b/src/sage/matrix/constructor.pyx @@ -608,7 +608,6 @@ def matrix(*args, **kwds): Some calls using an iterator:: - sage: from six.moves import range sage: matrix(QQ, 3, 6, range(18), sparse=true) [ 0 1 2 3 4 5] [ 6 7 8 9 10 11] diff --git a/src/sage/matrix/matrix0.pxd b/src/sage/matrix/matrix0.pxd index e8fedda5ff0..8fbc2195563 100644 --- a/src/sage/matrix/matrix0.pxd +++ b/src/sage/matrix/matrix0.pxd @@ -49,6 +49,7 @@ cdef class Matrix(sage.structure.element.Matrix): cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, object x) cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j) cdef _coerce_element(self, x) + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j) # Row and column operations cdef check_row_bounds(self, Py_ssize_t r1, Py_ssize_t r2) diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index a163a7006c7..aebb0453322 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -30,7 +30,7 @@ import sage.misc.latex import sage.rings.integer from sage.arith.power cimport generic_power -from sage.misc.misc import verbose, get_verbose +from sage.misc.verbose import verbose, get_verbose from sage.structure.sequence import Sequence from sage.structure.parent cimport Parent @@ -521,6 +521,17 @@ cdef class Matrix(sage.structure.element.Matrix): """ raise NotImplementedError("this must be defined in the derived type.") + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j): + """ + Return 1 if the entry ``(i, j)`` is zero, otherwise 0. + + Might/should be optimized for derived type. + """ + if self.get_unsafe(i, j): + return 0 + else: + return 1 + def add_to_entry(self, Py_ssize_t i, Py_ssize_t j, elt): r""" Add ``elt`` to the entry at position ``(i, j)``. @@ -555,7 +566,8 @@ cdef class Matrix(sage.structure.element.Matrix): ## This function it can very easily !! SEG FAULT !! if you call ## it with invalid input. Use with *extreme* caution. -## EXAMPLES: +## EXAMPLES:: +## ## sage: a = matrix(ZZ,2,range(4)) ## sage: a._get_very_unsafe(0,1) ## 1 @@ -1608,7 +1620,7 @@ cdef class Matrix(sage.structure.element.Matrix): sage: A.change_ring(ZZ) Traceback (most recent call last): ... - TypeError: matrix has denominators so can't change to ZZ. + TypeError: matrix has denominators so can...t change to ZZ. Changing rings preserves subdivisions:: @@ -1755,7 +1767,7 @@ cdef class Matrix(sage.structure.element.Matrix): return self.str() def str(self, rep_mapping=None, zero=None, plus_one=None, minus_one=None, - *, unicode=False, shape=None): + *, unicode=False, shape=None, character_art=False): r""" Return a nice string representation of the matrix. @@ -1800,6 +1812,11 @@ cdef class Matrix(sage.structure.element.Matrix): in accordance with the TeX rendering, while the ASCII rendering defaults to square brackets. + - ``character_art`` -- boolean (default: ``False``); if ``True``, the + result will be of type :class:`~sage.typeset.ascii_art.AsciiArt` or + :class:`~sage.typeset.unicode_art.UnicodeArt` which support line + breaking of wide matrices that exceed the window width + EXAMPLES:: sage: R = PolynomialRing(QQ,6,'z') @@ -1843,6 +1860,19 @@ cdef class Matrix(sage.structure.element.Matrix): ⎢│7 8│9││⎥ ⎣┼───┼─┼┼⎦ + If ``character_art`` is set, the lines of large matrices are wrapped in + a readable way:: + + sage: set_random_seed(0) + sage: matrix.random(RDF, 3, 5).str(unicode=True, character_art=True) + ⎛ -0.27440062056807446 0.5031965950979831 -0.001975438590219314 + ⎜ -0.05461130074681608 -0.033673314214051286 -0.9401270875197381 + ⎝ 0.19906256610645512 0.3242250183948632 0.6026443545751128 + + -0.9467802263760512 0.5056889961514748⎞ + -0.35104242112828943 0.5084492941557279⎟ + -0.9541798283979341 -0.8948790563276592⎠ + TESTS: Prior to :trac:`11544` this could take a full minute to run (2011). :: @@ -1927,8 +1957,15 @@ cdef class Matrix(sage.structure.element.Matrix): brb = right.bottom # - bottom right bracket srb = right.character # - single-row right bracket + if character_art: + if unicode: + from sage.typeset.unicode_art import UnicodeArt as CharacterArt + else: + from sage.typeset.ascii_art import AsciiArt as CharacterArt + if nr == 0 or nc == 0: - return slb + srb + result = slb + srb + return CharacterArt([result]) if character_art else result row_divs, col_divs = self.subdivisions() row_div_counts = [0] * (nr + 1) @@ -1964,7 +2001,6 @@ cdef class Matrix(sage.structure.element.Matrix): width = max(map(len, S)) rows = [] - m = 0 hline = cl.join(hl * ((width + 1)*(b - a) - 1) for a,b in zip([0] + col_divs, col_divs + [nc])) @@ -1989,17 +2025,49 @@ cdef class Matrix(sage.structure.element.Matrix): last_row = len(rows) - 1 if last_row == 0: - return slb + rows[0] + srb - rows[0] = tlb + rows[0] + trb - for r from 1 <= r < last_row: - rows[r] = mlb + rows[r] + mrb - rows[last_row] = blb + rows[last_row] + brb - s = "\n".join(rows) - return s + rows[0] = slb + rows[0] + srb + else: + rows[0] = tlb + rows[0] + trb + for r from 1 <= r < last_row: + rows[r] = mlb + rows[r] + mrb + rows[last_row] = blb + rows[last_row] + brb + + if character_art: + breakpoints = [] + idx = len(tlb) + (col_div_counts[0] if nc > 0 else 0) + width + for c from 1 <= c < nc: + breakpoints.append(idx) + len_sep = max(col_div_counts[c], 1) + idx += len_sep + width + return CharacterArt(rows, breakpoints=breakpoints) + else: + return "\n".join(rows) + + def _ascii_art_(self): + """ + Return an ASCII art representation of this matrix. + + EXAMPLES:: + + sage: set_random_seed(0) + sage: ascii_art(matrix.random(RDF, 3, 5)) # indirect doctest + [ -0.27440062056807446 0.5031965950979831 -0.001975438590219314 + [ -0.05461130074681608 -0.033673314214051286 -0.9401270875197381 + [ 0.19906256610645512 0.3242250183948632 0.6026443545751128 + + -0.9467802263760512 0.5056889961514748] + -0.35104242112828943 0.5084492941557279] + -0.9541798283979341 -0.8948790563276592] + """ + if self._nrows < max_rows and self._ncols < max_cols: + return self.str(character_art=True) + else: + from sage.typeset.ascii_art import AsciiArt + return AsciiArt(repr(self).splitlines()) def _unicode_art_(self): """ - Unicode art representation of matrices + Return a unicode art representation of this matrix. EXAMPLES:: @@ -2019,12 +2087,11 @@ cdef class Matrix(sage.structure.element.Matrix): sage: unicode_art(A) 100 x 100 dense matrix over Integer Ring """ - from sage.typeset.unicode_art import UnicodeArt if self._nrows < max_rows and self._ncols < max_cols: - output = self.str(unicode=True) + return self.str(unicode=True, character_art=True) else: - output = repr(self) - return UnicodeArt(output.splitlines()) + from sage.typeset.unicode_art import UnicodeArt + return UnicodeArt(repr(self).splitlines()) def _latex_(self): r""" @@ -3604,9 +3671,8 @@ cdef class Matrix(sage.structure.element.Matrix): if skew: # testing the diagonal entries to be zero - zero = self.parent().base_ring().zero() for i from 0 <= i < self._nrows: - if self.get_unsafe(i,i) != zero: + if not self.get_is_zero_unsafe(i,i): return False sign = -1 else: @@ -4377,20 +4443,18 @@ cdef class Matrix(sage.structure.element.Matrix): return np def nonzero_positions(self, copy=True, column_order=False): - """ - Returns the sorted list of pairs (i,j) such that self[i,j] != 0. + r""" + Return the sorted list of pairs ``(i,j)`` such that ``self[i,j] != 0``. INPUT: + - ``copy`` -- (default: ``True``) it is safe to change the + resulting list (unless you give the option ``copy=False``) - - ``copy`` - (default: True) It is safe to change the - resulting list (unless you give the option copy=False). - - - ``column_order`` - (default: False) If true, - returns the list of pairs (i,j) such that self[i,j] != 0, but - sorted by columns, i.e., column j=0 entries occur first, then - column j=1 entries, etc. - + - ``column_order`` -- (default: ``False``) If ``True``, + returns the list of pairs ``(i,j)`` such that ``self[i,j] != 0``, but + sorted by columns, i.e., column ``j=0`` entries occur first, then + column ``j=1`` entries, etc. EXAMPLES:: @@ -4420,16 +4484,16 @@ cdef class Matrix(sage.structure.element.Matrix): def _nonzero_positions_by_row(self, copy=True): """ - Returns the list of pairs (i,j) such that self[i,j] != 0. + Returns the list of pairs ``(i,j)`` such that ``self[i,j] != 0``. - It is safe to change the resulting list (unless you give the option copy=False). + It is safe to change the resulting list (unless you give the + option ``copy=False``). EXAMPLES:: sage: M = Matrix(CC, [[1,0],[0,1]], sparse=True) sage: M._nonzero_positions_by_row() [(0, 0), (1, 1)] - """ x = self.fetch('nonzero_positions') if not x is None: @@ -4437,11 +4501,10 @@ cdef class Matrix(sage.structure.element.Matrix): return list(x) return x cdef Py_ssize_t i, j - z = self._base_ring(0) nzp = [] for i from 0 <= i < self._nrows: for j from 0 <= j < self._ncols: - if self.get_unsafe(i,j) != z: + if not self.get_is_zero_unsafe(i,j): nzp.append((i,j)) self.cache('nonzero_positions', nzp) if copy: @@ -4450,19 +4513,18 @@ cdef class Matrix(sage.structure.element.Matrix): def _nonzero_positions_by_column(self, copy=True): """ - Returns the list of pairs (i,j) such that self[i,j] != 0, but - sorted by columns, i.e., column j=0 entries occur first, then - column j=1 entries, etc. + Returns the list of pairs ``(i,j)`` such that ``self[i,j] != 0``, but + sorted by columns, i.e., column ``j=0`` entries occur first, then + column ``j=1`` entries, etc. It is safe to change the resulting list (unless you give the option - copy=False). + ``copy=False``). EXAMPLES:: sage: m=matrix(QQ,2,[1,0,1,1,1,0]) sage: m._nonzero_positions_by_column() [(0, 0), (1, 0), (1, 1), (0, 2)] - """ x = self.fetch('nonzero_positions_by_column') if not x is None: @@ -4470,11 +4532,10 @@ cdef class Matrix(sage.structure.element.Matrix): return list(x) return x cdef Py_ssize_t i, j - z = self._base_ring(0) nzp = [] for j from 0 <= j < self._ncols: for i from 0 <= i < self._nrows: - if self.get_unsafe(i,j) != z: + if not self.get_is_zero_unsafe(i,j): nzp.append((i,j)) self.cache('nonzero_positions_by_column', nzp) if copy: @@ -4483,15 +4544,13 @@ cdef class Matrix(sage.structure.element.Matrix): def nonzero_positions_in_column(self, Py_ssize_t i): """ - Return a sorted list of the integers j such that self[j,i] is - nonzero, i.e., such that the j-th position of the i-th column is - nonzero. + Return a sorted list of the integers ``j`` such that ``self[j,i]`` is + nonzero, i.e., such that the ``j``-th position of the ``i``-th column + is nonzero. INPUT: - - - ``i`` - an integer - + - ``i`` -- an integer OUTPUT: list @@ -4506,7 +4565,7 @@ cdef class Matrix(sage.structure.element.Matrix): sage: a.nonzero_positions_in_column(1) [0, 1] - You'll get an IndexError, if you select an invalid column:: + You will get an ``IndexError`` if you select an invalid column:: sage: a.nonzero_positions_in_column(2) Traceback (most recent call last): @@ -4514,26 +4573,23 @@ cdef class Matrix(sage.structure.element.Matrix): IndexError: matrix column index out of range """ cdef Py_ssize_t j - z = self._base_ring(0) tmp = [] if i<0 or i >= self._ncols: raise IndexError("matrix column index out of range") for j from 0 <= j < self._nrows: - if self.get_unsafe(j,i) != z: + if not self.get_is_zero_unsafe(j,i): tmp.append(j) return tmp def nonzero_positions_in_row(self, Py_ssize_t i): """ - Return the integers j such that self[i,j] is nonzero, i.e., such - that the j-th position of the i-th row is nonzero. + Return the integers ``j`` such that ``self[i,j]`` is nonzero, i.e., + such that the ``j``-th position of the ``i``-th row is nonzero. INPUT: - - - ``i`` - an integer - + - ``i`` -- an integer OUTPUT: list @@ -4552,14 +4608,13 @@ cdef class Matrix(sage.structure.element.Matrix): """ cdef Py_ssize_t j - if i<0 or i >= self._nrows: + if i < 0 or i >= self._nrows: raise IndexError("matrix row index out of range") - z = self._base_ring(0) tmp = [] for j from 0 <= j < self._ncols: - if self.get_unsafe(i,j) != z: + if not self.get_is_zero_unsafe(i,j): tmp.append(j) return tmp @@ -5477,7 +5532,7 @@ cdef class Matrix(sage.structure.element.Matrix): sage: matrix(RR, 1, 1, [2]).inverse_of_unit() Traceback (most recent call last): ... - NotImplementedError + NotImplementedError: Lifting of multivariate polynomials over non-fields is not implemented. sage: R = ZZ.cartesian_product(ZZ) sage: m = matrix(R, 2, [R((2,1)), R((1,1)), R((1,1)), R((1,2))]) @@ -5579,9 +5634,9 @@ cdef class Matrix(sage.structure.element.Matrix): [1] sage: 0^0 1 - + Non-integer (symbolic) exponents are also supported:: - + sage: k = var('k') sage: A = matrix([[2, -1], [1, 0]]) sage: A^(2*k+1) @@ -5774,7 +5829,7 @@ cdef class Matrix(sage.structure.element.Matrix): cdef Py_ssize_t i, j for i from 0 <= i < self._nrows: for j from 0 <= j < self._ncols: - if self.get_unsafe(i,j): + if not self.get_is_zero_unsafe(i,j): return True return False diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index fbdbe4fc5e2..689195dc858 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -2204,6 +2204,112 @@ cdef class Matrix(Matrix0): msg = "Cannot set column with {0} elements over {1}, use change_ring first." raise TypeError(msg.format(v[i].parent(), self.base_ring())) + def zero_pattern_matrix(self, ring=None): + """ + Return a matrix that contains one for corresponding zero entries. + + All other entries are zero. + + INPUT: + + - ``ring`` -- (optional); base ring of the output; default is ``ZZ`` + + OUTPUT: + + A new dense matrix with same dimensions as ``self`` + and with base ring ``ring``. + + EXAMPLES:: + + sage: M = Matrix(ZZ, 2, [1,2,-2,0]) + sage: M.zero_pattern_matrix() + [0 0] + [0 1] + + sage: M = Matrix(QQ, 2, [1,2/3,-2,0]) + sage: M.zero_pattern_matrix() + [0 0] + [0 1] + + Default base ring for the output is ``ZZ``:: + + sage: M.zero_pattern_matrix().base_ring() + Integer Ring + + Specify a different base ring for the output:: + + sage: M.zero_pattern_matrix(GF(2)).base_ring() + Finite Field of size 2 + + Examples for different base rings for ``self``:: + + sage: M = Matrix(Zmod(8), 3, 2, [2, 3, 9, 8, 1, 0]); M + [2 3] + [1 0] + [1 0] + sage: M.zero_pattern_matrix() + [0 0] + [0 1] + [0 1] + + :: + + sage: W. = CyclotomicField(100) + sage: M = Matrix(2, 3, [a, a/2, 0, a^2, a^100-1, a^2 - a]); M + [ a 1/2*a 0] + [ a^2 0 a^2 - a] + sage: M.zero_pattern_matrix() + [0 0 1] + [0 1 0] + + :: + + sage: K. = GF(2^4) + sage: l = [a^2 + 1, a^3 + 1, 0, 0, a, a^3 + a + 1, a + 1, + ....: a + 1, a^2, a^3 + a + 1, a^3 + a, a^3 + a] + sage: M = Matrix(K, 3, 4, l); M + [ a^2 + 1 a^3 + 1 0 0] + [ a a^3 + a + 1 a + 1 a + 1] + [ a^2 a^3 + a + 1 a^3 + a a^3 + a] + sage: M.zero_pattern_matrix() + [0 0 1 1] + [0 0 0 0] + [0 0 0 0] + + :: + + sage: K. = GF(25) + sage: M = Matrix(K, 2, 3, [0, 2, 3, 5, a, a^2]) + sage: M + [ 0 2 3] + [ 0 a a + 3] + sage: M.zero_pattern_matrix() + [1 0 0] + [1 0 0] + + .. NOTE:: + + This method can be optimized by improving + :meth:`get_is_zero_unsafe` for derived matrix classes. + """ + if ring is None: + from sage.rings.all import ZZ + ring = ZZ + + cdef object zero = ring.zero() + cdef object one = ring.one() + cdef Py_ssize_t i, j + + from sage.matrix.matrix_space import MatrixSpace + MZ = MatrixSpace(ring, self._nrows, self._ncols, sparse=False) + cdef Matrix M = MZ(zero, None, None) # initialize with zeros + + for i from 0 <= i < self._nrows: + for j from 0 <= j < self._ncols: + if self.get_is_zero_unsafe(i, j): + M.set_unsafe(i, j, one) + return M + #################################################################################### # Change of representation between dense and sparse. diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index cb978997a32..82e5aab2fa1 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -74,7 +74,7 @@ from sage.structure.sequence import Sequence from sage.structure.coerce cimport coercion_model from sage.structure.element import is_Vector from sage.structure.element cimport have_same_parent -from sage.misc.misc import verbose, get_verbose +from sage.misc.verbose import verbose, get_verbose from sage.categories.fields import Fields from sage.rings.ring import is_Ring from sage.rings.number_field.number_field_base import is_NumberField @@ -3216,13 +3216,13 @@ cdef class Matrix(Matrix1): # Search for a non-zero entry in column m-1 i = -1 for r from m+1 <= r < n: - if self.get_unsafe(r, m-1) != zero: + if not self.get_is_zero_unsafe(r, m-1): i = r break if i != -1: # Found a nonzero entry in column m-1 that is strictly below row m # Now set i to be the first nonzero position >= m in column m-1 - if self.get_unsafe(m,m-1) != zero: + if not self.get_is_zero_unsafe(m,m-1): i = m t = self.get_unsafe(i,m-1) t_inv = None @@ -3704,6 +3704,7 @@ cdef class Matrix(Matrix1): ....: [4, -1, 0, -6, 2]], ....: sparse=False) sage: B = copy(A).sparse_matrix() + sage: from sage.misc.verbose import set_verbose sage: set_verbose(1) sage: D = A.right_kernel(); D verbose 1 () computing a right kernel for 4x5 matrix over Rational Field @@ -8822,6 +8823,40 @@ cdef class Matrix(Matrix1): return False return True + def is_diagonal(self): + """ + Return True if this matrix is a diagonal matrix. + + OUTPUT: + + - whether self is a diagonal matrix. + + EXAMPLES:: + + sage: m = matrix(QQ,2,2,range(4)) + sage: m.is_diagonal() + False + sage: m = matrix(QQ,2,[5,0,0,5]) + sage: m.is_diagonal() + True + sage: m = matrix(QQ,2,[1,0,0,1]) + sage: m.is_diagonal() + True + sage: m = matrix(QQ,2,[1,1,1,1]) + sage: m.is_diagonal() + False + """ + if not self.is_square(): + return False + cdef Py_ssize_t i, j + + for i in range(self._nrows): + for j in range(self._ncols): + if i != j: + if not self.get_unsafe(i,j).is_zero(): + return False + return True + def is_unitary(self): r""" Returns ``True`` if the columns of the matrix are an orthonormal basis. @@ -13275,7 +13310,8 @@ cdef class Matrix(Matrix1): sage: len(str(a.det())) 12215 """ - from sage.rings.all import RDF, RealField + from sage.rings.real_double import RDF + from sage.rings.real_mpfr import RealField try: A = self.change_ring(RDF) m1 = A._hadamard_row_bound() @@ -13468,7 +13504,7 @@ cdef class Matrix(Matrix1): There is also a shortcut for the conjugate transpose, or "Hermitian transpose":: sage: M.H - [ I + 2 6*I + 9] + [ I + 2 6*I + 9] [-4*I + 3 -5*I] Matrices over base rings that can be embedded in the @@ -13585,6 +13621,13 @@ cdef class Matrix(Matrix1): 0.0 sage: a.norm(Infinity) == a.norm(1) True + + TESTS: + + Check that a sparse zero matrix is handled (:trac:`29214`):: + + sage: matrix(CDF, 2, 2, sparse=True).norm(1) + 0.0 """ if self._nrows == 0 or self._ncols == 0: @@ -13597,7 +13640,7 @@ cdef class Matrix(Matrix1): U, S, V = A.SVD() return max(S.list()).real().sqrt() - A = self.apply_map(abs).change_ring(RDF) + A = self.apply_map(abs, R=RDF) # 1-norm: largest column-sum if p == 1: @@ -13682,13 +13725,20 @@ cdef class Matrix(Matrix1): [0.750000000000000 0.800000000000000 0.833333333333333] [0.857142857142857 0.875000000000000 0.888888888888889] [0.900000000000000 0.909090909090909 0.916666666666667] + + We check that :trac:`29700` is fixed:: + + sage: M = matrix(3,[1,1,1,1,0,0,0,1,0]) + sage: A,B = M.diagonalization(QQbar) + sage: _ = A.n() + """ if prec is None: prec = digits_to_bits(digits) try: return self.change_ring(sage.rings.real_mpfr.RealField(prec)) - except TypeError: + except (TypeError, ValueError): # try to return a complex result return self.change_ring(sage.rings.complex_field.ComplexField(prec)) diff --git a/src/sage/matrix/matrix_complex_ball_dense.pyx b/src/sage/matrix/matrix_complex_ball_dense.pyx index 05152cff694..d809009fe5e 100644 --- a/src/sage/matrix/matrix_complex_ball_dense.pyx +++ b/src/sage/matrix/matrix_complex_ball_dense.pyx @@ -1,3 +1,4 @@ +# distutils: libraries = ARB_LIBRARY r""" Arbitrary precision complex ball matrices using Arb diff --git a/src/sage/matrix/matrix_complex_double_dense.pyx b/src/sage/matrix/matrix_complex_double_dense.pyx index fbf27492fdb..9f8b8ec9fc8 100644 --- a/src/sage/matrix/matrix_complex_double_dense.pyx +++ b/src/sage/matrix/matrix_complex_double_dense.pyx @@ -60,26 +60,26 @@ cdef class Matrix_complex_double_dense(Matrix_double_dense): [ 0.3333333333333333 + 0.3333333333333333*I 0.16666666666666669 - 0.16666666666666666*I] [-0.16666666666666666 - 0.3333333333333333*I 0.08333333333333331 + 0.08333333333333333*I] - To compute eigenvalues the use the functions ``left_eigenvectors`` or - ``right_eigenvectors``:: + To compute eigenvalues, use the methods + :meth:`~.Matrix_double_dense.left_eigenvectors` or + :meth:`~.Matrix_double_dense.right_eigenvectors`:: sage: p,e = m.right_eigenvectors() - the result of eigen is a pair (p,e), where p is a list of - eigenvalues and the e is a matrix whose columns are the + The result is a pair ``(p,e)``, where ``p`` is a diagonal matrix of + eigenvalues and ``e`` is a matrix whose columns are the eigenvectors. - To solve a linear system Ax = b where A = [[1,2] and b = [5,6] - [3,4]] - - :: + To solve a linear system `Ax = b` where ``A = [[1,2*I],[3+I,4]]`` and + ``b = [5,6]``:: sage: b = vector(CDF,[5,6]) sage: m.solve_right(b) # abs tol 1e-14 (2.6666666666666665 + 0.6666666666666669*I, -0.3333333333333333 - 1.1666666666666667*I) - See the commands qr, lu, and svd for QR, LU, and singular value - decomposition. + See the methods :meth:`~.Matrix_double_dense.QR`, + :meth:`~.Matrix_double_dense.LU`, and :meth:`.SVD` for QR, LU, and singular + value decomposition. """ def __cinit__(self): global numpy diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index ae26af36122..3b835d58fb7 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -1,3 +1,5 @@ +# distutils: language = c++ +# distutils: libraries = ntl """ Matrices over Cyclotomic Fields @@ -62,7 +64,7 @@ from .misc import matrix_integer_dense_rational_reconstruction from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ from sage.arith.all import previous_prime, binomial -from sage.rings.all import RealNumber +from sage.rings.real_mpfr import create_RealNumber as RealNumber from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -71,7 +73,7 @@ from sage.rings.number_field.number_field_element cimport NumberFieldElement from sage.rings.number_field.number_field_element_quadratic cimport NumberFieldElement_quadratic from sage.structure.proof.proof import get_flag as get_proof_flag -from sage.misc.misc import verbose +from sage.misc.verbose import verbose import math from sage.matrix.matrix_modn_dense_double import MAX_MODULUS as MAX_MODULUS_modn_dense_double @@ -402,6 +404,26 @@ cdef class Matrix_cyclo_dense(Matrix_dense): return x + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j): + r""" + Return 1 if the entry ``(i, j)`` is zero, otherwise 0. + + EXAMPLES:: + + sage: K. = CyclotomicField(3) + sage: A = matrix(K, 4, 3, [0, -z, -2, -2*z + 2, 2*z, z, z, 1-z, 2+3*z, z, 1+z, 0]) + sage: A.zero_pattern_matrix() # indirect doctest + [1 0 0] + [0 0 0] + [0 0 0] + [0 0 1] + """ + cdef int a + for a in range(self._degree): + if not self._matrix.get_is_zero_unsafe(a, j+i*self._ncols): + return False + return True + def _pickle(self): """ Used for pickling matrices. This function returns the @@ -736,15 +758,16 @@ cdef class Matrix_cyclo_dense(Matrix_dense): EXAMPLES: - We create a cyclotomic matrix.:: + We create a cyclotomic matrix:: sage: W. = CyclotomicField(5) sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2]) - We make a copy of A.:: + We make a copy of A:: + sage: C = A.__copy__() - We make another reference to A.:: + We make another reference to A:: sage: B = A @@ -754,7 +777,7 @@ cdef class Matrix_cyclo_dense(Matrix_dense): sage: A[0,0] 10 - Changing the copy does not change A.:: + Changing the copy does not change A:: sage: C[0,0] = 20 sage: C[0,0] @@ -1486,6 +1509,7 @@ cdef class Matrix_cyclo_dense(Matrix_dense): [4, 9, 1, 3] The reduction matrix is cached:: + sage: w._reduction_matrix(7) is w._reduction_matrix(7) True """ @@ -1499,7 +1523,7 @@ cdef class Matrix_cyclo_dense(Matrix_dense): pass K = self.base_ring() phi = K.defining_polynomial() - from sage.rings.all import GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from .constructor import matrix F = GF(p) aa = [a for a, _ in phi.change_ring(F).roots()] diff --git a/src/sage/matrix/matrix_double_dense.pyx b/src/sage/matrix/matrix_double_dense.pyx index ddbf4e43209..988477a4a42 100644 --- a/src/sage/matrix/matrix_double_dense.pyx +++ b/src/sage/matrix/matrix_double_dense.pyx @@ -602,9 +602,6 @@ cdef class Matrix_double_dense(Matrix_dense): [ 3.0 + 9.0*I 4.0 + 16.0*I 5.0 + 25.0*I] [6.0 + 36.0*I 7.0 + 49.0*I 8.0 + 64.0*I] sage: B.condition() - doctest:warning - ... - ...ComplexWarning: Casting complex values to real discards the imaginary part 203.851798... sage: B.condition(p='frob') 203.851798... @@ -689,7 +686,7 @@ cdef class Matrix_double_dense(Matrix_dense): import numpy import sage.rings.infinity import sage.rings.integer - import sage.rings.real_double + from sage.rings.real_double import RDF if p == sage.rings.infinity.Infinity: p = numpy.inf elif p == -sage.rings.infinity.Infinity: @@ -710,7 +707,7 @@ cdef class Matrix_double_dense(Matrix_dense): if c == numpy.inf: return sage.rings.infinity.Infinity else: - return sage.rings.real_double.RDF(c) + return RDF(c.real if numpy.iscomplexobj(c) else c) def norm(self, p=2): r""" @@ -981,6 +978,7 @@ cdef class Matrix_double_dense(Matrix_dense): singular values are always real. :: sage: A = matrix(CDF, 4, range(16)) + sage: from sage.misc.verbose import set_verbose sage: set_verbose(1) sage: sv = A.singular_values(eps='auto'); sv verbose 1 () singular values, @@ -1005,7 +1003,7 @@ cdef class Matrix_double_dense(Matrix_dense): - Rob Beezer - (2011-02-18) """ - from sage.misc.misc import verbose + from sage.misc.verbose import verbose from sage.rings.real_double import RDF global scipy # get SVD decomposition, which is a cached quantity @@ -1050,7 +1048,7 @@ cdef class Matrix_double_dense(Matrix_dense): For an `m\times n` matrix ``A`` this method returns a triple of immutable matrices ``P, L, U`` such that - - ``P*A = L*U`` + - ``A = P*L*U`` - ``P`` is a square permutation matrix, of size `m\times m`, so is all zeroes, but with exactly a single one in each row and each column. @@ -1072,27 +1070,31 @@ cdef class Matrix_double_dense(Matrix_dense): the zero entries of ``U``. .. NOTE:: - - Sometimes this decomposition is written as ``A=P*L*U``, - where ``P`` represents the inverse permutation and is + The behaviour of ``LU()`` has changed in Sage version 9.1. + Earlier, ``LU()`` returned ``P,L,U`` such that ``P*A=L*U``, + where ``P`` represents the permutation and is the matrix inverse of the ``P`` returned by this method. The computation of this matrix inverse can be accomplished quickly with just a transpose as the matrix is orthogonal/unitary. + For details see :trac:`18365`. + EXAMPLES:: sage: m = matrix(RDF,4,range(16)) sage: P,L,U = m.LU() - sage: P*m - [12.0 13.0 14.0 15.0] + sage: P*L*U # rel tol 2e-16 [ 0.0 1.0 2.0 3.0] - [ 8.0 9.0 10.0 11.0] [ 4.0 5.0 6.0 7.0] - sage: L*U # rel tol 2e-16 - [12.0 13.0 14.0 15.0] - [ 0.0 1.0 2.0 3.0] [ 8.0 9.0 10.0 11.0] - [ 4.0 5.0 6.0 7.0] + [12.0 13.0 14.0 15.0] + + Below example illustrates the change in behaviour of ``LU()``. :: + + sage: m == P*L*U + True + sage: P*m == L*U + False :trac:`10839` made this routine available for rectangular matrices. :: @@ -1104,11 +1106,11 @@ cdef class Matrix_double_dense(Matrix_dense): [24.0 25.0 26.0 27.0 28.0 29.0] sage: P, L, U = A.LU() sage: P + [0.0 1.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0 1.0] - [1.0 0.0 0.0 0.0 0.0] [0.0 0.0 1.0 0.0 0.0] [0.0 0.0 0.0 1.0 0.0] - [0.0 1.0 0.0 0.0 0.0] + [1.0 0.0 0.0 0.0 0.0] sage: L.zero_at(0) # Use zero_at(0) to get rid of signed zeros [ 1.0 0.0 0.0 0.0 0.0] [ 0.0 1.0 0.0 0.0 0.0] @@ -1121,13 +1123,13 @@ cdef class Matrix_double_dense(Matrix_dense): [ 0.0 0.0 0.0 0.0 0.0 0.0] [ 0.0 0.0 0.0 0.0 0.0 0.0] [ 0.0 0.0 0.0 0.0 0.0 0.0] - sage: P*A-L*U + sage: P.transpose()*A-L*U [0.0 0.0 0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0 0.0 0.0] [0.0 0.0 0.0 0.0 0.0 0.0] - sage: P.transpose()*L*U + sage: P*L*U [ 0.0 1.0 2.0 3.0 4.0 5.0] [ 6.0 7.0 8.0 9.0 10.0 11.0] [12.0 13.0 14.0 15.0 16.0 17.0] @@ -1145,7 +1147,7 @@ cdef class Matrix_double_dense(Matrix_dense): Full MatrixSpace of 5 by 5 dense matrices over Real Double Field sage: U.parent() Full MatrixSpace of 5 by 0 dense matrices over Real Double Field - sage: P*A-L*U + sage: A-P*L*U [] The results are immutable since they are cached. :: @@ -1189,14 +1191,12 @@ cdef class Matrix_double_dense(Matrix_dense): if numpy is None: import numpy PM, LM, UM = scipy.linalg.lu(self._matrix_numpy) - # Numpy has a different convention than we had with GSL - # So we invert (transpose) the P to match our prior behavior # TODO: It's an awful waste to store a huge matrix for P, which # is just a simple permutation, really. P = self._new(m, m) L = self._new(m, m) U = self._new(m, n) - P._matrix_numpy = PM.T.copy() + P._matrix_numpy = numpy.ascontiguousarray(PM) L._matrix_numpy = numpy.ascontiguousarray(LM) U._matrix_numpy = numpy.ascontiguousarray(UM) PLU = (P, L, U) @@ -2269,8 +2269,7 @@ cdef class Matrix_double_dense(Matrix_dense): sage: A.is_unitary() False - The smallest cases. The Schur decomposition used by the - orthonormal algorithm will fail on a matrix of size zero. :: + The smallest cases:: sage: P = matrix(CDF, 0, 0) sage: P.is_unitary(algorithm='naive') @@ -2282,9 +2281,7 @@ cdef class Matrix_double_dense(Matrix_dense): sage: P = matrix(CDF, 0, 0,) sage: P.is_unitary(algorithm='orthonormal') - Traceback (most recent call last): - ... - error: ((lwork==-1)||(lwork >= MAX(1,2*n))) failed for 3rd keyword lwork: zgees:lwork=0 + True TESTS:: @@ -2309,6 +2306,12 @@ cdef class Matrix_double_dense(Matrix_dense): - Rob Beezer (2011-05-04) """ + if self.dimensions() == (0,0): + # The "orthonormal" algorithm would otherwise fail in this + # corner case. Returning `True` is consistent with the + # other implementations of this method. + return True + global numpy try: tol = float(tol) diff --git a/src/sage/matrix/matrix_generic_sparse.pyx b/src/sage/matrix/matrix_generic_sparse.pyx index bbf0015ca8a..51858e0e1af 100644 --- a/src/sage/matrix/matrix_generic_sparse.pyx +++ b/src/sage/matrix/matrix_generic_sparse.pyx @@ -202,6 +202,20 @@ cdef class Matrix_generic_sparse(matrix_sparse.Matrix_sparse): cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j): return self._entries.get((i,j), self._zero) + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j): + """ + Return 1 if the entry ``(i, j)`` is zero, otherwise 0. + + EXAMPLES:: + + sage: R. = Zmod(5)['a','b'] + sage: m = matrix(R,2,4, {(1,3): a, (0,0):b}, sparse=True) + sage: m.zero_pattern_matrix() # indirect doctest + [0 1 1 1] + [1 1 1 0] + """ + return (i,j) not in self._entries + def _pickle(self): version = 0 return self._entries, version @@ -210,7 +224,8 @@ cdef class Matrix_generic_sparse(matrix_sparse.Matrix_sparse): """ EXAMPLES:: - sage: a = matrix([[1,10],[3,4]],sparse=True); a + sage: R. = ZZ[] + sage: a = matrix(R, [[1,10],[3,4]],sparse=True); a [ 1 10] [ 3 4] sage: loads(dumps(a)) == a @@ -239,7 +254,8 @@ cdef class Matrix_generic_sparse(matrix_sparse.Matrix_sparse): """ EXAMPLES:: - sage: a = matrix([[1,10],[3,4]],sparse=True); a + sage: R. = QQ[] + sage: a = matrix(R, [[1,10],[3,4]],sparse=True); a [ 1 10] [ 3 4] sage: a+a @@ -248,7 +264,7 @@ cdef class Matrix_generic_sparse(matrix_sparse.Matrix_sparse): :: - sage: a = matrix([[1,10,-5/3],[2/8,3,4]],sparse=True); a + sage: a = matrix(R, [[1,10,-5/3],[2/8,3,4]], sparse=True); a [ 1 10 -5/3] [ 1/4 3 4] sage: a+a diff --git a/src/sage/matrix/matrix_gf2e_dense.pxd b/src/sage/matrix/matrix_gf2e_dense.pxd index 6050b3da431..c8d81d279f9 100644 --- a/src/sage/matrix/matrix_gf2e_dense.pxd +++ b/src/sage/matrix/matrix_gf2e_dense.pxd @@ -1,4 +1,5 @@ from sage.libs.m4rie cimport mzed_t +from sage.libs.m4ri cimport m4ri_word from .matrix_dense cimport Matrix_dense @@ -6,6 +7,7 @@ cdef class Matrix_gf2e_dense(Matrix_dense): cdef mzed_t *_entries cdef object _one cdef object _zero + cdef m4ri_word _zero_word # m4ri_word representation of _zero cpdef Matrix_gf2e_dense _multiply_newton_john(Matrix_gf2e_dense self, Matrix_gf2e_dense right) cpdef Matrix_gf2e_dense _multiply_karatsuba(Matrix_gf2e_dense self, Matrix_gf2e_dense right) diff --git a/src/sage/matrix/matrix_gf2e_dense.pyx b/src/sage/matrix/matrix_gf2e_dense.pyx index d61bae81e88..e0fde0f15f1 100644 --- a/src/sage/matrix/matrix_gf2e_dense.pyx +++ b/src/sage/matrix/matrix_gf2e_dense.pyx @@ -1,5 +1,10 @@ +# -*- coding: utf-8 -*- +# distutils: libraries = m4rie M4RI_LIBRARIES m +# distutils: library_dirs = M4RI_LIBDIR +# distutils: include_dirs = M4RI_INCDIR +# distutils: extra_compile_args = M4RI_CFLAGS """ -Dense matrices over `\GF{2^e}` for `2 <= e <= 10` using the M4RIE library +Dense matrices over `\GF{2^e}` for `2 \leq e \leq 16` using the M4RIE library The M4RIE library offers two matrix representations: @@ -89,7 +94,7 @@ from sage.structure.element cimport Matrix, Vector from sage.structure.element cimport ModuleElement, Element, RingElement from sage.structure.richcmp cimport rich_to_bool -from sage.rings.all import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.misc.randstate cimport randstate, current_randstate from sage.matrix.matrix_mod2_dense cimport Matrix_mod2_dense @@ -183,6 +188,7 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): # cache elements self._zero = self._base_ring(0) + self._zero_word = poly_to_word(self._zero) self._one = self._base_ring(1) def __dealloc__(self): @@ -201,7 +207,7 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): def __init__(self, parent, entries=None, copy=None, bint coerce=True): r""" - Create new matrix over `GF(2^e)` for 2<=e<=10. + Create new matrix over `GF(2^e)` for `2 \leq e \leq 16`. INPUT: @@ -290,6 +296,20 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): cdef int r = mzed_read_elem(self._entries, i, j) return word_to_poly(r, self._base_ring) + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j): + r""" + Return 1 if the entry ``(i, j)`` is zero, otherwise 0. + + EXAMPLES:: + + sage: K. = GF(2^4) + sage: A = Matrix(K, 2, 2, a) + sage: A.zero_pattern_matrix() # indirect doctest + [0 1] + [1 0] + """ + return mzed_read_elem(self._entries, i, j) == self._zero_word + cpdef _add_(self, right): """ Return A+B @@ -707,6 +727,24 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): return A + def __bool__(self): + """ + Return if ``self`` is a zero matrix or not. + + EXAMPLES:: + + sage: K. = GF(2^4) + sage: A = Matrix(K, 2, 2, a) + sage: bool(A) + True + sage: zero = MatrixSpace(K, 3, 3).zero() + sage: bool(zero) + False + """ + if self._nrows and self._ncols: + return not mzed_is_zero(self._entries) + return False + def _list(self): """ EXAMPLES:: @@ -950,7 +988,7 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): i = 0 while i < self._nrows: for j from i <= j < nc: - if self.get_unsafe(i,j): + if not self.get_is_zero_unsafe(i,j): pivots.append(j) i += 1 break @@ -981,8 +1019,13 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): cdef Matrix_gf2e_dense A A = Matrix_gf2e_dense.__new__(Matrix_gf2e_dense, self._parent, 0, 0, 0) - if self._nrows and self._nrows == self._ncols: + if self.rank() != self._nrows: + raise ZeroDivisionError("Matrix does not have full rank.") + + if self._nrows: + sig_on() mzed_invert_newton_john(A._entries, self._entries) + sig_off() return A diff --git a/src/sage/matrix/matrix_gfpn_dense.pxd b/src/sage/matrix/matrix_gfpn_dense.pxd index 39b524a20f9..5929141ff89 100644 --- a/src/sage/matrix/matrix_gfpn_dense.pxd +++ b/src/sage/matrix/matrix_gfpn_dense.pxd @@ -15,6 +15,7 @@ from sage.libs.meataxe cimport * cdef class FieldConverter_class: cdef field # A function converting an int to a field element + cdef FEL zero_FEL # the FEL representation of zero cpdef fel_to_field(self, FEL x) cpdef FEL field_to_fel(self, x) except 255 diff --git a/src/sage/matrix/matrix_gfpn_dense.pyx b/src/sage/matrix/matrix_gfpn_dense.pyx index ed9c0aaf09f..b62dc4c685c 100644 --- a/src/sage/matrix/matrix_gfpn_dense.pyx +++ b/src/sage/matrix/matrix_gfpn_dense.pyx @@ -1,3 +1,5 @@ +# distutils: libraries = mtx +# sage_setup: distribution = sage-meataxe r""" Dense Matrices over `\mathbb F_q`, with `q<255`. @@ -118,6 +120,7 @@ cdef class FieldConverter_class: """ self.field = field._cache.fetch_int + self.zero_FEL = self.field_to_fel(field.zero()) cpdef fel_to_field(self, FEL x): """ @@ -575,6 +578,21 @@ cdef class Matrix_gfpn_dense(Matrix_dense): # This method is here for speed! return FfToInt(FfExtract(MatGetPtr(self.Data,i), j)) + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j): + r""" + Return 1 if the entry ``(i, j)`` is zero, otherwise 0. + + EXAMPLES:: + + sage: F. = GF(9) + sage: M = MatrixSpace(F,2,5)(sorted(list(F))+[0]) + sage: M.zero_pattern_matrix() # indirect doctest + [1 0 0 0 0] + [0 0 0 0 1] + """ + FfSetField(self.Data.Field) + return FfExtract(MatGetPtr(self.Data,i), j) == self._converter.zero_FEL + cpdef Matrix_gfpn_dense get_slice(self, Py_ssize_t i, Py_ssize_t j): """ Return a horizontal slice of this matrix. @@ -1390,7 +1408,7 @@ cdef class Matrix_gfpn_dense(Matrix_dense): sig_off() return new_mtx(mat, self) - def __div__(Matrix_gfpn_dense self, p): + def __truediv__(Matrix_gfpn_dense self, p): """ Divide a matrix by a scalar. diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 750e245d032..7753468c3f4 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -1,4 +1,8 @@ # -*- coding: utf-8 -*- +# distutils: extra_compile_args = M4RI_CFLAGS +# distutils: libraries = iml ntl gmp m CBLAS_LIBRARIES +# distutils: library_dirs = CBLAS_LIBDIR +# distutils: include_dirs = M4RI_INCDIR CBLAS_INCDIR """ Dense matrices over the integer ring @@ -70,7 +74,8 @@ from sage.libs.gmp.mpz cimport * from sage.modules.vector_integer_dense cimport Vector_integer_dense -from sage.misc.misc import verbose, get_verbose, cputime +from sage.misc.misc import cputime +from sage.misc.verbose import verbose, get_verbose from sage.arith.all import previous_prime from sage.arith.long cimport integer_check_long_py @@ -116,6 +121,7 @@ from .matrix_modn_dense_double cimport Matrix_modn_dense_double from .matrix_mod2_dense import Matrix_mod2_dense from .matrix_mod2_dense cimport Matrix_mod2_dense +from sage.rings.finite_rings.finite_field_constructor import GF from .matrix2 import decomp_seq @@ -373,7 +379,7 @@ cdef class Matrix_integer_dense(Matrix_dense): """ Returns (i, j) entry of self as a new Integer. - .. warning:: + .. WARNING:: This is very unsafe; it assumes i and j are in the right range. @@ -401,7 +407,7 @@ cdef class Matrix_integer_dense(Matrix_dense): """ Copy entry i,j of the matrix ``self`` to ``value``. - .. warning:: + .. WARNING:: This is very unsafe; it assumes i and j are in the right range. @@ -427,7 +433,7 @@ cdef class Matrix_integer_dense(Matrix_dense): """ Returns (j, i) entry of self as a new Integer. - .. warning:: + .. WARNING:: This is very unsafe; it assumes i and j are in the right range. @@ -449,6 +455,17 @@ cdef class Matrix_integer_dense(Matrix_dense): """ return fmpz_get_d(fmpz_mat_entry(self._matrix, i, j)) + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j): + """ + Return 1 if the entry (i, j) is zero, otherwise 0. + + .. WARNING:: + + This is very unsafe; it assumes i and j are in the right + range. + """ + return fmpz_is_zero(fmpz_mat_entry(self._matrix, i,j)) + def _pickle(self): """ EXAMPLES:: @@ -676,7 +693,7 @@ cdef class Matrix_integer_dense(Matrix_dense): """ Multiply matrices over ZZ using linbox. - .. warning:: + .. WARNING:: This is very slow right now, i.e., linbox is very slow. @@ -1542,7 +1559,17 @@ cdef class Matrix_integer_dense(Matrix_dense): return self._mod_int_c(modulus) cdef _mod_two(self): - MS = matrix_space.MatrixSpace(IntegerModRing(2), self._nrows, self._ncols) + """ + TESTS: + + Check that bug discovered in :trac:`29839` is fixed:: + + sage: M = Matrix(ZZ, [[0,1],[0,1]]) + sage: M._mod_int(2).transpose() + [0 0] + [1 1] + """ + MS = matrix_space.MatrixSpace(GF(2), self._nrows, self._ncols) return Matrix_mod2_dense(MS, self, True, True) cdef _mod_int_c(self, mod_int p): @@ -2235,7 +2262,7 @@ cdef class Matrix_integer_dense(Matrix_dense): """ Return the elementary divisors of self, in order. - .. warning:: + .. WARNING:: This is MUCH faster than the :meth:`smith_form` function. @@ -4842,7 +4869,7 @@ cdef class Matrix_integer_dense(Matrix_dense): a new matrix that is the echelon form of self with row appended to the bottom. - .. warning:: + .. WARNING:: It is assumed that self is in echelon form. diff --git a/src/sage/matrix/matrix_integer_dense_hnf.py b/src/sage/matrix/matrix_integer_dense_hnf.py index d7299b09bf9..7a0f343a0c1 100644 --- a/src/sage/matrix/matrix_integer_dense_hnf.py +++ b/src/sage/matrix/matrix_integer_dense_hnf.py @@ -6,14 +6,16 @@ - Clement Pernet and William Stein (2008-02-07): initial version """ from __future__ import print_function -from six.moves import range from copy import copy -from sage.misc.misc import verbose, cputime +from sage.misc.misc import cputime +from sage.misc.verbose import verbose from sage.matrix.constructor import (random_matrix, matrix, identity_matrix) -from sage.rings.all import ZZ, Integer, RR +from sage.rings.integer_ring import ZZ +from sage.rings.real_mpfr import RR +from sage.rings.integer import Integer from sage.arith.all import previous_prime, CRT_list diff --git a/src/sage/matrix/matrix_integer_dense_saturation.py b/src/sage/matrix/matrix_integer_dense_saturation.py index eedb007d3b3..a4e46b71506 100644 --- a/src/sage/matrix/matrix_integer_dense_saturation.py +++ b/src/sage/matrix/matrix_integer_dense_saturation.py @@ -2,12 +2,12 @@ Saturation over ZZ """ from __future__ import absolute_import -from six.moves import range -from sage.rings.all import ZZ, GF +from sage.rings.integer_ring import ZZ +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.arith.all import binomial, gcd from sage.matrix.constructor import identity_matrix, random_matrix -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.misc.randstate import current_randstate from . import matrix_integer_dense_hnf from copy import copy diff --git a/src/sage/matrix/matrix_integer_sparse.pyx b/src/sage/matrix/matrix_integer_sparse.pyx index c6cb12ad86a..f21a9975af3 100644 --- a/src/sage/matrix/matrix_integer_sparse.pyx +++ b/src/sage/matrix/matrix_integer_sparse.pyx @@ -119,6 +119,20 @@ cdef class Matrix_integer_sparse(Matrix_sparse): mpz_vector_get_entry(x.value, &self._matrix[i], j) return x + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j): + """ + Return 1 if the entry ``(i, j)`` is zero, otherwise 0. + + EXAMPLES:: + + sage: M = matrix(ZZ, [[0,1,0],[0,0,0]], sparse=True) + sage: M.zero_pattern_matrix() # indirect doctest + [1 0 1] + [1 1 1] + """ + return mpz_vector_is_entry_zero_unsafe(&self._matrix[i], j) + + ######################################################################## # LEVEL 2 functionality # * def _pickle @@ -219,6 +233,63 @@ cdef class Matrix_integer_sparse(Matrix_sparse): self.cache('dict', d) return d + cdef sage.structure.element.Matrix _matrix_times_matrix_(self, sage.structure.element.Matrix _right): + """ + Return the product of the sparse integer matrices + ``self`` and ``_right``. + + EXAMPLES:: + + sage: a = matrix(ZZ, 2, [1,2,3,4], sparse=True) + sage: b = matrix(ZZ, 2, 3, [1..6], sparse=True) + sage: a * b + [ 9 12 15] + [19 26 33] + """ + cdef Matrix_integer_sparse right, ans + right = _right + + cdef mpz_vector* v + + # Build a table that gives the nonzero positions in each column of right + cdef list nonzero_positions_in_columns = [set() for _ in range(right._ncols)] + cdef Py_ssize_t i, j, k + for i in range(right._nrows): + v = &(right._matrix[i]) + for j in range(v.num_nonzero): + ( nonzero_positions_in_columns[v.positions[j]]).add(i) + # pre-computes the list of nonzero columns of right + cdef list right_indices + right_indices = [j for j in range(right._ncols) + if nonzero_positions_in_columns[j]] + + ans = self.new_matrix(self._nrows, right._ncols) + + # Now do the multiplication, getting each row completely before filling it in. + cdef set c + cdef mpz_t x, y, s + mpz_init(x) + mpz_init(y) + mpz_init(s) + for i in range(self._nrows): + v = &(self._matrix[i]) + if not v.num_nonzero: + continue + for j in right_indices: + mpz_set_si(s, 0) + c = nonzero_positions_in_columns[j] + for k in range(v.num_nonzero): + if v.positions[k] in c: + mpz_vector_get_entry(y, &right._matrix[v.positions[k]], j) + mpz_mul(x, v.entries[k], y) + mpz_add(s, s, x) + mpz_vector_set_entry(&ans._matrix[i], j, s) + + mpz_clear(x) + mpz_clear(y) + mpz_clear(s) + return ans + ######################################################################## # LEVEL 3 functionality (Optional) # * cdef _sub_ diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index 575500ab1af..72986685c1f 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -15,10 +15,8 @@ # # The full text of the GPL is available at: # -# https://www.gnu.org/licenses/ -# **************************************************************************** -from six.moves import range -from six import iteritems +# http://www.gnu.org/licenses/ +#***************************************************************************** from sage.categories.fields import Fields from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -59,10 +57,10 @@ def prm_mul(p1, p2, mask_free, prec): p = {} if not p2: return p - for exp1, v1 in iteritems(p1): + for exp1, v1 in p1.items(): if v1.is_zero(): continue - for exp2, v2 in iteritems(p2): + for exp2, v2 in p2.items(): if exp1 & exp2: continue v = v1 * v2 diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index e43e5064d10..be2ba45a5d2 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -1,4 +1,8 @@ # -*- coding: utf-8 -*- +# distutils: libraries = M4RI_LIBRARIES GDLIB_LIBRARIES LIBPNG_LIBRARIES ZLIB_LIBRARIES +# distutils: library_dirs = M4RI_LIBDIR GDLIB_LIBDIR LIBPNG_LIBDIR ZLIB_LIBDIR +# distutils: include_dirs = M4RI_INCDIR GDLIB_INCDIR LIBPNG_INCDIR ZLIB_INCDIR +# distutils: extra_compile_args = M4RI_CFLAGS """ Dense matrices over GF(2) using the M4RI library @@ -112,7 +116,8 @@ from sage.structure.element cimport (Matrix, Vector, from sage.modules.free_module_element cimport FreeModuleElement from sage.libs.gmp.random cimport * from sage.misc.randstate cimport randstate, current_randstate -from sage.misc.misc import verbose, get_verbose, cputime +from sage.misc.misc import cputime +from sage.misc.verbose import verbose, get_verbose from sage.modules.free_module import VectorSpace from sage.modules.vector_mod2_dense cimport Vector_mod2_dense from sage.structure.richcmp cimport rich_to_bool @@ -335,7 +340,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse def str(self, rep_mapping=None, zero=None, plus_one=None, minus_one=None, - *, unicode=False, shape=None): + *, unicode=False, shape=None, character_art=False): r""" Return a nice string representation of the matrix. @@ -368,6 +373,11 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse in accordance with the TeX rendering, while the ASCII rendering defaults to square brackets. + - ``character_art`` -- boolean (default: ``False``); if ``True``, the + result will be of type :class:`~sage.typeset.ascii_art.AsciiArt` or + :class:`~sage.typeset.unicode_art.UnicodeArt` which support line + breaking of wide matrices that exceed the window width + EXAMPLES:: sage: B = random_matrix(GF(2),3,3) @@ -400,12 +410,13 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse # Set the mapping based on keyword arguments # We ignore minus_one (it's only there for compatibility with Matrix) if (rep_mapping is not None or zero is not None or plus_one is not None - or unicode or shape is not None): + or unicode or shape is not None or character_art): # Shunt mappings off to the generic code since they might not be # single characters return matrix_dense.Matrix_dense.str(self, rep_mapping=rep_mapping, zero=zero, plus_one=plus_one, - unicode=unicode, shape=shape) + unicode=unicode, shape=shape, + character_art=character_art) if self._nrows == 0 or self._ncols == 0: return "[]" @@ -547,7 +558,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0, alloc=False) if self._nrows == 0 or self._ncols == 0: return A - A._entries = mzd_add(NULL, self._entries,(right)._entries) + A._entries = mzd_add(A._entries, self._entries,(right)._entries) return A @@ -589,20 +600,18 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) """ cdef mzd_t *tmp + VS = VectorSpace(self._base_ring, self._nrows) if not isinstance(v, Vector_mod2_dense): - M = VectorSpace(self._base_ring, self._nrows) - v = M(v) + v = VS(v) if self.ncols() != v.degree(): raise ArithmeticError("number of columns of matrix must equal degree of vector") - VS = VectorSpace(self._base_ring, self._nrows) # If the vector is 0-dimensional, the result will be the 0-vector if not self.ncols(): return VS.zero() cdef Vector_mod2_dense c = Vector_mod2_dense.__new__(Vector_mod2_dense) sig_str("matrix allocation failed") c._init(self._nrows, VS) - c._entries = mzd_init(1, self._nrows) if c._entries.nrows and c._entries.ncols: tmp = mzd_init(self._nrows, 1) _mzd_mul_naive(tmp, self._entries, (v)._entries, 0) @@ -886,7 +895,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0, alloc = False) sig_on() - A._entries = mzd_inv_m4ri(NULL, self._entries, 0) + A._entries = mzd_inv_m4ri(A._entries, self._entries, 0) sig_off() if A._entries==NULL: diff --git a/src/sage/matrix/matrix_modn_dense_double.pyx b/src/sage/matrix/matrix_modn_dense_double.pyx index cc743e35a42..b10d2e9a71d 100644 --- a/src/sage/matrix/matrix_modn_dense_double.pyx +++ b/src/sage/matrix/matrix_modn_dense_double.pyx @@ -1,3 +1,8 @@ +# distutils: language = c++ +# distutils: libraries = CBLAS_LIBRARIES +# distutils: library_dirs = CBLAS_LIBDIR +# distutils: include_dirs = CBLAS_INCDIR +# distutils: extra_compile_args = -D_XPG6 """ Dense matrices over `\ZZ/n\ZZ` for `n < 2^{23}` using LinBox's ``Modular`` diff --git a/src/sage/matrix/matrix_modn_dense_float.pyx b/src/sage/matrix/matrix_modn_dense_float.pyx index d3114b3e17d..b23389a83b9 100644 --- a/src/sage/matrix/matrix_modn_dense_float.pyx +++ b/src/sage/matrix/matrix_modn_dense_float.pyx @@ -1,3 +1,7 @@ +# distutils: language = c++ +# distutils: libraries = CBLAS_LIBRARIES +# distutils: library_dirs = CBLAS_LIBDIR +# distutils: include_dirs = CBLAS_INCDIR """ Dense matrices over `\ZZ/n\ZZ` for `n < 2^{11}` using LinBox's ``Modular`` diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index 0877ee78b43..e03663448be 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -115,7 +115,8 @@ from sage.structure.element cimport (Element, Vector, Matrix, from sage.matrix.matrix_dense cimport Matrix_dense from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense from sage.rings.finite_rings.integer_mod cimport IntegerMod_int, IntegerMod_abstract -from sage.misc.misc import verbose, get_verbose, cputime +from sage.misc.misc import cputime +from sage.misc.verbose import verbose, get_verbose from sage.rings.integer cimport Integer from sage.rings.integer_ring import ZZ from sage.structure.proof.proof import get_flag as get_proof_flag @@ -3368,3 +3369,22 @@ cdef class Matrix_modn_dense_template(Matrix_dense): cdef celement *_from = self._entries+(i*self._ncols) for j in range(self._ncols): to[j] = _from[j] + + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j): + r""" + Return 1 if the entry ``(i, j)`` is zero, otherwise 0. + + EXAMPLES:: + + sage: M = Matrix(GF(49), 2, [1,2,-2,0]) + sage: M.zero_pattern_matrix() # indirect doctest + [0 0] + [0 1] + + sage: M = Matrix(Integers(10), 2, [1,2,-2,0]) + sage: M.zero_pattern_matrix() # indirect doctest + [0 0] + [0 1] + """ + return self._entries[j+i*self._ncols] == 0 + diff --git a/src/sage/matrix/matrix_modn_sparse.pyx b/src/sage/matrix/matrix_modn_sparse.pyx index 5f40214bac4..e1d2da42ee6 100644 --- a/src/sage/matrix/matrix_modn_sparse.pyx +++ b/src/sage/matrix/matrix_modn_sparse.pyx @@ -107,11 +107,10 @@ cimport sage.matrix.matrix_sparse as matrix_sparse cimport sage.matrix.matrix_dense as matrix_dense from sage.rings.finite_rings.integer_mod cimport IntegerMod_int, IntegerMod_abstract from sage.rings.integer cimport Integer +from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ -from sage.misc.misc import verbose, get_verbose - -import sage.rings.all as rings +from sage.misc.verbose import verbose, get_verbose from sage.matrix.matrix2 import Matrix as Matrix2 from .args cimport SparseEntry, MatrixArgs_init @@ -194,6 +193,19 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): n.ivalue = get_entry(&self.rows[i], j) return n + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j): + """ + Return 1 if the entry ``(i, j)`` is zero, otherwise 0. + + EXAMPLES:: + + sage: M = matrix(GF(13), [[0,1,0],[0,0,0]], sparse=True) + sage: M.zero_pattern_matrix() # indirect doctest + [1 0 1] + [1 1 1] + """ + return is_entry_zero_unsafe(&self.rows[i], j) + def _dict(self): """ Unsafe version of the dict method, mainly for internal use. This @@ -272,7 +284,8 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): Even though sparse and dense matrices are represented differently, they still compare as equal if they have the - same entries: + same entries:: + sage: a*b == a._matrix_times_matrix_dense(b) True sage: d = matrix(GF(43), 3, 8, range(24)) @@ -288,7 +301,6 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): [32770 32770 32770] sage: M*M.transpose() # previously returned [32738] [3] - """ cdef Matrix_modn_sparse right, ans right = _right @@ -296,24 +308,29 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): cdef c_vector_modint* v # Build a table that gives the nonzero positions in each column of right - nonzero_positions_in_columns = [set([]) for _ in range(right._ncols)] + cdef list nonzero_positions_in_columns = [set() for _ in range(right._ncols)] cdef Py_ssize_t i, j, k - for i from 0 <= i < right._nrows: + for i in range(right._nrows): v = &(right.rows[i]) - for j from 0 <= j < right.rows[i].num_nonzero: - nonzero_positions_in_columns[v.positions[j]].add(i) + for j in range(v.num_nonzero): + ( nonzero_positions_in_columns[v.positions[j]]).add(i) + # pre-computes the list of nonzero columns of right + cdef list right_indices + right_indices = [j for j in range(right._ncols) + if nonzero_positions_in_columns[j]] ans = self.new_matrix(self._nrows, right._ncols) # Now do the multiplication, getting each row completely before filling it in. cdef int x, y, s + cdef set c - for i from 0 <= i < self._nrows: - v = &self.rows[i] - for j from 0 <= j < right._ncols: + for i in range(self._nrows): + v = &(self.rows[i]) + for j in range(right._ncols): s = 0 - c = nonzero_positions_in_columns[j] - for k from 0 <= k < v.num_nonzero: + c = nonzero_positions_in_columns[j] + for k in range(v.num_nonzero): if v.positions[k] in c: y = get_entry(&right.rows[v.positions[k]], j) x = v.entries[k] * y @@ -403,6 +420,7 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): TODO: Implement switching to a dense method when the matrix gets dense. """ + from sage.misc.verbose import verbose, get_verbose x = self.fetch('in_echelon_form') if not x is None and x: return # already known to be in echelon form self.check_mutability() @@ -465,17 +483,17 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): def _nonzero_positions_by_row(self, copy=True): """ - Returns the list of pairs (i,j) such that self[i,j] != 0. + Return the list of pairs (i,j) such that self[i,j] != 0. It is safe to change the resulting list (unless you give the option copy=False). EXAMPLES:: + sage: M = Matrix(GF(7), [[0,0,0,1,0,0,0,0],[0,1,0,0,0,0,1,0]], sparse=True); M [0 0 0 1 0 0 0 0] [0 1 0 0 0 0 1 0] sage: M.nonzero_positions() [(0, 3), (1, 1), (1, 6)] - """ x = self.fetch('nonzero_positions') if not x is None: @@ -519,7 +537,7 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): for i from 0 <= i < self._nrows: nonzero_entries += self.rows[i].num_nonzero - return rings.ZZ(nonzero_entries)/rings.ZZ(self._nrows*self._ncols) + return ZZ(nonzero_entries) / ZZ(self._nrows*self._ncols) def transpose(self): """ @@ -926,7 +944,6 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): return Matrix_sparse.solve_right(self, B) else: if isinstance(B, sage.structure.element.Matrix): - from sage.rings.rational_field import QQ from sage.matrix.special import diagonal_matrix m, d = self._solve_matrix_linbox(B, algorithm) return m * diagonal_matrix([QQ((1,x)) for x in d]) diff --git a/src/sage/matrix/matrix_mpolynomial_dense.pyx b/src/sage/matrix/matrix_mpolynomial_dense.pyx index 78f93cf0943..607a0a49333 100644 --- a/src/sage/matrix/matrix_mpolynomial_dense.pyx +++ b/src/sage/matrix/matrix_mpolynomial_dense.pyx @@ -280,7 +280,7 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): for c in xrange(self.ncols()): self.set_unsafe(r, c, R._zero_element) - from sage.rings.all import ZZ + from sage.rings.integer_ring import ZZ l = [ZZ(e-1) for e in l] self.cache('in_echelon_form_bareiss',True) diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index 77648949336..d51dce68aed 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -1,3 +1,8 @@ +# distutils: extra_compile_args = -D_XPG6 M4RI_CFLAGS +# distutils: libraries = iml ntl m CBLAS_LIBRARIES +# distutils: library_dirs = CBLAS_LIBDIR +# distutils: include_dirs = M4RI_INCDIR CBLAS_INCDIR + """ Dense matrices over the rational field @@ -110,7 +115,8 @@ from sage.arith.all import gcd from .matrix2 import decomp_seq from .matrix0 import Matrix as Matrix_base -from sage.misc.all import verbose, get_verbose, prod +from sage.misc.all import prod +from sage.misc.verbose import verbose, get_verbose ######################################################### # PARI C library @@ -134,7 +140,7 @@ cdef class Matrix_rational_dense(Matrix_dense): sage: type(a) - .. warning:: + .. WARNING:: This is for internal use only, or if you really know what you're doing. @@ -260,6 +266,17 @@ cdef class Matrix_rational_dense(Matrix_dense): fmpq_get_mpq(x.value, fmpq_mat_entry(self._matrix, i, j)) return x + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j): + """ + Return 1 if the entry (i, j) is zero, otherwise 0. + + .. WARNING:: + + This is very unsafe; it assumes i and j are in the right + range. + """ + return fmpq_is_zero(fmpq_mat_entry(self._matrix, i,j)) + cdef _add_ui_unsafe_assuming_int(self, Py_ssize_t i, Py_ssize_t j, unsigned long int n): # doesn't check immutability # doesn't do bounds checks. @@ -1426,7 +1443,7 @@ cdef class Matrix_rational_dense(Matrix_dense): sage: a.change_ring(ZZ) Traceback (most recent call last): ... - TypeError: matrix has denominators so can't change to ZZ. + TypeError: matrix has denominators so can...t change to ZZ. sage: b = a.change_ring(QQ['x']); b [1/2 -1] [ 2 3] @@ -2652,6 +2669,7 @@ cdef class Matrix_rational_dense(Matrix_dense): Return the determinant of this matrix computed using pari. EXAMPLES:: + sage: matrix(QQ,3,[1..9])._det_pari() 0 sage: matrix(QQ,3,[1..9])._det_pari(1) diff --git a/src/sage/matrix/matrix_rational_sparse.pyx b/src/sage/matrix/matrix_rational_sparse.pyx index 15476b9f48d..b00416b8e92 100644 --- a/src/sage/matrix/matrix_rational_sparse.pyx +++ b/src/sage/matrix/matrix_rational_sparse.pyx @@ -54,7 +54,7 @@ import sage.matrix.matrix_space from .matrix_integer_sparse cimport Matrix_integer_sparse from .matrix_rational_dense cimport Matrix_rational_dense -from sage.misc.misc import verbose +from sage.misc.verbose import verbose cdef class Matrix_rational_sparse(Matrix_sparse): def __cinit__(self): @@ -103,6 +103,19 @@ cdef class Matrix_rational_sparse(Matrix_sparse): mpq_vector_get_entry(x.value, &self._matrix[i], j) return x + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j): + """ + Return 1 if the entry ``(i, j)`` is zero, otherwise 0. + + EXAMPLES:: + + sage: M = matrix(QQ, [[0,1,0],[0,0,0]], sparse=True) + sage: M.zero_pattern_matrix() # indirect doctest + [1 0 1] + [1 1 1] + """ + return mpq_vector_is_entry_zero_unsafe(&self._matrix[i], j) + def add_to_entry(self, Py_ssize_t i, Py_ssize_t j, elt): r""" Add ``elt`` to the entry at position ``(i, j)``. @@ -163,26 +176,33 @@ cdef class Matrix_rational_sparse(Matrix_sparse): cdef mpq_vector* v # Build a table that gives the nonzero positions in each column of right - nonzero_positions_in_columns = [set([]) for _ in range(right._ncols)] + cdef list nonzero_positions_in_columns = [set() for _ in range(right._ncols)] cdef Py_ssize_t i, j, k - for i from 0 <= i < right._nrows: + for i in range(right._nrows): v = &(right._matrix[i]) - for j from 0 <= j < right._matrix[i].num_nonzero: - nonzero_positions_in_columns[v.positions[j]].add(i) + for j in range(v.num_nonzero): + ( nonzero_positions_in_columns[v.positions[j]]).add(i) + # pre-computes the list of nonzero columns of right + cdef list right_indices + right_indices = [j for j in range(right._ncols) + if nonzero_positions_in_columns[j]] ans = self.new_matrix(self._nrows, right._ncols) # Now do the multiplication, getting each row completely before filling it in. + cdef set c cdef mpq_t x, y, s mpq_init(x) mpq_init(y) mpq_init(s) - for i from 0 <= i < self._nrows: - v = &self._matrix[i] - for j from 0 <= j < right._ncols: + for i in range(self._nrows): + v = &(self._matrix[i]) + if not v.num_nonzero: + continue + for j in right_indices: mpq_set_si(s, 0, 1) - c = nonzero_positions_in_columns[j] - for k from 0 <= k < v.num_nonzero: + c = nonzero_positions_in_columns[j] + for k in range(v.num_nonzero): if v.positions[k] in c: mpq_vector_get_entry(y, &right._matrix[v.positions[k]], j) mpq_mul(x, v.entries[k], y) @@ -265,7 +285,8 @@ cdef class Matrix_rational_sparse(Matrix_sparse): # TODO ## cpdef _lmul_(self, Element right): ## """ -## EXAMPLES: +## EXAMPLES:: +## ## sage: a = matrix(QQ,2,range(6)) ## sage: (3/4) * a ## [ 0 3/4 3/2] diff --git a/src/sage/matrix/matrix_real_double_dense.pyx b/src/sage/matrix/matrix_real_double_dense.pyx index 4cfeac84cf0..39bb0fcdbab 100644 --- a/src/sage/matrix/matrix_real_double_dense.pyx +++ b/src/sage/matrix/matrix_real_double_dense.pyx @@ -63,28 +63,28 @@ cdef class Matrix_real_double_dense(Matrix_double_dense): [-1.9999999999999996 0.9999999999999998] [ 1.4999999999999998 -0.4999999999999999] - To compute eigenvalues the use the functions left_eigenvectors or - right_eigenvectors + To compute eigenvalues, use the method + :meth:`~.Matrix_double_dense.left_eigenvectors` or + :meth:`~.Matrix_double_dense.right_eigenvectors`. :: sage: p,e = m.right_eigenvectors() - the result of eigen is a pair (p,e), where p is a list of - eigenvalues and the e is a matrix whose columns are the + The result is a pair ``(p,e)``, where ``p`` is a diagonal matrix of + eigenvalues and ``e`` is a matrix whose columns are the eigenvectors. - To solve a linear system Ax = b where A = [[1,2],[3,4]] and - b = [5,6]. - - :: + To solve a linear system `Ax = b` where ``A = [[1,2],[3,4]]`` and + `b = [5,6]`:: sage: b = vector(RDF,[5,6]) sage: m.solve_right(b) # rel tol 1e-15 (-3.9999999999999987, 4.499999999999999) - See the commands qr, lu, and svd for QR, LU, and singular value - decomposition. + See the methods :meth:`~.Matrix_double_dense.QR`, + :meth:`~.Matrix_double_dense.LU`, and :meth:`.SVD` for QR, LU, and singular + value decomposition. """ def __cinit__(self): global numpy diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 9202c6a6c9b..a8fc5e64493 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -32,8 +32,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range -from six import iteritems, integer_types # System imports import sys @@ -135,6 +133,11 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): sage: get_matrix_class(ZZ, 3, 3, False, 'generic') + sage: get_matrix_class(GF(2^15), 3, 3, False, None) + + sage: get_matrix_class(GF(2^17), 3, 3, False, None) + + sage: get_matrix_class(GF(2), 2, 2, False, 'm4ri') sage: get_matrix_class(GF(4), 2, 2, False, 'm4ri') @@ -149,6 +152,19 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): sage: get_matrix_class(CDF, 2, 3, False, 'numpy') + sage: get_matrix_class(GF(25,'x'), 4, 4, False, 'meataxe') # optional: meataxe + + sage: get_matrix_class(IntegerModRing(3), 4, 4, False, 'meataxe') # optional: meataxe + + sage: get_matrix_class(IntegerModRing(4), 4, 4, False, 'meataxe') + Traceback (most recent call last): + ... + ValueError: 'meataxe' matrix can only deal with finite fields of order < 256 + sage: get_matrix_class(GF(next_prime(255)), 4, 4, False, 'meataxe') + Traceback (most recent call last): + ... + ValueError: 'meataxe' matrix can only deal with finite fields of order < 256 + sage: get_matrix_class(ZZ, 3, 5, False, 'crazy_matrix') Traceback (most recent call last): ... @@ -156,15 +172,15 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): sage: get_matrix_class(GF(3), 2, 2, False, 'm4ri') Traceback (most recent call last): ... - ValueError: m4ri matrices are only available in characteristic 2 + ValueError: 'm4ri' matrices are only available for fields of characteristic 2 and order <= 65536 sage: get_matrix_class(Zmod(2**30), 2, 2, False, 'linbox-float') Traceback (most recent call last): ... - ValueError: linbox-float can only deal with order < 256 + ValueError: 'linbox-float' matrices can only deal with order < 256 sage: get_matrix_class(Zmod(2**30), 2, 2, False, 'linbox-double') Traceback (most recent call last): ... - ValueError: linbox-double can only deal with order < 8388608 + ValueError: 'linbox-double' matrices can only deal with order < 8388608 sage: type(matrix(SR, 2, 2, 0)) @@ -180,146 +196,150 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): sage: type(matrix(GF(125,'z'), 2, range(4))) # optional: meataxe + """ if isinstance(implementation, type): return implementation if not sparse: - if implementation == 'generic': - return matrix_generic_dense.Matrix_generic_dense - - elif implementation == 'gap': - from .matrix_gap import Matrix_gap - return Matrix_gap - - if R is sage.rings.integer_ring.ZZ: - if implementation is None or implementation == 'flint': + if implementation is None: + # Choose default implementation: + if R is sage.rings.integer_ring.ZZ: return matrix_integer_dense.Matrix_integer_dense - elif R is sage.rings.rational_field.QQ: - if implementation is None or implementation == 'flint': + if R is sage.rings.rational_field.QQ: return matrix_rational_dense.Matrix_rational_dense - elif sage.rings.number_field.number_field.is_CyclotomicField(R): - if implementation is None or implementation == 'rational': - from . import matrix_cyclo_dense - return matrix_cyclo_dense.Matrix_cyclo_dense - - elif R is sage.rings.real_double.RDF: - if implementation is None or implementation == 'numpy': + if R is sage.rings.real_double.RDF: from . import matrix_real_double_dense return matrix_real_double_dense.Matrix_real_double_dense - elif R is sage.rings.complex_double.CDF: - if implementation is None or implementation == 'numpy': - from . import matrix_complex_double_dense - return matrix_complex_double_dense.Matrix_complex_double_dense - - elif sage.rings.finite_rings.integer_mod_ring.is_IntegerModRing(R): - from . import matrix_modn_dense_double, matrix_modn_dense_float + if R is sage.rings.complex_double.CDF: + if implementation is None or implementation == 'numpy': + from . import matrix_complex_double_dense + return matrix_complex_double_dense.Matrix_complex_double_dense - if implementation is None: + if sage.rings.finite_rings.finite_field_constructor.is_FiniteField(R): if R.order() == 2: - implementation = 'm4ri' - elif R.order() < matrix_modn_dense_float.MAX_MODULUS: - implementation = 'linbox-float' - elif R.order() < matrix_modn_dense_double.MAX_MODULUS: - implementation = 'linbox-double' - else: - implementation = 'generic' - - if implementation == 'm4ri': - if R.order() != 2: - raise ValueError('m4ri matrices are only available in characteristic 2') - else: return matrix_mod2_dense.Matrix_mod2_dense - elif implementation == 'linbox-float': - if R.order() >= matrix_modn_dense_float.MAX_MODULUS: - raise ValueError('linbox-float can only deal with order < %s' % matrix_modn_dense_float.MAX_MODULUS) - else: - return matrix_modn_dense_float.Matrix_modn_dense_float - elif implementation == 'linbox-double': - if R.order() >= matrix_modn_dense_double.MAX_MODULUS: - raise ValueError('linbox-double can only deal with order < %s' % matrix_modn_dense_double.MAX_MODULUS) - else: - return matrix_modn_dense_double.Matrix_modn_dense_double - - elif sage.rings.finite_rings.finite_field_constructor.is_FiniteField(R): - if implementation is None: - if R.characteristic() == 2 and R.order() <= 65536: - implementation = 'm4ri' - elif R.order() <= 255: - try: - from . import matrix_gfpn_dense - except ImportError: - implementation = 'generic' - else: - implementation = 'meataxe' - else: - implementation = 'generic' - - if implementation == 'm4ri': - if R.characteristic() != 2 or R.order() > 65536: - raise ValueError('m4ri matrices are only available in characteristic 2 and order <= 65536') - else: + if R.characteristic() == 2 and R.order() <= 65536: # 65536 == 2^16 return matrix_gf2e_dense.Matrix_gf2e_dense - elif implementation == 'meataxe': - if R.order() > 255: - raise ValueError('meataxe library only deals with finite fields of order < 256') - else: + if (not R.is_prime_field()) and R.order() < 256: try: from . import matrix_gfpn_dense - except ImportError: - from sage.misc.package import PackageNotFoundError - raise PackageNotFoundError('meataxe') - else: return matrix_gfpn_dense.Matrix_gfpn_dense + except ImportError: + pass - elif sage.rings.polynomial.polynomial_ring.is_PolynomialRing(R) and R.base_ring() in _Fields: - if implementation is None: - return matrix_polynomial_dense.Matrix_polynomial_dense - - elif sage.rings.polynomial.multi_polynomial_ring_base.is_MPolynomialRing(R) and R.base_ring() in _Fields: - if implementation is None: - return matrix_mpolynomial_dense.Matrix_mpolynomial_dense + if sage.rings.finite_rings.integer_mod_ring.is_IntegerModRing(R): + from . import matrix_modn_dense_double, matrix_modn_dense_float + if R.order() < matrix_modn_dense_float.MAX_MODULUS: + return matrix_modn_dense_float.Matrix_modn_dense_float + if R.order() < matrix_modn_dense_double.MAX_MODULUS: + return matrix_modn_dense_double.Matrix_modn_dense_double - else: - # deal with late imports here + if sage.rings.number_field.number_field.is_CyclotomicField(R): + from . import matrix_cyclo_dense + return matrix_cyclo_dense.Matrix_cyclo_dense - # importing SR causes circular imports from sage.symbolic.ring import SR if R is SR: - if implementation is None: - from . import matrix_symbolic_dense - return matrix_symbolic_dense.Matrix_symbolic_dense + from . import matrix_symbolic_dense + return matrix_symbolic_dense.Matrix_symbolic_dense - # avoid importing ComplexBallField from sage.rings.complex_arb import ComplexBallField if isinstance(R, ComplexBallField): - if implementation is None: - from . import matrix_complex_ball_dense - return matrix_complex_ball_dense.Matrix_complex_ball_dense + from . import matrix_complex_ball_dense + return matrix_complex_ball_dense.Matrix_complex_ball_dense - # generic fallback - if implementation != 'generic' and implementation is not None: - raise ValueError("unknown matrix implementation %r over %r" % (implementation, R)) - else: + if sage.rings.polynomial.polynomial_ring.is_PolynomialRing(R) and R.base_ring() in _Fields: + return matrix_polynomial_dense.Matrix_polynomial_dense + + if sage.rings.polynomial.multi_polynomial_ring_base.is_MPolynomialRing(R) and R.base_ring() in _Fields: + return matrix_mpolynomial_dense.Matrix_mpolynomial_dense + + # The fallback + return matrix_generic_dense.Matrix_generic_dense + + # Deal with request for a specific implementation + if implementation == 'flint': + if R is sage.rings.integer_ring.ZZ: + return matrix_integer_dense.Matrix_integer_dense + if R is sage.rings.rational_field.QQ: + return matrix_rational_dense.Matrix_rational_dense + raise ValueError("'flint' matrices are only available over the integers or the rationals") + + if implementation == 'm4ri': + if R.is_field() and R.characteristic() == 2 and R.order() <= 65536: + if R.order() == 2: + return matrix_mod2_dense.Matrix_mod2_dense + return matrix_gf2e_dense.Matrix_gf2e_dense + raise ValueError("'m4ri' matrices are only available for fields of characteristic 2 and order <= 65536") + + if implementation == 'meataxe': + if R.is_field() and R.order() < 256: + try: + from . import matrix_gfpn_dense + except ImportError: + from sage.misc.package import PackageNotFoundError + raise PackageNotFoundError('meataxe') + else: + return matrix_gfpn_dense.Matrix_gfpn_dense + raise ValueError("'meataxe' matrix can only deal with finite fields of order < 256") + + if implementation == 'numpy': + if R is sage.rings.real_double.RDF: + from . import matrix_real_double_dense + return matrix_real_double_dense.Matrix_real_double_dense + if R is sage.rings.complex_double.CDF: + from . import matrix_complex_double_dense + return matrix_complex_double_dense.Matrix_complex_double_dense + raise ValueError("'numpy' matrices are only available over RDF and CDF") + + if implementation == 'rational': + if sage.rings.number_field.number_field.is_CyclotomicField(R): + from . import matrix_cyclo_dense + return matrix_cyclo_dense.Matrix_cyclo_dense + raise ValueError("'rational' matrices are only available over a cyclotomic field") + + if implementation == 'linbox-float': + from . import matrix_modn_dense_float + if R.order() < matrix_modn_dense_float.MAX_MODULUS: + return matrix_modn_dense_float.Matrix_modn_dense_float + raise ValueError("'linbox-float' matrices can only deal with order < %s" % matrix_modn_dense_float.MAX_MODULUS) + + if implementation == 'linbox-double': + from . import matrix_modn_dense_double + if R.order() < matrix_modn_dense_double.MAX_MODULUS: + return matrix_modn_dense_double.Matrix_modn_dense_double + raise ValueError("'linbox-double' matrices can only deal with order < %s" % matrix_modn_dense_double.MAX_MODULUS) + + if implementation == 'generic': return matrix_generic_dense.Matrix_generic_dense - else: - if implementation is not None: - raise ValueError("can not choose an implementation for sparse matrices") + if implementation == 'gap': + from .matrix_gap import Matrix_gap + return Matrix_gap + + raise ValueError("unknown matrix implementation %r over %r" % (implementation, R)) + + # By now, we are dealing with sparse matrices + if implementation is not None: + raise ValueError("can not choose an implementation for sparse matrices") + + if sage.rings.finite_rings.integer_mod_ring.is_IntegerModRing(R) and R.order() < matrix_modn_sparse.MAX_MODULUS: + return matrix_modn_sparse.Matrix_modn_sparse - if sage.rings.finite_rings.integer_mod_ring.is_IntegerModRing(R) and R.order() < matrix_modn_sparse.MAX_MODULUS: - return matrix_modn_sparse.Matrix_modn_sparse - elif sage.rings.rational_field.is_RationalField(R): - return matrix_rational_sparse.Matrix_rational_sparse - elif sage.rings.integer_ring.is_IntegerRing(R): - return matrix_integer_sparse.Matrix_integer_sparse + if sage.rings.rational_field.is_RationalField(R): + return matrix_rational_sparse.Matrix_rational_sparse + + if sage.rings.integer_ring.is_IntegerRing(R): + return matrix_integer_sparse.Matrix_integer_sparse + + # the fallback + return matrix_generic_sparse.Matrix_generic_sparse - # the default - return matrix_generic_sparse.Matrix_generic_sparse class MatrixSpace(UniqueRepresentation, Parent): @@ -425,10 +445,9 @@ class MatrixSpace(UniqueRepresentation, Parent): sage: M1(m * m) == M1(m) * M1(m) True """ - _no_generic_basering_coercion = True @staticmethod - def __classcall__(cls, base_ring, nrows, ncols=None, sparse=False, implementation=None): + def __classcall__(cls, base_ring, nrows, ncols=None, sparse=False, implementation=None, **kwds): """ Normalize the arguments to call the ``__init__`` constructor. @@ -454,6 +473,24 @@ def __classcall__(cls, base_ring, nrows, ncols=None, sparse=False, implementatio Traceback (most recent call last): ... ValueError: unknown matrix implementation 'foobar' over Integer Ring + + Check that :trac:`29466`is fixed:: + + sage: class MyMatrixSpace(MatrixSpace): + ....: @staticmethod + ....: def __classcall__(cls, base_ring, nrows, ncols=None, my_option=True, sparse=False, implementation=None): + ....: return super(MyMatrixSpace, cls).__classcall__(cls, base_ring, nrows, ncols=ncols, my_option=my_option, sparse=sparse, implementation=implementation) + ....: + ....: def __init__(self, base_ring, nrows, ncols, sparse, implementation, my_option=True): + ....: super(MyMatrixSpace, self).__init__(base_ring, nrows, ncols, sparse, implementation) + ....: self._my_option = my_option + + sage: MS1 = MyMatrixSpace(ZZ, 2) + sage: MS1._my_option + True + sage: MS2 = MyMatrixSpace(ZZ, 2, my_option=False) + sage: MS2._my_option + False """ if base_ring not in _Rings: raise TypeError("base_ring (=%s) must be a ring"%base_ring) @@ -473,7 +510,7 @@ def __classcall__(cls, base_ring, nrows, ncols=None, sparse=False, implementatio matrix_cls = get_matrix_class(base_ring, nrows, ncols, sparse, implementation) return super(MatrixSpace, cls).__classcall__( - cls, base_ring, nrows, ncols, sparse, matrix_cls) + cls, base_ring, nrows, ncols, sparse, matrix_cls, **kwds) def __init__(self, base_ring, nrows, ncols, sparse, implementation): r""" @@ -951,19 +988,17 @@ def _get_action_(self, S, op, self_on_left): except TypeError: return None - def _coerce_map_from_(self, S): + def _coerce_map_from_base_ring(self): """ - Canonical coercion from ``S`` to this matrix space. + Return a coercion map from the base ring of ``self``. + + .. NOTE:: + + This is only called for algebras of square matrices. EXAMPLES:: sage: MS1 = MatrixSpace(QQ, 3) - sage: MS2 = MatrixSpace(ZZ, 3) - sage: MS1.coerce_map_from(MS2) - Coercion map: - From: Full MatrixSpace of 3 by 3 dense matrices over Integer Ring - To: Full MatrixSpace of 3 by 3 dense matrices over Rational Field - sage: MS2.coerce_map_from(MS1) sage: MS1.coerce_map_from(QQ) Coercion map: From: Rational Field @@ -979,12 +1014,32 @@ def _coerce_map_from_(self, S): Coercion map: From: Rational Field To: Full MatrixSpace of 3 by 3 dense matrices over Rational Field + + sage: MS2 = MatrixSpace(ZZ, 3) sage: MS2.coerce_map_from(QQ) sage: MS2.coerce_map_from(ZZ) Coercion map: From: Integer Ring To: Full MatrixSpace of 3 by 3 dense matrices over Integer Ring + sage: MatrixSpace(QQ, 1, 3).coerce_map_from(QQ) + """ + return self._generic_coerce_map(self.base_ring()) + + def _coerce_map_from_(self, S): + """ + Canonical coercion from ``S`` to this matrix space. + + EXAMPLES:: + + sage: MS1 = MatrixSpace(QQ, 3) + sage: MS2 = MatrixSpace(ZZ, 3) + sage: MS1.coerce_map_from(MS2) + Coercion map: + From: Full MatrixSpace of 3 by 3 dense matrices over Integer Ring + To: Full MatrixSpace of 3 by 3 dense matrices over Rational Field + sage: MS2.coerce_map_from(MS1) + There are also coercions possible from matrix group and arithmetic subgroups:: @@ -1065,11 +1120,6 @@ def _coerce_map_from_(self, S): """ B = self.base() - if S is B: - # Coercion from base ring to a scalar matrix, - # but only if matrices are square. - return self.nrows() == self.ncols() - if isinstance(S, MatrixSpace): # Disallow coercion if dimensions do not match if self.nrows() != S.nrows() or self.ncols() != S.ncols(): @@ -1450,7 +1500,7 @@ def __getitem__(self, x): ... AttributeError: 'MatrixSpace_with_category' object has no attribute 'list' """ - if isinstance(x, integer_types + (integer.Integer,)): + if isinstance(x, (integer.Integer, int)): return self.list()[x] return super(MatrixSpace, self).__getitem__(x) @@ -1565,6 +1615,62 @@ def identity_matrix(self): one = identity_matrix + def diagonal_matrix(self, entries): + """ + Create a diagonal matrix in ``self`` using the specified elements + + INPUT: + + - ``entries`` -- the elements to use as the diagonal entries + + ``self`` must be a space of square matrices. The length of + ``entries`` must be less than or equal to the matrix + dimensions. If the length of ``entries`` is less than the + matrix dimensions, ``entries`` is padded with zeroes at the + end. + + EXAMPLES:: + + sage: MS1 = MatrixSpace(ZZ,4) + sage: MS2 = MatrixSpace(QQ,3,4) + sage: I = MS1.diagonal_matrix([1, 2, 3, 4]) + sage: I + [1 0 0 0] + [0 2 0 0] + [0 0 3 0] + [0 0 0 4] + sage: MS2.diagonal_matrix([1, 2]) + Traceback (most recent call last): + ... + TypeError: diagonal matrix must be square + sage: MS1.diagonal_matrix([1, 2, 3, 4, 5]) + Traceback (most recent call last): + ... + ValueError: number of diagonal matrix entries (5) exceeds the matrix size (4) + sage: MS1.diagonal_matrix([1/2, 2, 3, 4]) + Traceback (most recent call last): + ... + TypeError: no conversion of this rational to integer + + Check different implementations:: + + sage: M1 = MatrixSpace(ZZ, 2, implementation='flint') + sage: M2 = MatrixSpace(ZZ, 2, implementation='generic') + + sage: type(M1.diagonal_matrix([1, 2])) + + sage: type(M2.diagonal_matrix([1, 2])) + + """ + if self.__nrows != self.__ncols: + raise TypeError("diagonal matrix must be square") + if self.__nrows < len(entries): + raise ValueError('number of diagonal matrix entries (%s) exceeds the matrix size (%s)' % (len(entries), self.__nrows)) + A = self.zero_matrix().__copy__() + for i in range(len(entries)): + A[i, i] = entries[i] + return A + def is_dense(self): """ Return whether matrices in ``self`` are dense. @@ -2122,7 +2228,7 @@ def dict_to_list(entries, nrows, ncols): [1, 0, 0, 0, 2, 0] """ v = [0] * (nrows * ncols) - for ij, y in iteritems(entries): + for ij, y in entries.items(): i, j = ij v[i * ncols + j] = y return v diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index e69578ace8d..0c124232ef1 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -19,7 +19,7 @@ cimport sage.matrix.matrix0 as matrix0 from sage.structure.element cimport Element, RingElement, ModuleElement, Vector from sage.structure.richcmp cimport richcmp_item, rich_to_bool from sage.rings.ring import is_Ring -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from cpython cimport * from cpython.object cimport Py_EQ, Py_NE @@ -177,17 +177,20 @@ cdef class Matrix_sparse(matrix.Matrix): sage: type(A) sage: B = matrix(QQ['x,y'], 2, [-1,-1,-2,-2], sparse=True) - sage: A*B + sage: A * B [2 2] [2 2] + sage: B * A + [-2 3] + [-4 6] """ cdef Py_ssize_t row, col, row_start, k1, k2, len_left, len_right, a, b - left_nonzero = left.nonzero_positions(copy=False, column_order=False) - right_nonzero = right.nonzero_positions(copy=False, column_order=True) + cdef list left_nonzero = left.nonzero_positions(copy=False, column_order=False) + cdef list right_nonzero = right.nonzero_positions(copy=False, column_order=True) len_left = len(left_nonzero) len_right = len(right_nonzero) - e = {} + cdef dict e = {} k1 = 0 while k1 < len_left: row_start = k1 @@ -196,30 +199,30 @@ cdef class Matrix_sparse(matrix.Matrix): while k2 < len_right: sig_check() col = get_ij(right_nonzero, k2, 1) - sum = None + s = None k1 = row_start - while k1 < len_left and get_ij(left_nonzero,k1,0) == row and \ - k2 < len_right and get_ij(right_nonzero,k2,1) == col: + while (k1 < len_left and get_ij(left_nonzero,k1,0) == row + and k2 < len_right and get_ij(right_nonzero,k2,1) == col): sig_check() - a = get_ij(left_nonzero, k1,1) - b = get_ij(right_nonzero,k2,0) + a = get_ij(left_nonzero, k1, 1) + b = get_ij(right_nonzero, k2, 0) if a == b: - if sum is None: - sum = left.get_unsafe(row,a)*right.get_unsafe(a,col) + if s is None: + s = left.get_unsafe(row,a) * right.get_unsafe(a,col) else: - sum = sum + left.get_unsafe(row,a)*right.get_unsafe(a,col) - k1 = k1 + 1 - k2 = k2 + 1 + s += left.get_unsafe(row,a) * right.get_unsafe(a,col) + k1 += 1 + k2 += 1 elif a < b: - k1 = k1 + 1 + k1 += 1 else: - k2 = k2 + 1 - if sum is not None: - e[row, col] = sum - while k2 < len_right and get_ij(right_nonzero,k2,1) == col: - k2 = k2 + 1 - while k1 < len_left and get_ij(left_nonzero,k1,0) == row: - k1 = k1 + 1 + k2 += 1 + if s is not None: + e[row, col] = s + while k2 < len_right and get_ij(right_nonzero, k2, 1) == col: + k2 += 1 + while k1 < len_left and get_ij(left_nonzero, k1, 0) == row: + k1 += 1 return left.new_matrix(left._nrows, right._ncols, entries=e, coerce=False, copy=False) def _multiply_classical_with_cache(Matrix_sparse left, Matrix_sparse right): @@ -747,6 +750,12 @@ cdef class Matrix_sparse(matrix.Matrix): sage: m.apply_map(lambda x: x+1) [1|1] [4|1] + + When applying a map to a sparse zero matrix, the codomain is determined + from the image of zero (:trac:`29214`):: + + sage: matrix(RR, 2, 2, sparse=True).apply_map(floor).base_ring() is ZZ + True """ if self._nrows==0 or self._ncols==0: if not sparse: @@ -756,8 +765,6 @@ cdef class Matrix_sparse(matrix.Matrix): self_dict = self._dict() if len(self_dict) < self._nrows * self._ncols: zero_res = phi(self.base_ring()(0)) - if zero_res.is_zero(): - zero_res = None else: zero_res = None v = [(ij, phi(z)) for ij,z in self_dict.iteritems()] @@ -770,7 +777,7 @@ cdef class Matrix_sparse(matrix.Matrix): v = {v[i][0]: w[i] for i in range(len(v))} else: v = dict(v) - if zero_res is not None: + if zero_res is not None and not zero_res.is_zero(): M = sage.matrix.matrix_space.MatrixSpace(R, self._nrows, self._ncols, sparse=sparse) m = M([zero_res] * (self._nrows * self._ncols)) diff --git a/src/sage/matrix/matrix_symbolic_dense.pyx b/src/sage/matrix/matrix_symbolic_dense.pyx index 2065a61e844..e4e009e68d2 100644 --- a/src/sage/matrix/matrix_symbolic_dense.pyx +++ b/src/sage/matrix/matrix_symbolic_dense.pyx @@ -772,7 +772,7 @@ cdef class Matrix_symbolic_dense(Matrix_generic_dense): [ x^2 + 2 -2*x + 3] [ -4*x + 6 x^2 - 6*x + 11] """ - from sage.misc.misc import attrcall + from sage.misc.call import attrcall return self.apply_map(attrcall('expand')) def variables(self): @@ -905,3 +905,24 @@ cdef class Matrix_symbolic_dense(Matrix_generic_dense): new_entries.append(entry) return self.parent(new_entries) + + cdef bint get_is_zero_unsafe(self, Py_ssize_t i, Py_ssize_t j): + r""" + Return 1 if the entry ``(i, j)`` is zero, otherwise 0. + + EXAMPLES:: + + sage: M = matrix(SR, [[0,1,0],[0,0,0]]) + sage: M.zero_pattern_matrix() # indirect doctest + [1 0 1] + [1 1 1] + """ + entry = self.get_unsafe(i, j) + # See if we can avoid the full proof machinery that the entry is 0 + if entry.is_trivial_zero(): + return 1 + if entry: + return 0 + else: + return 1 + diff --git a/src/sage/matrix/misc.pyx b/src/sage/matrix/misc.pyx index b69bcf78298..9008edf5920 100644 --- a/src/sage/matrix/misc.pyx +++ b/src/sage/matrix/misc.pyx @@ -44,7 +44,7 @@ from sage.rings.real_mpfr import is_RealField from sage.rings.real_mpfr cimport RealNumber -from sage.misc.misc import verbose, get_verbose +from sage.misc.verbose import verbose, get_verbose def matrix_integer_dense_rational_reconstruction(Matrix_integer_dense A, Integer N): """ diff --git a/src/sage/matrix/operation_table.py b/src/sage/matrix/operation_table.py index 4690ddf97e7..99e2f5cf009 100644 --- a/src/sage/matrix/operation_table.py +++ b/src/sage/matrix/operation_table.py @@ -16,7 +16,6 @@ from __future__ import absolute_import -import six from sage.structure.sage_object import SageObject class OperationTable(SageObject): @@ -542,12 +541,12 @@ def _name_maker(self, names): if len(names) != self._n: raise ValueError('list of element names must be the same size as the set, %s != %s'%(len(names), self._n)) width = 0 - for str in names: - if not isinstance(str, six.string_types): - raise ValueError('list of element names must only contain strings, not %s'%str) - if len(str) > width: - width = len(str) - name_list.append(str) + for name in names: + if not isinstance(name, str): + raise ValueError('list of element names must only contain strings, not %s' % name) + if len(name) > width: + width = len(name) + name_list.append(name) else: raise ValueError("element names must be a list, or one of the keywords: 'letters', 'digits', 'elements'") name_dict = {} @@ -717,9 +716,9 @@ def set_print_symbols(self, ascii, latex): ... ValueError: ASCII symbol should be a single character, not 5 """ - if not isinstance(ascii, six.string_types) or not len(ascii)==1: + if not isinstance(ascii, str) or not len(ascii)==1: raise ValueError('ASCII symbol should be a single character, not %s' % ascii) - if not isinstance(latex, six.string_types): + if not isinstance(latex, str): raise ValueError('LaTeX symbol must be a string, not %s' % latex) self._ascii_symbol = ascii self._latex_symbol = latex diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index 6d6c2bdcf39..5bc4e4e116e 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -63,15 +63,14 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import, division -from six.moves import range -from six import integer_types -import sage.rings.all as rings from sage.rings.ring import is_Ring import sage.matrix.matrix_space as matrix_space from sage.modules.free_module_element import vector from sage.structure.element import is_Matrix -from sage.rings.all import ZZ, QQ +from sage.rings.integer_ring import ZZ +from sage.rings.rational_field import QQ +from sage.rings.integer import Integer from sage.misc.misc_c import running_total from copy import copy from .constructor import matrix @@ -801,7 +800,7 @@ def diagonal_matrix(arg0=None, arg1=None, arg2=None, sparse=True): # Size of matrix specified? # Formats 2, 4 nrows = None - if isinstance(arg0, integer_types + (rings.Integer,)): + if isinstance(arg0, (Integer, int)): nrows = arg0 arg0 = arg1 # Object holding entries @@ -819,14 +818,14 @@ def diagonal_matrix(arg0=None, arg1=None, arg2=None, sparse=True): except TypeError: raise TypeError('unable to determine number of entries for diagonal matrix construction') # sometimes catches a negative size - if not nrows is None and nentries > nrows: + if nrows is not None and nentries > nrows: raise ValueError('number of diagonal matrix entries (%s) exceeds the requested matrix size (%s)' % (nentries, nrows)) if nrows is None: nrows = nentries # provide a default ring for an empty list if not len(entries) and ring is None: - ring = rings.ZZ + ring = ZZ # Convert entries to a list v over a common ring from sage.modules.free_module_element import prepare @@ -834,9 +833,7 @@ def diagonal_matrix(arg0=None, arg1=None, arg2=None, sparse=True): # Create a "diagonal" dictionary for matrix constructor # If nentries < nrows, diagonal is effectively padded with zeros at end - w = {} - for i in range(len(v)): - w[(i, i)] = v[i] + w = {(i, i): v[i] for i in range(len(v))} # Ship ring, matrix size, dictionary to matrix constructor if ring is None: @@ -876,11 +873,12 @@ def identity_matrix(ring, n=0, sparse=False): sage: M.is_mutable() True """ - if isinstance(ring, integer_types + (rings.Integer,)): + if isinstance(ring, (Integer, int)): n = ring - ring = rings.ZZ + ring = ZZ return matrix_space.MatrixSpace(ring, n, n, sparse)(1) + @matrix_method def lehmer(ring, n=0): r""" @@ -902,11 +900,12 @@ def lehmer(ring, n=0): """ from sage.sets.integer_range import IntegerRange - if isinstance(ring, integer_types + (rings.Integer,)): + if isinstance(ring, (Integer, int)): n = ring - ring = rings.QQ + ring = QQ return matrix_space.MatrixSpace(ring, n, n).matrix([[min(i, j)/max(i, j) for i in IntegerRange(1, n+1)] for j in IntegerRange(1, n+1)]) + @matrix_method def zero_matrix(ring, nrows=None, ncols=None, sparse=False): r""" @@ -945,11 +944,12 @@ def zero_matrix(ring, nrows=None, ncols=None, sparse=False): [0 0 0 0 0] """ - if isinstance(ring, integer_types + (rings.Integer,)): + if isinstance(ring, (Integer, int)): nrows, ncols = (ring, nrows) - ring = rings.ZZ + ring = ZZ return matrix_space.MatrixSpace(ring, nrows, ncols, sparse)(0) + @matrix_method def ones_matrix(ring, nrows=None, ncols=None, sparse=False): r""" @@ -1030,9 +1030,9 @@ def ones_matrix(ring, nrows=None, ncols=None, sparse=False): ... ValueError: constructing an all ones matrix requires at least one dimension """ - if isinstance(ring, integer_types + (rings.Integer,)): + if isinstance(ring, (Integer, int)): nrows, ncols = (ring, nrows) - ring = rings.ZZ + ring = ZZ if nrows is None: raise ValueError("constructing an all ones matrix requires at least one dimension") if ncols is None: @@ -1352,7 +1352,7 @@ def elementary_matrix(arg0, arg1=None, **kwds): """ import sage.structure.element # determine ring and matrix size - if not arg1 is None and not is_Ring(arg0): + if arg1 is not None and not is_Ring(arg0): raise TypeError('optional first parameter must be a ring, not {0}'.format(arg0)) scale = kwds.pop('scale', None) if is_Ring(arg0): @@ -1363,11 +1363,11 @@ def elementary_matrix(arg0, arg1=None, **kwds): raise TypeError('scale must be an element of some ring, not {0}'.format(scale)) R = scale.parent() else: - R = rings.ZZ + R = ZZ if arg0 is None: raise ValueError('size of elementary matrix must be given') try: - n = rings.Integer(arg0) + n = Integer(arg0) except TypeError: raise TypeError('size of elementary matrix must be an integer, not {0}'.format(arg0)) if n <= 0: @@ -1378,9 +1378,9 @@ def elementary_matrix(arg0, arg1=None, **kwds): col1 = kwds.pop('col1', None) if row1 is None and col1 is None: raise ValueError('row1 or col1 must be specified') - if not row1 is None and not col1 is None: + if row1 is not None and col1 is not None: raise ValueError('cannot specify both row1 and col1') - rowop = not row1 is None + rowop = row1 is not None if rowop: opstring = "row" row2 = kwds.pop('row2', None) @@ -1395,19 +1395,19 @@ def elementary_matrix(arg0, arg1=None, **kwds): # analyze parameters to determine matrix type try: - row1 = rings.Integer(row1) + row1 = Integer(row1) except TypeError: raise TypeError('{0} of elementary matrix must be an integer, not {1}'.format(opstring, row1)) - if row1 < 0 or row1 >= n : + if row1 < 0 or row1 >= n: raise ValueError('{0} of elementary matrix must be positive and smaller than {1}, not {2}'.format(opstring, n, row1)) - if not row2 is None: + if row2 is not None: try: - row2 = rings.Integer(row2) + row2 = Integer(row2) except TypeError: raise TypeError('{0} of elementary matrix must be an integer, not {1}'.format(opstring, row2)) - if row2 < 0 or row2 >= n : + if row2 < 0 or row2 >= n: raise ValueError('{0} of elementary matrix must be positive and smaller than {1}, not {2}'.format(opstring, n, row2)) - if not scale is None: + if scale is not None: try: scale = R(scale) except Exception: @@ -1418,16 +1418,16 @@ def elementary_matrix(arg0, arg1=None, **kwds): elem = identity_matrix(R, n, sparse=sparse) if row2 is None and scale is None: raise ValueError('insufficient parameters provided to construct elementary matrix') - elif not row2 is None and not scale is None: + elif row2 is not None and scale is not None: if row1 == row2: raise ValueError('cannot add a multiple of a {0} to itself'.format(opstring)) elem[row1, row2] = scale - elif not row2 is None and scale is None: + elif row2 is not None and scale is None: elem[row1, row1] = 0 elem[row2, row2] = 0 elem[row1, row2] = 1 elem[row2, row1] = 1 - elif row2 is None and not scale is None: + elif row2 is None and scale is not None: if scale == 0: raise ValueError('scale parameter of {0} of elementary matrix must be non-zero'.format(opstring)) elem[row1, row1] = scale @@ -1436,6 +1436,7 @@ def elementary_matrix(arg0, arg1=None, **kwds): else: return elem.transpose() + @matrix_method def circulant(v, sparse=None): r""" @@ -1564,6 +1565,7 @@ def _determine_block_matrix_grid(sub_matrices): return (row_heights, col_widths) + def _determine_block_matrix_rows(sub_matrices): """ For internal use. This tests if the matrices in sub_matrices @@ -1579,7 +1581,7 @@ def _determine_block_matrix_rows(sub_matrices): Non-zero scalars are considered to be square matrices of any size, and zeroes are considered to be zero matrices of any size. - A ValueError is raised if there is insufficient or + A ``ValueError`` is raised if there is insufficient or conflicting information. TESTS:: @@ -1594,9 +1596,8 @@ def _determine_block_matrix_rows(sub_matrices): ([2, 2], [0, 0], 4) """ total_width = None - - row_heights = [ None ] * len(sub_matrices) - zero_widths = [ 0 ] * len(sub_matrices) + row_heights = [None] * len(sub_matrices) + zero_widths = [0] * len(sub_matrices) # We first do a pass to see if we can determine the width unknowns = False @@ -1668,7 +1669,7 @@ def _determine_block_matrix_rows(sub_matrices): elif zero_state == 2: zero_state = 3 else: - scalars += 1 + scalars += 1 remaining_width = total_width - width # This remaining width has to be split over the @@ -1689,7 +1690,7 @@ def _determine_block_matrix_rows(sub_matrices): # if we don't know the height, and there are zeroes, # we can't determine the height raise ValueError("insufficient information to determine submatrix heights") - elif total_width % len(R) != 0: + elif total_width % len(R): raise ValueError("incompatible submatrix widths") else: height = int(total_width / len(R)) @@ -1845,32 +1846,14 @@ def block_matrix(*args, **kwds): ... ValueError: insufficient information to determine submatrix widths - Historically, giving only a flat list of submatrices, whose number - was a perfect square, would create a new matrix by laying out the submatrices - in a square grid. This behavior is now deprecated. :: + Giving only a flat list of submatrices does not work:: sage: A = matrix(2, 3, range(6)) sage: B = matrix(3, 3, range(9)) sage: block_matrix([A, A, B, B]) - doctest:...: DeprecationWarning: invocation of block_matrix with just a list whose length is a perfect square is deprecated. See the documentation for details. - [0 1 2|0 1 2] - [3 4 5|3 4 5] - [-----+-----] - [0 1 2|0 1 2] - [3 4 5|3 4 5] - [6 7 8|6 7 8] - - Historically, a flat list of matrices whose number is not a perfect square, - with no specification of the number of rows or columns, would raise an error. - This behavior continues, but could be removed when the deprecation above is - completed. :: - - sage: A = matrix(2, 3, range(6)) - sage: B = matrix(3, 3, range(9)) - sage: block_matrix([A, A, A, B, B, B]) Traceback (most recent call last): ... - ValueError: must specify nrows or ncols for non-square block matrix. + ValueError: must specify either nrows or ncols TESTS:: @@ -1884,9 +1867,9 @@ def block_matrix(*args, **kwds): if not args: if sparse is not None: - return matrix_space.MatrixSpace(rings.ZZ, 0, 0, sparse=sparse)([]) + return matrix_space.MatrixSpace(ZZ, 0, 0, sparse=sparse)([]) else: - return matrix_space.MatrixSpace(rings.ZZ, 0, 0)([]) + return matrix_space.MatrixSpace(ZZ, 0, 0)([]) if len(args) >= 1 and is_Ring(args[0]): # A ring is specified @@ -1972,24 +1955,19 @@ def block_matrix(*args, **kwds): else: # A flat list # determine the block dimensions - n = ZZ(len(sub_matrices)) + n = len(sub_matrices) if nrows is None: if ncols is None: - if n.is_square(): - import warnings - warnings.warn("invocation of block_matrix with just a list whose length is a perfect square is deprecated. See the documentation for details.", DeprecationWarning, stacklevel=2) - nrows = ncols = n.sqrt() - else: - # this form (ie just a flat list) could be allowed once deprecated invocation (above) goes away - raise ValueError("must specify nrows or ncols for non-square block matrix.") + raise ValueError("must specify either nrows or ncols") else: - nrows = int(n/ncols) + nrows = n // ncols elif ncols is None: - ncols = int(n/nrows) + ncols = n // nrows if nrows * ncols != n: raise ValueError("given number of rows (%s), columns (%s) incompatible with number of submatrices (%s)" % (nrows, ncols, n)) # Now create a list of lists from this - sub_matrices = [ sub_matrices[i*ncols : (i+1)*ncols] for i in range(nrows) ] + sub_matrices = [sub_matrices[i * ncols: (i + 1) * ncols] + for i in range(nrows)] # At this point sub_matrices is a list of lists @@ -2022,7 +2000,6 @@ def block_matrix(*args, **kwds): if subdivide: raise ValueError(e) - if col_widths is None: # Try placing the matrices in rows instead # (Only if subdivide is False) @@ -2697,21 +2674,20 @@ def random_echelonizable_matrix(parent, rank, upper_bound=None, max_tries=100): matrix.add_multiple_of_row(0, randint(1,rows-1), randint(-3,3)) else: if rank == 1: # would be better just to have a special generator... - tries = 0 - while max(map(abs,matrix.list())) >= upper_bound: - matrix = random_rref_matrix(parent, rank) - tries += 1 - if tries > max_tries: # to prevent endless attempts - raise ValueError("tried "+str(max_tries)+" times to get a rank 1 random matrix. Try bigger upper_bound?") - matrix_copy = matrix - - rrr = range(len(matrix.pivots())-1,-1,-1) - for pivots in rrr: + tries = 0 + while max(abs(c) for c in matrix.list()) >= upper_bound: + matrix = random_rref_matrix(parent, rank) + tries += 1 + if tries > max_tries: # to prevent endless attempts + raise ValueError("tried "+str(max_tries)+" times to get a rank 1 random matrix. Try bigger upper_bound?") + matrix_copy = matrix + + for pivots in range(len(matrix.pivots()) - 1, -1, -1): # keep track of the pivot column positions from the pivot column with the largest index to # the one with the smallest. - row_index=0 + row_index = 0 tries = 0 - while row_index # Copyright (C) 2013 Stefan van Zwam # @@ -26,13 +26,14 @@ Methods # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** include 'sage/data_structures/bitset.pxi' from .basis_matroid cimport BasisMatroid from sage.arith.all import binomial + cdef class CutNode: """ An internal class used for creating linear subclasses of a matroids in a @@ -41,11 +42,11 @@ cdef class CutNode: A linear subclass is a set of hyperplanes `mc` with the property that certain sets of hyperplanes must either be fully contained in `mc` or intersect `mc` in at most 1 element. The way we generate them is by a - depth-first seach. This class represents a node in the search tree. + depth-first search. This class represents a node in the search tree. It contains the set of hyperplanes selected so far, as well as a collection of hyperplanes whose insertion has been explored elsewhere in - the seach tree. + the search tree. The class has methods for selecting a hyperplane to insert, for inserting hyperplanes and closing the set to become a linear subclass again, and for diff --git a/src/sage/matroids/linear_matroid.pyx b/src/sage/matroids/linear_matroid.pyx index e72f7748d9e..bf354d86ab7 100644 --- a/src/sage/matroids/linear_matroid.pyx +++ b/src/sage/matroids/linear_matroid.pyx @@ -5870,7 +5870,7 @@ cdef class RegularMatroid(LinearMatroid): ALGORITHM: Since the matroid is regular, we use Kirchhoff's Matrix-Tree Theorem. - See also :wikipedia:`Kirchhoff's_theorem`. + See also :wikipedia:`Kirchhoff%27s_theorem`. """ if self._bases_count is None: R = self._basic_representation()._matrix_() diff --git a/src/sage/matroids/matroid.pxd b/src/sage/matroids/matroid.pxd index 28bd0631a8c..cd1df0244f7 100644 --- a/src/sage/matroids/matroid.pxd +++ b/src/sage/matroids/matroid.pxd @@ -171,7 +171,7 @@ cdef class Matroid(SageObject): cpdef _is_4connected_shifting(self, certificate=*) cpdef _shifting_all(self, X, P_rows, P_cols, Q_rows, Q_cols, m) cpdef _shifting(self, X, X_1, Y_2, X_2, Y_1, m) - cpdef is_3connected(self, certificate=*, algorithm=*, separation=*) + cpdef is_3connected(self, certificate=*, algorithm=*) cpdef is_4connected(self, certificate=*, algorithm=*) cpdef _is_3connected_CE(self, certificate=*) cpdef _is_3connected_BC(self, certificate=*) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 12e060fc88d..6c8e736e80b 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -337,7 +337,6 @@ from .set_system cimport SetSystem from sage.graphs.spanning_tree import kruskal from sage.graphs.graph import Graph from sage.matrix.constructor import matrix -from sage.misc.superseded import deprecation from .utilities import newlabel, sanitize_contractions_deletions, spanning_forest, spanning_stars from sage.rings.all import ZZ @@ -3785,18 +3784,6 @@ cdef class Matroid(SageObject): """ return self.minor(contractions=X) - def __div__(self, X): - r""" - Shorthand for ``self.contract(X)``. - - EXAMPLES:: - - sage: M = matroids.CompleteGraphic(4) - sage: M.contract(1) == M / 1 # indirect doctest - True - """ - return self.contract(X) - def __truediv__(self, X): r""" Shorthand for ``self.contract(X)``. @@ -5112,7 +5099,7 @@ cdef class Matroid(SageObject): return True, None return True - cpdef is_3connected(self, certificate=False, algorithm=None, separation=False): + cpdef is_3connected(self, certificate=False, algorithm=None): r""" Return ``True`` if the matroid is 3-connected, ``False`` otherwise. It can optionally return a separator as a witness. @@ -5179,18 +5166,15 @@ cdef class Matroid(SageObject): sage: M.connectivity(X) 1 """ - if separation: - deprecation(18539, "Use `certificate` in place of `separation`") - certificate = certificate or separation if algorithm is None: if certificate: return self._is_3connected_CE(True) else: return self._is_3connected_BC() if algorithm == "bridges": - return self._is_3connected_BC(separation) + return self._is_3connected_BC(certificate) if algorithm == "intersection": - return self._is_3connected_CE(separation) + return self._is_3connected_CE(certificate) if algorithm == "shifting": return self._is_3connected_shifting(certificate) raise ValueError("Not a valid algorithm.") @@ -6627,7 +6611,7 @@ cdef class Matroid(SageObject): sage: M.is_max_weight_independent_generic() False - Here is an example from [GriRei18]_ (Example 7.4.12 in v5):: + Here is an example from [GriRei18]_ (Example 7.4.12 in v6):: sage: A = Matrix(QQ, [[ 1, 1, 0, 0], ....: [-1, 0, 1, 1], @@ -6785,7 +6769,7 @@ cdef class Matroid(SageObject): sage: M.dual().is_max_weight_coindependent_generic(weights=wt) True - Here is an example from [GriRei18]_ (Example 7.4.12 in v5):: + Here is an example from [GriRei18]_ (Example 7.4.12 in v6):: sage: A = Matrix(QQ, [[ 1, 1, 0, 0], ....: [-1, 0, 1, 1], diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 1967c76c485..bef61c33bfd 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -524,7 +524,7 @@ def addlp(M, M1, L, P, ptsdict, G=None, limits=None): if P: # create list of lists where inner lists are parallel classes pcls = [] - gnd = sorted(list(M1.groundset())) + gnd = sorted(M1.groundset()) for g in gnd: pcl = [g] for p in P: diff --git a/src/sage/matroids/rank_matroid.py b/src/sage/matroids/rank_matroid.py index ca504243412..164739a5ce5 100644 --- a/src/sage/matroids/rank_matroid.py +++ b/src/sage/matroids/rank_matroid.py @@ -18,7 +18,6 @@ class ``RankMatroid``. All that is required is a groundset and a function that sage: def f(X): ....: return min(len(X), 3) - ....: sage: M = Matroid(groundset=range(6), rank_function=f) sage: M.is_valid() True @@ -30,7 +29,6 @@ class ``RankMatroid``. All that is required is a groundset and a function that ....: return 1 ....: else: ....: return 0 - ....: sage: N = Matroid(groundset='abc', rank_function=g) sage: N.is_valid() False @@ -82,7 +80,6 @@ class RankMatroid(Matroid): sage: from sage.matroids.advanced import * sage: def f(X): ....: return min(len(X), 3) - ....: sage: M = RankMatroid(groundset=range(6), rank_function=f) sage: M.is_valid() True @@ -195,10 +192,8 @@ def __eq__(self, other): sage: from sage.matroids.advanced import * sage: def f(X): ....: return min(len(X), 3) - ....: sage: def g(X): ....: return min(len(X), 3) - ....: sage: M1 = RankMatroid(groundset=range(6), rank_function=f) sage: M2 = RankMatroid(groundset=range(6), rank_function=g) sage: M3 = RankMatroid(groundset=range(7), rank_function=f) @@ -238,10 +233,8 @@ def __ne__(self, other): sage: from sage.matroids.advanced import * sage: def f(X): ....: return min(len(X), 3) - ....: sage: def g(X): ....: return min(len(X), 3) - ....: sage: M1 = RankMatroid(groundset=range(6), rank_function=f) sage: M2 = RankMatroid(groundset=range(6), rank_function=g) sage: M3 = RankMatroid(groundset=range(7), rank_function=f) diff --git a/src/sage/matroids/utilities.py b/src/sage/matroids/utilities.py index 735705d3e87..5d156936ac3 100644 --- a/src/sage/matroids/utilities.py +++ b/src/sage/matroids/utilities.py @@ -24,7 +24,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six import iteritems from sage.matrix.constructor import Matrix from sage.rings.all import ZZ, QQ, GF @@ -127,7 +126,7 @@ def setprint_s(X, toplevel=False): return '{' + ', '.join(sorted(setprint_s(x) for x in X)) + '}' elif isinstance(X, dict): return '{' + ', '.join(sorted(setprint_s(key) + ': ' + setprint_s(val) - for key, val in iteritems(X))) + '}' + for key, val in X.items())) + '}' elif isinstance(X, str): if toplevel: return X @@ -558,7 +557,7 @@ def lift_cross_ratios(A, lift_map=None): True """ - for s, t in iteritems(lift_map): + for s, t in lift_map.items(): source_ring = s.parent() target_ring = t.parent() break @@ -629,13 +628,13 @@ def lift_cross_ratios(A, lift_map=None): div = True for entry2 in entries: if div: - for cr, degree in iteritems(F[entry2]): + for cr, degree in F[entry2].items(): if cr in monomial: monomial[cr] = monomial[cr] + degree else: monomial[cr] = degree else: - for cr, degree in iteritems(F[entry2]): + for cr, degree in F[entry2].items(): if cr in monomial: monomial[cr] = monomial[cr] - degree else: @@ -647,9 +646,9 @@ def lift_cross_ratios(A, lift_map=None): # compute each entry of Z as the product of lifted cross ratios Z = Matrix(target_ring, A.nrows(), A.ncols()) - for entry, monomial in iteritems(F): + for entry, monomial in F.items(): Z[entry] = plus_one2 - for cr, degree in iteritems(monomial): + for cr, degree in monomial.items(): if cr == minus_one1: Z[entry] = Z[entry] * (minus_one2**degree) else: diff --git a/src/sage/media/wav.py b/src/sage/media/wav.py index 3b68644f791..79ed045eb49 100644 --- a/src/sage/media/wav.py +++ b/src/sage/media/wav.py @@ -25,7 +25,6 @@ - Bobby Moretti (2007-07-03): add doctests """ from __future__ import print_function, absolute_import -from six.moves import range import math import os diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py index d3ea945c173..46765a0a435 100644 --- a/src/sage/misc/all.py +++ b/src/sage/misc/all.py @@ -3,17 +3,21 @@ from .lazy_import import lazy_import from .misc import (BackslashOperator, - cputime, verbose, set_verbose, set_verbose_files, - get_verbose_files, unset_verbose_files, get_verbose, + cputime, union, uniq, powerset, subsets, exists, forall, is_iterator, random_sublist, walltime, - repr_lincomb, - pad_zeros, attrcall, + pad_zeros, SAGE_DB, SAGE_TMP, newton_method_sizes, compose, nest) +from .verbose import (set_verbose, set_verbose_files, + get_verbose_files, unset_verbose_files, get_verbose) +lazy_import('sage.misc.verbose', 'verbose', + deprecation=17815) +from .call import attrcall + from .banner import version, banner from .temporary_file import tmp_dir, tmp_filename @@ -26,6 +30,8 @@ from .html import html +from .repr import repr_lincomb + from .table import table from .sage_timeit_class import timeit @@ -160,7 +166,7 @@ from .decorators import specialize, sage_wraps, infix_operator -from .unknown import Unknown +from .unknown import Unknown, UnknownError lazy_import('sage.misc.inline_fortran', 'fortran') diff --git a/src/sage/misc/bindable_class.py b/src/sage/misc/bindable_class.py index 5157de20b80..a3b29ee120c 100644 --- a/src/sage/misc/bindable_class.py +++ b/src/sage/misc/bindable_class.py @@ -15,11 +15,10 @@ from __future__ import absolute_import, print_function import functools -from sage.misc import six from sage.misc.nested_class import NestedClassMetaclass from sage.misc.classcall_metaclass import ClasscallMetaclass -class BindableClass(six.with_metaclass(ClasscallMetaclass)): +class BindableClass(metaclass=ClasscallMetaclass): """ Bindable classes @@ -259,7 +258,7 @@ class Inner2(BindableClass): """ # We need NestedClassMetaclass to work around a Python pickling bug -class Outer(six.with_metaclass(NestedClassMetaclass)): +class Outer(metaclass=NestedClassMetaclass): """ A class with a bindable nested class, for testing purposes """ diff --git a/src/sage/misc/call.py b/src/sage/misc/call.py new file mode 100644 index 00000000000..ddb05e44819 --- /dev/null +++ b/src/sage/misc/call.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +""" +Attribute and method calling +""" + +# **************************************************************************** +# Copyright (C) 2008 Mike Hansen +# Copyright (C) 2010, 2013 Nicolas M. Thiery +# Copyright (C) 2018 Frédéric Chapoton +# +# 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/ +# **************************************************************************** + +############################################# +# Operators +############################################# +class AttrCallObject(object): + def __init__(self, name, args, kwds): + """ + TESTS:: + + sage: f = attrcall('core', 3); f + *.core(3) + sage: TestSuite(f).run() + """ + self.name = name + self.args = args + self.kwds = kwds + + def __call__(self, x, *args): + """ + Gets the ``self.name`` method from ``x``, calls it with + ``self.args`` and ``args`` as positional parameters and + ``self.kwds`` as keyword parameters, and returns the result. + + EXAMPLES:: + + sage: core = attrcall('core', 3) + sage: core(Partition([4,2])) + [4, 2] + + sage: series = attrcall('series', x) + sage: series(sin(x), 4) + 1*x + (-1/6)*x^3 + Order(x^4) + """ + return getattr(x, self.name)(*(self.args + args), **self.kwds) + + def __repr__(self): + """ + Return a string representation of this object. + + The star in the output represents the object passed into ``self``. + + EXAMPLES:: + + sage: attrcall('core', 3) + *.core(3) + sage: attrcall('hooks', flatten=True) + *.hooks(flatten=True) + sage: attrcall('hooks', 3, flatten=True) + *.hooks(3, flatten=True) + """ + s = "*.%s(%s" % (self.name, ", ".join(map(repr, self.args))) + if self.kwds: + if self.args: + s += ", " + s += ", ".join("%s=%s" % keyvalue for keyvalue in self.kwds.items()) + s += ")" + return s + + def __eq__(self, other): + """ + Equality testing + + EXAMPLES:: + + sage: attrcall('core', 3, flatten = True) == attrcall('core', 3, flatten = True) + True + sage: attrcall('core', 2) == attrcall('core', 3) + False + sage: attrcall('core', 2) == 1 + False + """ + return self.__class__ == other.__class__ and self.__dict__ == other.__dict__ + + def __ne__(self, other): + """ + Equality testing + + EXAMPLES:: + + sage: attrcall('core', 3, flatten = True) != attrcall('core', 3, flatten = True) + False + sage: attrcall('core', 2) != attrcall('core', 3) + True + sage: attrcall('core', 2) != 1 + True + """ + return not self == other + + def __hash__(self): + """ + Hash value + + This method tries to ensure that, when two ``attrcall`` + objects are equal, they have the same hash value. + + .. warning:: dicts are not hashable, so we instead hash their + items; however the order of those items might differ. The + proper fix would be to use a frozen dict for ``kwds``, when + frozen dicts will be available in Python. + + EXAMPLES:: + + sage: x = attrcall('core', 3, flatten = True, blah = 1) + sage: hash(x) # random # indirect doctest + 210434060 + sage: type(hash(x)) + + sage: y = attrcall('core', 3, blah = 1, flatten = True) + sage: hash(y) == hash(x) + True + sage: y = attrcall('core', 3, flatten = True, blah = 2) + sage: hash(y) != hash(x) + True + sage: hash(attrcall('core', 2)) != hash(attrcall('core', 3)) + True + sage: hash(attrcall('core', 2)) != hash(1) + True + + Note: a missing ``__hash__`` method here used to break the + unique representation of parents taking ``attrcall`` objects + as input; see :trac:`8911`. + """ + return hash((self.args, tuple(sorted(self.kwds.items())))) + + +def attrcall(name, *args, **kwds): + """ + Return a callable which takes in an object, gets the method named + name from that object, and calls it with the specified arguments + and keywords. + + INPUT: + + - ``name`` - a string of the name of the method you + want to call + + - ``args, kwds`` - arguments and keywords to be passed + to the method + + EXAMPLES:: + + sage: f = attrcall('core', 3); f + *.core(3) + sage: [f(p) for p in Partitions(5)] + [[2], [1, 1], [1, 1], [3, 1, 1], [2], [2], [1, 1]] + """ + return AttrCallObject(name, args, kwds) + + +def call_method(obj, name, *args, **kwds): + """ + Call the method ``name`` on ``obj``. + + This has to exist somewhere in Python!!! + + .. SEEALSO:: :func:`operator.methodcaller` :func:`attrcal` + + EXAMPLES:: + + sage: from sage.misc.call import call_method + sage: call_method(1, "__add__", 2) + 3 + """ + return getattr(obj, name)(*args, **kwds) + +from sage.misc.persist import register_unpickle_override +register_unpickle_override("sage.misc.misc", "call_method", call_method) diff --git a/src/sage/misc/classcall_metaclass.pyx b/src/sage/misc/classcall_metaclass.pyx index 47e71349345..eb8f75a5094 100644 --- a/src/sage/misc/classcall_metaclass.pyx +++ b/src/sage/misc/classcall_metaclass.pyx @@ -87,10 +87,8 @@ cdef class ClasscallMetaclass(NestedClassMetaclass): If a class is put in this metaclass it automatically becomes a new-style class:: - sage: from six import add_metaclass sage: from sage.misc.classcall_metaclass import ClasscallMetaclass - sage: @add_metaclass(ClasscallMetaclass) - ....: class Foo: pass + sage: class Foo(metaclass=ClasscallMetaclass): pass sage: x = Foo(); x <__main__.Foo object at 0x...> sage: issubclass(Foo, object) @@ -103,10 +101,8 @@ cdef class ClasscallMetaclass(NestedClassMetaclass): r""" TESTS:: - sage: from six import add_metaclass sage: from sage.misc.classcall_metaclass import ClasscallMetaclass - sage: @add_metaclass(ClasscallMetaclass) - ....: class FOO(object): pass + sage: class FOO(metaclass=ClasscallMetaclass): pass sage: isinstance(FOO, ClasscallMetaclass) # indirect doctest True """ @@ -126,10 +122,8 @@ cdef class ClasscallMetaclass(NestedClassMetaclass): EXAMPLES:: - sage: from six import add_metaclass sage: from sage.misc.classcall_metaclass import ClasscallMetaclass - sage: @add_metaclass(ClasscallMetaclass) - ....: class FOO(object): pass + sage: class FOO(metaclass=ClasscallMetaclass): pass sage: FOO() <__main__.FOO object at ...> @@ -194,10 +188,8 @@ cdef class ClasscallMetaclass(NestedClassMetaclass): EXAMPLES:: - sage: from six import add_metaclass sage: from sage.misc.classcall_metaclass import ClasscallMetaclass - sage: @add_metaclass(ClasscallMetaclass) - ....: class Foo(object): + sage: class Foo(metaclass=ClasscallMetaclass): ....: @staticmethod ....: def __classcall__(cls): ....: print("calling classcall") @@ -224,8 +216,7 @@ cdef class ClasscallMetaclass(NestedClassMetaclass): We now show the usage of ``__classcall_private__``:: - sage: @add_metaclass(ClasscallMetaclass) - ....: class FooNoInherits(object): + sage: class FooNoInherits(object, metaclass=ClasscallMetaclass): ....: __metaclass__ = ClasscallMetaclass ....: @staticmethod ....: def __classcall_private__(cls): @@ -244,8 +235,7 @@ cdef class ClasscallMetaclass(NestedClassMetaclass): We now show the usage of both:: - sage: @add_metaclass(ClasscallMetaclass) - ....: class Foo2(object): + sage: class Foo2(object, metaclass=ClasscallMetaclass): ....: @staticmethod ....: def __classcall_private__(cls): ....: print("calling private classcall") @@ -312,16 +302,14 @@ cdef class ClasscallMetaclass(NestedClassMetaclass): We check for memory leaks:: - sage: @add_metaclass(ClasscallMetaclass) - ....: class NOCALL(object): + sage: class NOCALL(object, metaclass=ClasscallMetaclass): ....: pass sage: sys.getrefcount(NOCALL()) 1 We check that exceptions are correctly handled:: - sage: @add_metaclass(ClasscallMetaclass) - ....: class Exc(object): + sage: class Exc(object, metaclass=ClasscallMetaclass): ....: @staticmethod ....: def __classcall__(cls): ....: raise ValueError("Calling classcall") @@ -367,13 +355,10 @@ cdef class ClasscallMetaclass(NestedClassMetaclass): ``obj.Inner(...)`` is equivalent to ``Outer.Inner(obj, ...)``:: sage: import functools - sage: from six import add_metaclass sage: from sage.misc.nested_class import NestedClassMetaclass sage: from sage.misc.classcall_metaclass import ClasscallMetaclass - sage: @add_metaclass(NestedClassMetaclass) - ....: class Outer: - ....: @add_metaclass(ClasscallMetaclass) - ....: class Inner(object): + sage: class Outer(metaclass=NestedClassMetaclass): + ....: class Inner(metaclass=ClasscallMetaclass): ....: @staticmethod ....: def __classget__(cls, instance, owner): ....: print("calling __classget__(%s, %s, %s)" % ( @@ -439,10 +424,8 @@ cdef class ClasscallMetaclass(NestedClassMetaclass): We construct a class which implements membership testing, and which contains ``1`` and no other x:: - sage: from six import add_metaclass sage: from sage.misc.classcall_metaclass import ClasscallMetaclass - sage: @add_metaclass(ClasscallMetaclass) - ....: class Foo(object): + sage: class Foo(metaclass=ClasscallMetaclass): ....: @staticmethod ....: def __classcontains__(cls, x): ....: return x == 1 @@ -454,10 +437,8 @@ cdef class ClasscallMetaclass(NestedClassMetaclass): We now check that for a class without ``__classcontains__`` method, we emulate the usual error message:: - sage: from six import add_metaclass sage: from sage.misc.classcall_metaclass import ClasscallMetaclass - sage: @add_metaclass(ClasscallMetaclass) - ....: class Bar(object): pass + sage: class Bar(metaclass=ClasscallMetaclass): pass sage: 1 in Bar Traceback (most recent call last): ... @@ -557,7 +538,6 @@ def timeCall(T, int n, *args): EXAMPLES:: - sage: from six import add_metaclass sage: from sage.misc.classcall_metaclass import ( ....: ClasscallMetaclass, CRef, C2, C3, C2C, timeCall) sage: timeCall(object, 1000) @@ -576,8 +556,7 @@ def timeCall(T, int n, *args): overhead in using :class:`ClasscallMetaclass` if there is no classcall defined:: - sage: @add_metaclass(ClasscallMetaclass) - ....: class P(object): + sage: class P(metaclass=ClasscallMetaclass): ....: def __init__(self, i): ....: self.i = i+i1 @@ -596,8 +575,7 @@ def timeCall(T, int n, *args): Let's now compare when there is a classcall defined:: - sage: @add_metaclass(ClasscallMetaclass) - ....: class PC(object): + sage: class PC(object, metaclass=ClasscallMetaclass): ....: @staticmethod ....: def __classcall__(cls, i): ....: return i+i1 diff --git a/src/sage/misc/converting_dict.py b/src/sage/misc/converting_dict.py index f9bdb17bba3..c3433ade6ce 100644 --- a/src/sage/misc/converting_dict.py +++ b/src/sage/misc/converting_dict.py @@ -45,7 +45,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import -from six import iteritems import collections @@ -294,5 +293,5 @@ def update(self, *args, **kwds): seq = ((f(k), v) for k, v in arg) u(seq) if kwds: - seq = ((f(k), v) for k, v in iteritems(kwds)) + seq = ((f(k), v) for k, v in kwds.items()) u(seq) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 0bb586e5fc9..0600603e62d 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -18,9 +18,8 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import builtins -from six import iteritems, PY2 +import builtins import os import sys import shutil @@ -42,7 +41,7 @@ cblas_include_dirs = list(cblas_pc['include_dirs']) standard_libs = [ - 'mpfr', 'gmp', 'gmpxx', 'stdc++', 'pari', 'm', + 'mpfr', 'gmp', 'gmpxx', 'pari', 'm', 'ec', 'gsl', ] + cblas_libs + [ 'ntl'] @@ -522,7 +521,7 @@ def cython_import_all(filename, globals, **kwds): code """ m = cython_import(filename, **kwds) - for k, x in iteritems(m.__dict__): + for k, x in m.__dict__.items(): if k[0] != '_': globals[k] = x @@ -659,9 +658,6 @@ def _strhash(s): l = len(s) for c in s: - if PY2: - c = ord(c) - h += c + (c << 17) h ^= h >> 2 diff --git a/src/sage/misc/decorators.py b/src/sage/misc/decorators.py index 4c8e6f6b575..0136c4281f4 100644 --- a/src/sage/misc/decorators.py +++ b/src/sage/misc/decorators.py @@ -29,7 +29,6 @@ from functools import (partial, update_wrapper, WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES) -from six import iteritems from copy import copy from sage.misc.sageinspect import (sage_getsource, sage_getsourcelines, @@ -402,7 +401,7 @@ def wrapper(*args, **kwds): # Collect all the relevant keywords in kwds # and put them in suboptions - for key, value in list(iteritems(kwds)): + for key, value in list(kwds.items()): if key.startswith(self.name): suboptions[key[len(self.name):]] = value del kwds[key] diff --git a/src/sage/misc/defaults.py b/src/sage/misc/defaults.py index 44e455a25ae..2aa3ad84013 100644 --- a/src/sage/misc/defaults.py +++ b/src/sage/misc/defaults.py @@ -25,19 +25,65 @@ def variable_names(n, name=None): + r""" + Converts a root string into a tuple of variable names by adding + numbers in sequence. + + INPUT: + + - ``n`` a non-negative Integer; the number of variable names to + output + - ``names`` a string (default: ``None``); the root of the variable + name. + + EXAMPLES:: + + sage: from sage.misc.defaults import variable_names + sage: variable_names(0) + () + sage: variable_names(1) + ('x',) + sage: variable_names(1,'alpha') + ('alpha',) + sage: variable_names(2,'alpha') + ('alpha0', 'alpha1') + """ if name is None: name = var_name n = int(n) if n == 1: - return [name] + return (name,) return tuple(['%s%s'%(name,i) for i in range(n)]) def latex_variable_names(n, name=None): + r""" + Converts a root string into a tuple of variable names by adding + numbers in sequence. + + INPUT: + + - ``n`` a non-negative Integer; the number of variable names to + output + - ``names`` a string (default: ``None``); the root of the variable + name. + + EXAMPLES:: + + sage: from sage.misc.defaults import latex_variable_names + sage: latex_variable_names(0) + () + sage: latex_variable_names(1,'a') + ('a',) + sage: latex_variable_names(3,beta) + ('beta_{0}', 'beta_{1}', 'beta_{2}') + sage: latex_variable_names(3,r'\beta') + ('\\beta_{0}', '\\beta_{1}', '\\beta_{2}') + """ if name is None: name = var_name n = int(n) if n == 1: - return [name] + return (name,) v = tuple(['%s_{%s}'%(name,i) for i in range(n)]) return v diff --git a/src/sage/misc/dev_tools.py b/src/sage/misc/dev_tools.py index 296cd206db8..3b991f48efb 100644 --- a/src/sage/misc/dev_tools.py +++ b/src/sage/misc/dev_tools.py @@ -21,8 +21,6 @@ from collections import defaultdict -from six import iteritems, string_types - def runsnake(command): """ @@ -162,8 +160,8 @@ def load_submodules(module=None, exclude_pattern=None): The second argument allows to exclude a pattern:: sage: sage.misc.dev_tools.load_submodules(sage.geometry, "database$|lattice") + load sage.geometry.cone_catalog... succeeded load sage.geometry.fan_isomorphism... succeeded - load sage.geometry.hyperplane_arrangement.affine_subspace... succeeded ... load sage.geometry.riemannian_manifolds.surface3d_generators... succeeded @@ -249,7 +247,7 @@ def find_objects_from_name(name, module_name=None): """ obj = [] - for smodule_name, smodule in iteritems(sys.modules): + for smodule_name, smodule in sys.modules.items(): if module_name and not smodule_name.startswith(module_name): continue if hasattr(smodule, '__dict__') and name in smodule.__dict__: @@ -304,7 +302,7 @@ def find_object_modules(obj): # otherwise, we parse all (already loaded) modules and hope to find # something module_to_obj = {} - for module_name, module in iteritems(sys.modules): + for module_name, module in sys.modules.items(): if module_name != '__main__' and hasattr(module, '__dict__'): d = module.__dict__ names = [key for key in d if d[key] is obj] @@ -315,7 +313,7 @@ def find_object_modules(obj): if sageinspect.isclassinstance(obj): dec_pattern = re.compile(r"^(\w[\w0-9\_]*)\s*=", re.MULTILINE) module_to_obj2 = {} - for module_name, obj_names in iteritems(module_to_obj): + for module_name, obj_names in module_to_obj.items(): module_to_obj2[module_name] = [] src = sageinspect.sage_getsource(sys.modules[module_name]) m = dec_pattern.search(src) @@ -522,7 +520,7 @@ def import_statements(*objects, **kwds): name = None # the name of the object # 1. if obj is a string, we look for an object that has that name - if isinstance(obj, string_types): + if isinstance(obj, str): from sage.all import sage_globals G = sage_globals() name = obj diff --git a/src/sage/misc/displayhook.py b/src/sage/misc/displayhook.py deleted file mode 100644 index 88edfe4645d..00000000000 --- a/src/sage/misc/displayhook.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- encoding: utf-8 -*- -""" -Entrypoint for the SageNB Display System - -Just for compatibility with the notebook, you should not use this any -more. Look into ``sage/repl/`` instead. -""" - -#***************************************************************************** -# Copyright (C) 2015 Volker Braun -# -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from sage.repl.rich_output import get_display_manager -from sage.repl.rich_output.backend_sagenb import BackendSageNB - - -def DisplayHook(): - """ - This function is called by SageNB to set up its displayhook. - - OUTPUT: - - The new displayhook that will be used by SageNB - - EXAMPLES:: - - sage: from sage.misc.displayhook import DisplayHook - sage: d = DisplayHook() - sage: d - - - sage: d(set([1, 2, 3])) # Sage commandline output - {1, 2, 3} - - sage: from sage.repl.rich_output import get_display_manager - sage: get_display_manager() - The Sage display manager using the SageNB backend - """ - display_manager = get_display_manager() - backend = BackendSageNB() - display_manager.switch_backend(backend) - return display_manager.displayhook - diff --git a/src/sage/misc/dist.py b/src/sage/misc/dist.py index e21dd881e59..47a1b0489f1 100644 --- a/src/sage/misc/dist.py +++ b/src/sage/misc/dist.py @@ -22,10 +22,8 @@ def install_scripts(directory=None, ignore_existing=False): - 'R' runs R - 'singular' runs Singular - 'sqlite3' runs SQLite version 3 - - 'kash' runs Kash if it is installed (Kash is an optional Sage - package) - - 'M2' runs Macaulay2 if it is installed (Macaulay2 is an - experimental Sage package) + - 'kash' runs Kash if it is installed + - 'M2' runs Macaulay2 if it is installed This command: diff --git a/src/sage/misc/explain_pickle.py b/src/sage/misc/explain_pickle.py index 6470f428b35..d4f64f1cf68 100644 --- a/src/sage/misc/explain_pickle.py +++ b/src/sage/misc/explain_pickle.py @@ -166,8 +166,6 @@ from pickletools import genops -from six import iteritems - import sage.all from sage.misc.sage_input import SageInputBuilder, SageInputExpression from sage.misc.sage_eval import sage_eval @@ -1210,7 +1208,7 @@ def EXT1(self, n): r""" TESTS:: - sage: from six.moves.copyreg import * + sage: from copyreg import * sage: from sage.misc.explain_pickle import * sage: add_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 42) sage: test_pickle(EmptyNewstyleClass()) # py2 @@ -1237,7 +1235,7 @@ def EXT2(self, n): r""" TESTS:: - sage: from six.moves.copyreg import * + sage: from copyreg import * sage: from sage.misc.explain_pickle import * sage: add_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 31415) sage: test_pickle(EmptyNewstyleClass()) # py2 @@ -1264,7 +1262,7 @@ def EXT4(self, n): r""" TESTS:: - sage: from six.moves.copyreg import * + sage: from copyreg import * sage: from sage.misc.explain_pickle import * sage: add_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 27182818) sage: test_pickle(EmptyNewstyleClass()) # py2 @@ -2466,12 +2464,12 @@ def unpickle_build(obj, state): if state is not None: assert(isinstance(state, dict)) d = obj.__dict__ - for k, v in iteritems(state): + for k, v in state.items(): d[k] = v if slots is not None: assert(isinstance(slots, dict)) - for k, v in iteritems(slots): + for k, v in slots.items(): setattr(obj, k, v) @@ -2518,13 +2516,13 @@ def unpickle_extension(code): EXAMPLES:: - sage: from six.moves.copyreg import * + sage: from copyreg import * sage: add_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 42) sage: unpickle_extension(42) sage: remove_extension('sage.misc.explain_pickle', 'EmptyNewstyleClass', 42) """ - from six.moves.copyreg import _inverted_registry, _extension_cache + from copyreg import _inverted_registry, _extension_cache # copied from .get_extension() in pickle.py nil = [] obj = _extension_cache.get(code, nil) @@ -2835,7 +2833,8 @@ def append(self): ... TypeError: append() takes 1 positional argument but 2 were given - We can still append by directly using the list method: + We can still append by directly using the list method:: + sage: list.append(v, 7) sage: v [7] @@ -2859,7 +2858,8 @@ def extend(self): ... TypeError: extend() takes 1 positional argument but 2 were given - We can still extend by directly using the list method: + We can still extend by directly using the list method:: + sage: list.extend(v, (3,1,4,1,5,9)) sage: v [3, 1, 4, 1, 5, 9] diff --git a/src/sage/misc/fpickle.pyx b/src/sage/misc/fpickle.pyx index 93f18854397..0305532e902 100644 --- a/src/sage/misc/fpickle.pyx +++ b/src/sage/misc/fpickle.pyx @@ -6,10 +6,9 @@ Function pickling REFERENCE: The python cookbook. """ +import copyreg +import pickle import types -import six -from six.moves import copyreg -from six.moves import cPickle def code_ctor(*args): @@ -38,12 +37,8 @@ def reduce_code(co): if co.co_freevars or co.co_cellvars: raise ValueError("Cannot pickle code objects from closures") - if six.PY2: - co_args = (co.co_argcount,) - else: - co_args = (co.co_argcount, co.co_kwonlyargcount) - - co_args += (co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code, + co_args = (co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, + co.co_stacksize, co.co_flags, co.co_code, co.co_consts, co.co_names, co.co_varnames, co.co_filename, co.co_name, co.co_firstlineno, co.co_lnotab) @@ -79,7 +74,7 @@ def pickle_function(func): sage: h(10) 11 """ - return cPickle.dumps(func.__code__) + return pickle.dumps(func.__code__) def unpickle_function(pickled): """ @@ -92,7 +87,7 @@ def unpickle_function(pickled): sage: unpickle_function(pickle_function(f))(3,5) 15 """ - recovered = cPickle.loads(pickled) + recovered = pickle.loads(pickled) return types.FunctionType(recovered, globals()) diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index f27976bdd0d..8eb93e7c743 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -21,8 +21,7 @@ # https://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import -from six.moves import range, builtins -from six import integer_types +import builtins from sage.rings.complex_double import CDF from sage.rings.real_double import RDF, RealDoubleElement @@ -128,8 +127,8 @@ def category(x): try: return x.category() except AttributeError: - import sage.categories.all - return sage.categories.all.Objects() + from sage.categories.objects import Objects + return Objects() def characteristic_polynomial(x, var='x'): @@ -247,7 +246,7 @@ def denominator(x): sage: denominator(r) x - 1 """ - if isinstance(x, integer_types): + if isinstance(x, int): return 1 return x.denominator() @@ -1213,7 +1212,7 @@ def numerator(x): sage: numerator(17/11111) 17 """ - if isinstance(x, integer_types): + if isinstance(x, int): return x return x.numerator() @@ -1548,10 +1547,9 @@ def round(x, ndigits=0): .. NOTE:: - This is currently slower than the builtin round function, since - it does more work - i.e., allocating an RDF element and - initializing it. To access the builtin version do - ``from six.moves import builtins; builtins.round``. + This is currently slower than the builtin round function, since it does + more work - i.e., allocating an RDF element and initializing it. To + access the builtin version do ``import builtins; builtins.round``. """ try: if ndigits: diff --git a/src/sage/misc/gperftools.py b/src/sage/misc/gperftools.py index bd802daee72..55ee572b8c4 100644 --- a/src/sage/misc/gperftools.py +++ b/src/sage/misc/gperftools.py @@ -207,12 +207,11 @@ def _pprof(self): EXAMPLES:: - sage: import six sage: from sage.misc.gperftools import Profiler sage: prof = Profiler() sage: try: ....: pp = prof._pprof() - ....: assert isinstance(pp, six.string_types) + ....: assert isinstance(pp, str) ....: except OSError: ....: pass # not installed """ diff --git a/src/sage/misc/inline_fortran.py b/src/sage/misc/inline_fortran.py index 6b8b5fdd72d..1625feeade4 100644 --- a/src/sage/misc/inline_fortran.py +++ b/src/sage/misc/inline_fortran.py @@ -3,15 +3,12 @@ """ from __future__ import absolute_import +import importlib import os import shutil import subprocess import sys -import six - -from six import iteritems - from sage.misc.temporary_file import tmp_dir @@ -50,36 +47,19 @@ def _import_module_from_path(name, path=None): return _import_module_from_path_impl(name, path) -if six.PY2: - import imp - - def _import_module_from_path_impl(name, path): - """Implement ``_import_module_from_path for Python 2.""" - - # Note: Raises an ImportError if not found - fileobj, pathname, description = imp.find_module(name, path) - try: - # Executes the module in fileobj using the appropriate loader and - # returns the module - return imp.load_module(name, fileobj, pathname, description) - finally: - fileobj.close() -else: - import importlib - - def _import_module_from_path_impl(name, path): - """Implement ``_import_module_from_path for Python 3.4+.""" +def _import_module_from_path_impl(name, path): + """Implement ``_import_module_from_path for Python 3.4+.""" - # This is remarkably tricky to do right, considering that the new - # importlib is supposed to make direct interaction with the import - # system easier. I blame the ModuleSpec stuff... - finder = importlib.machinery.PathFinder() - spec = finder.find_spec(name, path=path) - if spec is None: - raise ImportError('No module named {}'.format(name)) - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) - return mod + # This is remarkably tricky to do right, considering that the new + # importlib is supposed to make direct interaction with the import + # system easier. I blame the ModuleSpec stuff... + finder = importlib.machinery.PathFinder() + spec = finder.find_spec(name, path=path) + if spec is None: + raise ImportError('No module named {}'.format(name)) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod class InlineFortran: @@ -146,7 +126,7 @@ def eval(self, x, globals=None, locals=None): Traceback (most recent call last): ... RuntimeError: failed to compile Fortran code:... - sage: os.getcwd() == os.path.normpath(DOT_SAGE) + sage: os.getcwd() == os.path.realpath(DOT_SAGE) True """ if globals is None: @@ -221,7 +201,7 @@ def eval(self, x, globals=None, locals=None): # This can fail for example over NFS pass - for k, x in iteritems(mod.__dict__): + for k, x in mod.__dict__.items(): if k[0] != '_': globals[k] = x diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index 93028461347..b4ee01c09a7 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -10,15 +10,14 @@ - William Stein: original implementation - Joel B. Mohler: latex_variable_name() drastic rewrite and many doc-tests """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** from __future__ import print_function, absolute_import import os @@ -27,8 +26,6 @@ import shutil import subprocess -from six import integer_types - from sage.misc import sage_eval from sage.misc.cachefunc import cached_function, cached_method from sage.misc.sage_ostools import have_program @@ -401,18 +398,18 @@ def float_function(x): return latex(RDF(x)) -latex_table = {type(None): None_function, - bool: bool_function, - dict: dict_function, - float: float_function, - list: list_function, - str: str_function, - tuple: tuple_function, - type(NotImplemented): builtin_constant_function, - type(Ellipsis): builtin_constant_function} - -for t in integer_types: - latex_table[t] = str +latex_table = { + type(None): None_function, + bool: bool_function, + dict: dict_function, + float: float_function, + int: str, + list: list_function, + str: str_function, + tuple: tuple_function, + type(NotImplemented): builtin_constant_function, + type(Ellipsis): builtin_constant_function +} class LatexExpr(str): @@ -1896,7 +1893,7 @@ def __call__(self, x, combine_all=False): OUTPUT: - A :calss:`MathJaxExpr` + A :class:`MathJaxExpr` EXAMPLES:: @@ -2221,8 +2218,8 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False, print(MathJax().eval(objects, mode=mode, combine_all=combine_all)) else: base_dir = os.path.abspath("") - from sage.misc.temporary_file import graphics_filename - png_file = graphics_filename() + from sage.misc.temporary_file import tmp_filename + png_file = tmp_filename(ext='.png') png_link = "cell://" + png_file png(objects, os.path.join(base_dir, png_file), debug=debug, engine=engine) @@ -2342,7 +2339,7 @@ def coeff_repr(c): return c._latex_coeff_repr() except AttributeError: pass - if isinstance(c, integer_types + (float,)): + if isinstance(c, (int, float)): return str(c) s = latex(c) if s.find("+") != -1 or s.find("-") != -1: diff --git a/src/sage/misc/lazy_attribute.pyx b/src/sage/misc/lazy_attribute.pyx index f9191797377..b72cfb49d67 100644 --- a/src/sage/misc/lazy_attribute.pyx +++ b/src/sage/misc/lazy_attribute.pyx @@ -363,7 +363,7 @@ class lazy_attribute(_lazy_attribute): sage: a.x = 4 Traceback (most recent call last): ... - AttributeError: can't set attribute + AttributeError: can...t set attribute sage: a.__dict__ {} sage: a.x diff --git a/src/sage/misc/lazy_import.pyx b/src/sage/misc/lazy_import.pyx index 0fb97f04858..f759d953d98 100644 --- a/src/sage/misc/lazy_import.pyx +++ b/src/sage/misc/lazy_import.pyx @@ -63,7 +63,7 @@ cdef extern from *: int likely(int) nogil # Defined by Cython import os -from six.moves import cPickle as pickle +import pickle import inspect from . import sageinspect @@ -380,9 +380,8 @@ cdef class LazyImport(object): """ TESTS:: - sage: import six sage: lazy_import('sage.all', 'ZZ'); lazy_ZZ = ZZ - sage: six.text_type(lazy_ZZ) + sage: str(lazy_ZZ) u'Integer Ring' """ return unicode(self.get_object()) @@ -641,19 +640,6 @@ cdef class LazyImport(object): """ return obj(left) @ obj(right) - def __div__(left, right): - """ - TESTS:: - - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') - sage: type(foo) - - sage: foo / 2 - 5 - """ - return obj(left) / obj(right) - def __floordiv__(left, right): """ TESTS:: @@ -849,19 +835,6 @@ cdef class LazyImport(object): """ return int(self.get_object()) - def __long__(self): - """ - TESTS:: - - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') - sage: type(foo) - - sage: long(foo) - 10L - """ - return long(self.get_object()) - def __float__(self): """ TESTS:: diff --git a/src/sage/misc/lazy_list.pyx b/src/sage/misc/lazy_list.pyx index 539c2bd7135..b1698ddd4bc 100644 --- a/src/sage/misc/lazy_list.pyx +++ b/src/sage/misc/lazy_list.pyx @@ -763,7 +763,6 @@ cdef class lazy_list_generic(object): We check commutation:: - sage: from six.moves import range sage: l = lazy_list(iter(range(10000))) sage: l1 = l[::2][:3001] sage: l2 = l[:6002][::2] diff --git a/src/sage/misc/log.py b/src/sage/misc/log.py deleted file mode 100644 index 0b396875146..00000000000 --- a/src/sage/misc/log.py +++ /dev/null @@ -1,468 +0,0 @@ -r""" -Logging of Sage sessions - -.. TODO:: - - Pressing "control-D" can mess up the I/O sequence because of - a known bug. - -You can create a log of your Sage session as a web page and/or as a -latex document. Just type ``log_html()`` to create an HTML log, or -``log_dvi()`` to create a dvi (LaTeX) log. Your complete session so -far up until when you type the above command will be logged, along -with any future input. Thus you can view the log system as a way to -print or view your entire session so far, along with a way to see -nicely typeset incremental updates as you work. - -If ``L=log_dvi()`` or ``L=log_html()`` is a logger, you can type -``L.stop()`` and ``L.start()`` to stop and start logging. - -The environment variables ``BROWSER`` and ``DVI_VIEWER`` determine -which web browser or dvi viewer is used to display your running log. - -For both log systems you must have a TeX system installed on your -computer. For HTML logging, you must have the convert command, which -comes with the free ImageMagick tools. - -.. note:: - - The HTML output is done via LaTeX and PNG images right now, - sort of like how latex2html works. Obviously it would be - interesting to do something using MathML in the long run. - -AUTHORS: - -- William Stein (2006-02): initial version - -- William Stein (2006-02-27): changed html generation so log directory - is relocatable (no hardcoded paths). - -- William Stein (2006-03-04): changed environment variable to BROWSER. - -- Didier Deshommes (2006-05-06): added MathML support; refactored - code. - -- Dan Drake (2008-03-27): fix bit rotting so - that optional directories work, dvi logging works, viewer() command - works, remove no-longer-working MathML logger; fix off-by-one - problems with IPython history; add text logger; improve - documentation about viewers. -""" - -# Note: there is a web browser module standard with Python. -# But it seems so dated as to be useless. - -#***************************************************************************** -# Copyright (C) 2006 William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# http://www.gnu.org/licenses/ -#***************************************************************************** -from __future__ import print_function -from __future__ import absolute_import - -import os -import time - -import sage.repl.interpreter as interpreter -from . import latex -from . import misc - -from sage.misc.viewer import browser, dvi_viewer - -offset = 0 -loggers = [] - -def update(): - for X in loggers: - X._update() - -REFRESH = '' - -class Log: - """ - This is the base logger class. The two classes that you actually - instantiate are derived from this one. - """ - def __init__(self, dir=None, debug=False, viewer=None): - from sage.misc.misc import sage_makedirs - if dir is None: - dir = misc.DOT_SAGE + 'log' - self._time = time.strftime('%Y-%m-%d-%H%M%S') - dir = os.path.join(os.path.abspath(dir), 'log-' + self._time) - sage_makedirs(dir) - self._debug = debug - self._n = 0 - self._dir = dir - self._filename = os.path.join(dir, self._filename()) - self._output = __IPYTHON__.output_hist - self._input = __IPYTHON__.input_hist_raw - self._text = '' - self._after_output = False - self._init() - self._input_text = '' - self._stopped = False - self._viewer = viewer - loggers.append(self) - print('Now logging to ' + self._filename) - self._update() - self.view() - - def __repr__(self): - return "Logger" - - def _latex_(self): - return "\\mathrm{%s}"%self - - def dir(self): - """ - Return the directory that contains the log files. - """ - return self._dir - - def stop(self): - """ - Stop the logger. To restart use the start function. - """ - self._stopped = True - - def start(self): - """ - Start the logger. To stop use the stop function. - """ - self._stopped = False - - def _update(self): - """ - There is an off-by-one issue with IPython's input and output - history; ``__IPYTHON__.input_hist_raw`` is a *list* containing - the un-preparsed Sage commands. However, - ``__IPYTHON__.output_hist`` is a dictionary whose keys are - integers and whose values are outputs. This is good because - not every input has an output. - - **BUT**, the output from:: - - __IPYTHON__.input_hist_raw[n] - - is stored in:: - - __IPYTHON__.output_hist[n+1] ! - - This is annoying and it may be a bug. Right now the loggers - correct for this, but if modifying or extending this code, - consider yourself warned. - """ - if self._stopped: - return - # see note at end of this function for info about output and - # input - (O, I) = (self._output, self._input) - K = O.keys() - while self._n < max(len(I), max(K + [-1])): - n = self._n - followed_by_output = (n+1 in K) - if n < len(I): - L = I[n] - else: - L = "Sorry; raw input log out of sync" - Lstrip = L.strip() - if len(Lstrip) > 0 and Lstrip[0] != '%': - self._write(self._get_input(n, followed_by_output)) - self._input_text += L - #m = n + offset - m = n - if m+1 in K: - self._write(self._get_output(m+1)) - # what does the following line do? Nothing is done with - # this s. Commenting out for now. - #s = '# ' + '\n# '.join(str(O[m]).split('\n')) + '\n\n' - self._n += 1 - with open(self._filename,'w') as A: - A.write(self._header() + '\n' + self._text + '\n' + self._footer()) - self._update_plain() - self._build() - - def _write(self, lines): - self._text += lines - - def _plain_text(self): - return self._input_text - - def _input_log_name(self): - return os.path.join(self._dir, 'input-' + self._time) - - def _update_plain(self): - with open(self._input_log_name(), 'w') as f: - f.write(self._input_text) - - -class log_html(Log): - r""" - Create a running log of your Sage session as a web page. - - Easy usage: ``log_html()`` - - TODO: Pressing "control-D" can mess up the I/O sequence because of - a known bug. - - Use ``L=log_html([optional directory])`` to create an HTML - log. Your complete session so far up until when you type the above - command will be logged, along with any future input. Thus you can - view the log system as a way to print or view your entire session - so far, along with a way to see nicely typeset incremental updates - as you work. - - If L is a logger, you can type ``L.stop()`` and - ``L.start()`` to stop and start logging. - - The environment variable ``WEB_BROWSER`` determines which web - browser or dvi viewer is used to display your running log. You can - also specify a viewer when you start the logger with something - like ``log_html([opt. dir], viewer='firefox')``. - - You must have a TeX system installed on your computer, and you - must have the convert command, which comes with the free - ImageMagick tools. - """ - - def _init(self): - self._images = os.path.join(self._dir, 'images') - if not os.path.exists(self._images): - os.makedirs(self._images) - - - def view(self): - if not self._viewer is None: - viewer = self._viewer - else: - viewer = browser() - os.system('%s "%s"&'%(viewer, self._filename)) - - def _build(self): - return - - def __repr__(self): - return "HTML Logger" - - def _get_input(self, n, followed_by_output): - if n >= len(self._input): - return - return """ %s %s: %s"""%( - n, interpreter._prompt, self._input[n]) - - def _get_output(self, n): - x = self._output[n] - try: - L = latex.latex(x) - except Exception: - L = "\\mbox{error TeXing object}" - single_png = os.path.join(self._images, '%s.png' % n) - try: - x.png(single_png) - except AttributeError: - latex.png(x, single_png, debug=self._debug) - oi = os.path.join(self._dir, 'images', 'o' + '%s.html' % n) - with open(oi, 'w') as file: - file.write('
    OUTPUT:\n%s\n\n\nLATEX:\n%s
    '%( - x, L, single_png)) - extra_img_opts = '' - #if sage.plot.graphics.is_Graphics(x): - # extra_img_opts = 'width=300' - return """
    -
    - - %s - -
    \n
    \n"""%('images/o%s.html'%n, - 'images/%s.png'%n, L, - extra_img_opts) - - def _filename(self): - return 'index.html' - - def _header(self): - T = self._title() - inlog = os.path.split(self._input_log_name())[1] - return '\n%s\n%s\n

    %s

    \n

    %s

    '%(REFRESH,
    -            T,T, inlog, inlog)
    -
    -    def _footer(self):
    -        return "
    \n\n" - - def _title(self): - return 'Sage Log %s'%self._time - - -class log_dvi(Log): - """ - Create a running log of your Sage session as a nicely typeset dvi - file. - - Easy usage: ``log_dvi()`` - - TODO: Pressing "control-D" can mess up the I/O sequence because of - a known bug. - - Use ``L=log_dvi([optional directory])`` to create a dvi log. Your - complete session so far up until when you type the above command - will be logged, along with any future input. Thus you can view the - log system as a way to print or view your entire session so far, - along with a way to see nicely typeset incremental updates as you - work. - - If L is a logger, you can type ``L.stop()`` and ``L.start()`` to - stop and start logging. - - The environment variable ``DVI_VIEWER`` determines which web - browser or dvi viewer is used to display your running log. You can - also specify a viewer when you start the logger with something - like ``log_dvi([opt. dir], viewer='xdvi')``. - - You must have a LaTeX system installed on your computer and a dvi - viewer. - """ - def _init(self): - self._in_verbatim = False - - def __repr__(self): - return "DVI Logger" - - def _build(self): - cmd = 'cd %s; latex \\\\nonstopmode \\\\input %s '%( - self._dir, self._filename) - cmd += " 2>/dev/null 1>/dev/null" - dvifile = '%s.dvi'%os.path.splitext(self._filename)[0] - if os.path.exists(dvifile): - cmd += ' & ' - os.system(cmd) - - def view(self): - if not self._viewer is None: - viewer = self._viewer - else: - viewer = dvi_viewer() - self._build() - F = os.path.splitext(self._filename)[0] + '.dvi' - cmd = 'cd %s; %s %s '%( - self._dir, viewer, F) - os.system(cmd + " 2>/dev/null 1>/dev/null &") - - def _get_input(self, n, followed_by_output): - if n >= len(self._input): - return - s = '' - if not self._in_verbatim: - s += '\\begin{verbatim}' - self._in_verbatim = True - I = self._input[n] - #print('input: %s' % I) - s += "%s %s: %s"%(n, interpreter._prompt, I) - s += '\\end{verbatim}' - if followed_by_output: - self._in_verbatim = False - else: - s += '\\begin{verbatim}' - self._after_output = False - return s - - def _get_output(self, n): - s = '' - self._after_output = True - if self._in_verbatim: - s += '\\end{verbatim}\n' - self._in_verbatim = False - - L = latex.latex(self._output[n]) - # If we explicitly ask for LaTeX output, - # no need to format L - if "latex" in self._input[n-1]: - s+= "\\begin{verbatim}%s\\end{verbatim}" %L - else: - s += '\n\\begin{center}$\\displaystyle %s $\\end{center}\n'%L - return s - - def _filename(self): - return 'sagelog.tex' - - def _header(self): - return """ -\\documentclass{article} -\\title{%s}\\author{} -\\begin{document} -\\maketitle -"""%self._title() - - def _footer(self): - if self._in_verbatim: - return r"\end{verbatim}\end{document}" - else: - return r"\end{document}" - - - def _title(self): - return '\\SAGE Log %s'%self._time - - -class log_text(Log): - """ - Create a running log of your Sage session as a plain text file. - - Easy usage: ``log_text()`` - - TODO: Pressing "control-D" can mess up the I/O sequence because of - a known bug. - - Use ``L=log_text([optional directory])`` to create a text - log. Your complete session so far up until when you type the above - command will be logged, along with any future input. Thus you can - view the log system as a way to print or view your entire session - so far, along with a way to see incremental updates as you work. - - Unlike the html and dvi loggers, this one does not automatically - start a viewer unless you specify one; you can do that when you - start the logger with something like - ``log_text([opt. dir], viewer='xterm -e tail -f')``. - - If L is a logger, you can type ``L.stop()`` and - ``L.start()`` to stop and start logging. - """ - def _init(self): - return - - def __repr__(self): - return "Text Logger" - - def _build(self): - return - - def view(self): - if not self._viewer is None: - viewer = self._viewer - else: - return - cmd = 'cd %s; %s %s ' % (self._dir, viewer, self._filename) - os.system(cmd + " 2>/dev/null 1>/dev/null &") - - def _get_input(self, n, followed_by_output): - if n >= len(self._input): - return - else: - return "%s %s: %s"%(n, interpreter._prompt, self._input[n]) - - def _get_output(self, n): - return '\n ' + str(self._output[n]) + '\n\n' - - def _filename(self): - return 'sagelog.txt' - - def _header(self): - return self._title() - - def _footer(self): - return '' - - def _title(self): - return 'Sage Log %s' % self._time diff --git a/src/sage/misc/mathml.py b/src/sage/misc/mathml.py index d584a481900..e2d959bfe55 100644 --- a/src/sage/misc/mathml.py +++ b/src/sage/misc/mathml.py @@ -23,7 +23,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import -from six import iteritems, integer_types def list_function(x): @@ -44,13 +43,15 @@ def str_function(x): # One can add to the latex_table in order to install latexing # functionality for other types. -mathml_table = {list: list_function, - tuple:tuple_function, - bool:bool_function, - str: str_function, - float:str} -for x in integer_types: - mathml_table[x] = str +mathml_table = { + list: list_function, + tuple: tuple_function, + bool: bool_function, + str: str_function, + float: str, + int: str +} + class MathML(str): def __repr__(self): @@ -64,7 +65,7 @@ def mathml(x): try: return MathML(x._mathml_()) except (AttributeError, TypeError): - for k, f in iteritems(mathml_table): + for k, f in mathml_table.items(): if isinstance(x, k): return MathML(f(x)) diff --git a/src/sage/misc/messaging.py b/src/sage/misc/messaging.py index 05600b9f0a1..1664070efce 100644 --- a/src/sage/misc/messaging.py +++ b/src/sage/misc/messaging.py @@ -12,6 +12,10 @@ - Martin Albrecht (2012) - initial implementation """ from __future__ import absolute_import + +import http.client as httplib +from urllib.parse import urlencode + pushover_defaults = {"token": "Eql67F14ohOZJ0AtEBJJU7FiLAk8wK"} @@ -69,10 +73,6 @@ def pushover(message, **kwds): You may want to populate ``sage.misc.messaging.pushover_defaults`` with default values such as the default user in ``$HOME/.sage/init.sage``. """ - # import compatible with py2 and py3 - from six.moves import http_client as httplib - from six.moves.urllib.parse import urlencode - request = {"message": message} request.update(pushover_defaults) request.update(kwds) diff --git a/src/sage/misc/method_decorator.py b/src/sage/misc/method_decorator.py index 553ae78a2e5..149660d37a1 100644 --- a/src/sage/misc/method_decorator.py +++ b/src/sage/misc/method_decorator.py @@ -18,7 +18,6 @@ def __init__(self, f): ....: @MethodDecorator ....: def bar(self, x): ....: return x**2 - ....: sage: J = Foo() sage: J.bar diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index c0100a2d1af..e1c0946f515 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -38,11 +38,8 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range -from six import integer_types import os -import sys import time import resource import pdb @@ -51,6 +48,18 @@ from .lazy_string import lazy_string import sage.server.support +from sage.misc.lazy_import import lazy_import + +lazy_import("sage.misc.call", ["AttrCallObject", "attrcall", "call_method"], + deprecation=29869) + +lazy_import("sage.misc.verbose", ["verbose", "set_verbose", "set_verbose_files", + "get_verbose_files", "unset_verbose_files", "get_verbose"], + deprecation=17815) + +lazy_import("sage.misc.repr", ["coeff_repr", "repr_lincomb"], + deprecation=29892) + from sage.env import DOT_SAGE, HOSTNAME LOCAL_IDENTIFIER = '%s.%s' % (HOSTNAME, os.getpid()) @@ -492,169 +501,6 @@ def walltime(t=0): return time.time() - t -################################################################# -# simple verbosity system -################################################################# -LEVEL = 0 # default - -verbose_files = [] - - -def verbose(mesg="", t=0, level=1, caller_name=None): - """ - Print a message if the current verbosity is at least level. - - INPUT: - - - - ``mesg`` - str, a message to print - - - ``t`` - int, optional, if included, will also print - cputime(t), - which is the time since time t. Thus t should have - been obtained with t=cputime() - - - ``level`` - int, (default: 1) the verbosity level of - what we are printing - - - ``caller_name`` - string (default: None), the name - of the calling function; in most cases Python can deduce this, so - it need not be provided. - - - OUTPUT: possibly prints a message to stdout; also returns - cputime() - - EXAMPLES:: - - sage: set_verbose(1) - sage: t = cputime() - sage: t = verbose("This is Sage.", t, level=1, caller_name="william") # not tested - VERBOSE1 (william): This is Sage. (time = 0.0) - sage: set_verbose(0) - """ - if level > LEVEL: - return cputime() - - frame = sys._getframe(1).f_code - file_name = frame.co_filename - lineno = frame.co_firstlineno - if 'all' in verbose_files or level <= 0: - show = True - else: - show = False - for X in verbose_files: - if file_name.find(X) != -1: - show = True - break - - if not show: - return cputime() - - if t != 0 and mesg == "": - mesg = "Finished." - - # see recipe 14.7 in Python Cookbook - if caller_name is None: - caller_name = frame.co_name - if caller_name == "?: ": - caller_name = "" - short_file_name = os.path.split(frame.co_filename)[1] - if '<' in short_file_name and '>' in short_file_name: - s = "verbose %s (%s) %s" % (level, caller_name, mesg) - else: - s = "verbose %s (%s: %s, %s) %s" % (level, lineno, - short_file_name, caller_name, mesg) - if t != 0: - s = s + " (time = %s)" % cputime(t) - print(s) - sys.stdout.flush() - return cputime() - - -def set_verbose(level, files='all'): - """ - Set the global Sage verbosity level. - - INPUT: - - - ``level`` -- an integer between 0 and 2, inclusive. - - - ``files`` (default: 'all'): list of files to make verbose, or - 'all' to make ALL files verbose (the default). - - OUTPUT: changes the state of the verbosity flag and possibly - appends to the list of files that are verbose. - - EXAMPLES:: - - sage: set_verbose(2) - sage: verbose("This is Sage.", level=1) # not tested - VERBOSE1 (?): This is Sage. - sage: verbose("This is Sage.", level=2) # not tested - VERBOSE2 (?): This is Sage. - sage: verbose("This is Sage.", level=3) # not tested - [no output] - sage: set_verbose(0) - """ - if level is None: - level = -1 - if isinstance(level, str): - set_verbose_files([level]) - global LEVEL - LEVEL = level - if isinstance(files, str): - files = [files] - set_verbose_files(files) - - -def set_verbose_files(file_name): - """ - - """ - if not isinstance(file_name, list): - file_name = [file_name] - global verbose_files - verbose_files = file_name - - -def get_verbose_files(): - """ - - """ - return verbose_files - - -def unset_verbose_files(file_name): - """ - - """ - if not isinstance(file_name, list): - file_name = [file_name] - for X in file_name: - verbose_files.remove(X) - - -def get_verbose(): - """ - Return the global Sage verbosity level. - - INPUT: int level: an integer between 0 and 2, inclusive. - - OUTPUT: changes the state of the verbosity flag. - - EXAMPLES:: - - sage: get_verbose() - 0 - sage: set_verbose(2) - sage: get_verbose() - 2 - sage: set_verbose(0) - """ - global LEVEL - return LEVEL - - def union(x, y=None): """ Return the union of x and y, as a list. The resulting list need not @@ -772,173 +618,6 @@ def exactly_one_is_true(iterable): return any(it) and not any(it) -def coeff_repr(c, is_latex=False): - if not is_latex: - try: - return c._coeff_repr() - except AttributeError: - pass - if isinstance(c, integer_types + (float,)): - return str(c) - if is_latex and hasattr(c, '_latex_'): - s = c._latex_() - else: - s = str(c).replace(' ', '') - if s.find("+") != -1 or s.find("-") != -1: - if is_latex: - return "\\left(%s\\right)" % s - else: - return "(%s)" % s - return s - - -def repr_lincomb(terms, is_latex=False, scalar_mult="*", strip_one=False, - repr_monomial=None, latex_scalar_mult=None): - """ - Compute a string representation of a linear combination of some - formal symbols. - - INPUT: - - - ``terms`` -- list of terms, as pairs (support, coefficient) - - ``is_latex`` -- whether to produce latex (default: ``False``) - - ``scalar_mult`` -- string representing the multiplication (default:``'*'``) - - ``latex_scalar_mult`` -- latex string representing the multiplication - (default: ``''`` if ``scalar_mult`` is ``'*'``; otherwise ``scalar_mult``) - - ``coeffs`` -- for backward compatibility - - OUTPUT: - - - ``str`` - a string - - EXAMPLES:: - - sage: repr_lincomb([('a',1), ('b',-2), ('c',3)]) - 'a - 2*b + 3*c' - sage: repr_lincomb([('a',0), ('b',-2), ('c',3)]) - '-2*b + 3*c' - sage: repr_lincomb([('a',0), ('b',2), ('c',3)]) - '2*b + 3*c' - sage: repr_lincomb([('a',1), ('b',0), ('c',3)]) - 'a + 3*c' - sage: repr_lincomb([('a',-1), ('b','2+3*x'), ('c',3)]) - '-a + (2+3*x)*b + 3*c' - sage: repr_lincomb([('a', '1+x^2'), ('b', '2+3*x'), ('c', 3)]) - '(1+x^2)*a + (2+3*x)*b + 3*c' - sage: repr_lincomb([('a', '1+x^2'), ('b', '-2+3*x'), ('c', 3)]) - '(1+x^2)*a + (-2+3*x)*b + 3*c' - sage: repr_lincomb([('a', 1), ('b', -2), ('c', -3)]) - 'a - 2*b - 3*c' - sage: t = PolynomialRing(RationalField(),'t').gen() - sage: repr_lincomb([('a', -t), ('s', t - 2), ('', t^2 + 2)]) - '-t*a + (t-2)*s + (t^2+2)' - - Examples for ``scalar_mult``:: - - sage: repr_lincomb([('a',1), ('b',2), ('c',3)], scalar_mult='*') - 'a + 2*b + 3*c' - sage: repr_lincomb([('a',2), ('b',0), ('c',-3)], scalar_mult='**') - '2**a - 3**c' - sage: repr_lincomb([('a',-1), ('b',2), ('c',3)], scalar_mult='**') - '-a + 2**b + 3**c' - - Examples for ``scalar_mult`` and ``is_latex``:: - - sage: repr_lincomb([('a',-1), ('b',2), ('c',3)], is_latex=True) - '-a + 2b + 3c' - sage: repr_lincomb([('a',-1), ('b',-1), ('c',3)], is_latex=True, scalar_mult='*') - '-a - b + 3c' - sage: repr_lincomb([('a',-1), ('b',2), ('c',-3)], is_latex=True, scalar_mult='**') - '-a + 2**b - 3**c' - sage: repr_lincomb([('a',-2), ('b',-1), ('c',-3)], is_latex=True, latex_scalar_mult='*') - '-2*a - b - 3*c' - - Examples for ``strip_one``:: - - sage: repr_lincomb([ ('a',1), (1,-2), ('3',3) ]) - 'a - 2*1 + 3*3' - sage: repr_lincomb([ ('a',-1), (1,1), ('3',3) ]) - '-a + 1 + 3*3' - sage: repr_lincomb([ ('a',1), (1,-2), ('3',3) ], strip_one = True) - 'a - 2 + 3*3' - sage: repr_lincomb([ ('a',-1), (1,1), ('3',3) ], strip_one = True) - '-a + 1 + 3*3' - sage: repr_lincomb([ ('a',1), (1,-1), ('3',3) ], strip_one = True) - 'a - 1 + 3*3' - - Examples for ``repr_monomial``:: - - sage: repr_lincomb([('a',1), ('b',2), ('c',3)], repr_monomial = lambda s: s+"1") - 'a1 + 2*b1 + 3*c1' - """ - # Setting scalar_mult: symbol used for scalar multiplication - if is_latex: - if latex_scalar_mult is not None: - scalar_mult = latex_scalar_mult - elif scalar_mult == "*": - scalar_mult = "" - - if repr_monomial is None: - if is_latex: - - def repr_monomial(monomial): - return monomial._latex_() if hasattr(monomial, '_latex_') else str(monomial) - else: - repr_monomial = str - - s = "" - first = True - - if scalar_mult is None: - scalar_mult = "" if is_latex else "*" - - for (monomial, c) in terms: - if c != 0: - coeff = coeff_repr(c) - negative = False - if len(coeff) and coeff[0] == "-": - negative = True - try: - if c < 0: - negative = True - except (NotImplementedError, TypeError): - # comparisons may not be implemented for some coefficients - pass - if negative: - coeff = coeff_repr(-c, is_latex) - else: - coeff = coeff_repr(c, is_latex) - if coeff == "1": - coeff = "" - if coeff != "0": - if negative: - if first: - sign = "-" # add trailing space? - else: - sign = " - " - else: - if first: - sign = "" - else: - sign = " + " - b = repr_monomial(monomial) - if len(b): - if coeff != "": - if b == "1" and strip_one: - b = "" - else: - b = scalar_mult + b - s += "%s%s%s" % (sign, coeff, b) - first = False - if first: - return "0" - # this can happen only if are only terms with coeff_repr(c) == "0" - # elif s == "": - # return "1" # is empty string representation invalid? - else: - return s - - def strunc(s, n=60): """ Truncate at first space after position n, adding '...' if @@ -1565,172 +1244,6 @@ def embedded(): """ return sage.server.support.EMBEDDED_MODE - -############################################# -# Operators -############################################# -class AttrCallObject(object): - def __init__(self, name, args, kwds): - """ - TESTS:: - - sage: f = attrcall('core', 3); f - *.core(3) - sage: TestSuite(f).run() - """ - self.name = name - self.args = args - self.kwds = kwds - - def __call__(self, x, *args): - """ - Gets the ``self.name`` method from ``x``, calls it with - ``self.args`` and ``args`` as positional parameters and - ``self.kwds`` as keyword parameters, and returns the result. - - EXAMPLES:: - - sage: core = attrcall('core', 3) - sage: core(Partition([4,2])) - [4, 2] - - sage: series = attrcall('series', x) - sage: series(sin(x), 4) - 1*x + (-1/6)*x^3 + Order(x^4) - """ - return getattr(x, self.name)(*(self.args + args), **self.kwds) - - def __repr__(self): - """ - Return a string representation of this object. - - The star in the output represents the object passed into ``self``. - - EXAMPLES:: - - sage: attrcall('core', 3) - *.core(3) - sage: attrcall('hooks', flatten=True) - *.hooks(flatten=True) - sage: attrcall('hooks', 3, flatten=True) - *.hooks(3, flatten=True) - """ - s = "*.%s(%s" % (self.name, ", ".join(map(repr, self.args))) - if self.kwds: - if self.args: - s += ", " - s += ", ".join("%s=%s" % keyvalue for keyvalue in self.kwds.items()) - s += ")" - return s - - def __eq__(self, other): - """ - Equality testing - - EXAMPLES:: - - sage: attrcall('core', 3, flatten = True) == attrcall('core', 3, flatten = True) - True - sage: attrcall('core', 2) == attrcall('core', 3) - False - sage: attrcall('core', 2) == 1 - False - """ - return self.__class__ == other.__class__ and self.__dict__ == other.__dict__ - - def __ne__(self, other): - """ - Equality testing - - EXAMPLES:: - - sage: attrcall('core', 3, flatten = True) != attrcall('core', 3, flatten = True) - False - sage: attrcall('core', 2) != attrcall('core', 3) - True - sage: attrcall('core', 2) != 1 - True - """ - return not self == other - - def __hash__(self): - """ - Hash value - - This method tries to ensure that, when two ``attrcall`` - objects are equal, they have the same hash value. - - .. warning:: dicts are not hashable, so we instead hash their - items; however the order of those items might differ. The - proper fix would be to use a frozen dict for ``kwds``, when - frozen dicts will be available in Python. - - EXAMPLES:: - - sage: x = attrcall('core', 3, flatten = True, blah = 1) - sage: hash(x) # random # indirect doctest - 210434060 - sage: type(hash(x)) - - sage: y = attrcall('core', 3, blah = 1, flatten = True) - sage: hash(y) == hash(x) - True - sage: y = attrcall('core', 3, flatten = True, blah = 2) - sage: hash(y) != hash(x) - True - sage: hash(attrcall('core', 2)) != hash(attrcall('core', 3)) - True - sage: hash(attrcall('core', 2)) != hash(1) - True - - Note: a missing ``__hash__`` method here used to break the - unique representation of parents taking ``attrcall`` objects - as input; see :trac:`8911`. - """ - return hash((self.args, tuple(sorted(self.kwds.items())))) - - -def attrcall(name, *args, **kwds): - """ - Returns a callable which takes in an object, gets the method named - name from that object, and calls it with the specified arguments - and keywords. - - INPUT: - - - ``name`` - a string of the name of the method you - want to call - - - ``args, kwds`` - arguments and keywords to be passed - to the method - - EXAMPLES:: - - sage: f = attrcall('core', 3); f - *.core(3) - sage: [f(p) for p in Partitions(5)] - [[2], [1, 1], [1, 1], [3, 1, 1], [2], [2], [1, 1]] - """ - return AttrCallObject(name, args, kwds) - - -def call_method(obj, name, *args, **kwds): - """ - Call the method ``name`` on ``obj``. - - This has to exist somewhere in Python!!! - - .. SEEALSO:: :func:`operator.methodcaller` :func:`attrcal` - - EXAMPLES:: - - sage: from sage.misc.misc import call_method - sage: call_method(1, "__add__", 2) - 3 - """ - return getattr(obj, name)(*args, **kwds) - - def is_in_string(line, pos): r""" Returns True if the character at position pos in line occurs diff --git a/src/sage/misc/mrange.py b/src/sage/misc/mrange.py index fd513ff09cd..84500d961a1 100644 --- a/src/sage/misc/mrange.py +++ b/src/sage/misc/mrange.py @@ -64,7 +64,6 @@ def _is_finite(L, fallback=True): True sage: _is_finite([]) True - sage: from six.moves import range sage: _is_finite(range(10^8)) True sage: from itertools import product @@ -208,8 +207,7 @@ def mrange_iter(iter_list, typ=list): sage: mrange_iter([range(5),range(3),range(0)]) [] - sage: from six.moves import range - sage: mrange_iter([range(5),range(3),range(-2)]) + sage: mrange_iter([range(5), range(3), range(-2)]) [] This example is not empty, and should not be. See :trac:`6561`. diff --git a/src/sage/misc/nested_class.pyx b/src/sage/misc/nested_class.pyx index b164a049108..3054461a9c7 100644 --- a/src/sage/misc/nested_class.pyx +++ b/src/sage/misc/nested_class.pyx @@ -103,7 +103,6 @@ The name for ``"A1.A2"`` could potentially be set to ``"B1.A2"``. But that will import sys cdef dict sys_modules = sys.modules -from six import class_types __all__ = ['modify_for_nested_pickle', 'nested_pickle', 'NestedClassMetaclass', 'MainClass' @@ -214,7 +213,7 @@ cpdef modify_for_nested_pickle(cls, str name_prefix, module, first_run=True): setattr(module, dotted_name, v) modify_for_nested_pickle(v, name_prefix, module, False) v.__name__ = dotted_name - elif isinstance(v, class_types): + elif isinstance(v, type): v_name = v.__name__ if v_name == name and v.__module__ == mod_name and getattr(module, v_name, None) is not v: # OK, probably this is a nested class. @@ -224,7 +223,7 @@ cpdef modify_for_nested_pickle(cls, str name_prefix, module, first_run=True): v.__name__ = dotted_name else: for (name, v) in cls.__dict__.items(): - if isinstance(v, class_types): + if isinstance(v, type): v_name = v.__name__ if v_name == cls_name + name and v.__module__ == mod_name: # OK, probably this is a nested class. diff --git a/src/sage/misc/nested_class_test.py b/src/sage/misc/nested_class_test.py index cca9bdb0a72..61e29f46d46 100644 --- a/src/sage/misc/nested_class_test.py +++ b/src/sage/misc/nested_class_test.py @@ -45,7 +45,6 @@ # http://www.gnu.org/licenses/ #****************************************************************************** from __future__ import print_function, absolute_import -from six import add_metaclass __all__ = [] # Don't document any parents @@ -64,24 +63,24 @@ def __init__(self): sage: sage.misc.nested_class_test.TestParent1() """ - from sage.categories.all import Sets + from sage.categories.sets_cat import Sets Parent.__init__(self, category = Sets()) class Element(ElementWrapper): pass -@add_metaclass(NestedClassMetaclass) -class TestParent2(Parent): +class TestParent2(Parent, metaclass=NestedClassMetaclass): def __init__(self): """ EXAMPLES:: sage: sage.misc.nested_class_test.TestParent2() Traceback (most recent call last): + ... TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases """ - from sage.categories.all import Sets + from sage.categories.sets_cat import Sets Parent.__init__(self, category = Sets()) class Element(ElementWrapper): @@ -97,15 +96,14 @@ def __init__(self): sage: sage.misc.nested_class_test.TestParent3() """ - from sage.categories.all import Sets + from sage.categories.sets_cat import Sets Parent.__init__(self, category = Sets()) class Element(ElementWrapper): pass -@add_metaclass(ClasscallMetaclass) -class TestParent4(Parent): +class TestParent4(Parent, metaclass=ClasscallMetaclass): def __init__(self): """ EXAMPLES:: @@ -113,7 +111,7 @@ def __init__(self): sage: sage.misc.nested_class_test.TestParent4() """ - from sage.categories.all import Sets + from sage.categories.sets_cat import Sets Parent.__init__(self, category=Sets()) def __eq__(self, other): @@ -192,8 +190,7 @@ class C(object): C = ALB.C -@add_metaclass(NestedClassMetaclass) -class ABBMeta(object): +class ABBMeta(metaclass=NestedClassMetaclass): class B(object): """ B interne @@ -201,13 +198,11 @@ class B(object): pass -@add_metaclass(NestedClassMetaclass) -class ABLMeta(object): +class ABLMeta(metaclass=NestedClassMetaclass): B = B -@add_metaclass(NestedClassMetaclass) -class ALBMeta(object): +class ALBMeta(metaclass=NestedClassMetaclass): """ There is a nested class just below which is properly sphinxed. """ diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index 01ef9e7800e..c27b1e6e455 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -160,17 +160,22 @@ def list_packages(*pkg_types, **opts): The keys are package names and values are dictionaries with the following keys: - - ``'type'``: either ``'standard'``, ``'optional'``, ``'experimental'`` or ``'pip'`` + - ``'type'``: either ``'base``, ``'standard'``, ``'optional'``, or ``'experimental'`` + - ``'source'``: either ``'normal', ``'pip'``, or ``'script'`` - ``'installed'``: boolean - ``'installed_version'``: ``None`` or a string - ``'remote_version'``: string INPUT: - - ``pkg_types`` -- (optional) a sublist of ``'standard'``, ``'optional'``, - ``'experimental'`` or ``'pip'``. If provided, list only the packages with the + - ``pkg_types`` -- (optional) a sublist of ``'base``, ``'standard'``, ``'optional'``, + or ``'experimental'``. If provided, list only the packages with the given type(s), otherwise list all packages. + - ``pkg_sources`` -- (optional) a sublist of ``'normal', ``'pip'``, or ``'script'``. + If provided, list only the packages with the given source(s), otherwise list all + packages. + - ``local`` -- (optional, default: ``False``) if set to ``True``, then do not consult remote (PyPI) repositories for package versions (only applicable for ``'pip'`` type) @@ -192,36 +197,34 @@ def list_packages(*pkg_types, **opts): ... 'zn_poly', 'zope_interface'] - sage: L['ppl'] # optional - build - {'installed': True, - 'installed_version': '...', - 'remote_version': '...', - 'type': 'standard'} - - sage: L = list_packages('pip', local=True) # optional - build - sage: L['beautifulsoup4'] # optional - build - {'installed': ..., - 'installed_version': ..., - 'remote_version': None, - 'type': 'pip'} - - sage: L = list_packages('pip') # optional - build internet - sage: L['beautifulsoup4'] # optional - build internet - {'installed': ..., - 'installed_version': ..., - 'remote_version': u'...', - 'type': 'pip'} + sage: sage_conf_info = L['sage_conf'] # optional - build + sage: sage_conf_info['type'] # optional - build + 'standard' + sage: sage_conf_info['installed'] # optional - build + True + sage: sage_conf_info['source'] # optional - build + 'script' + + sage: L = list_packages(pkg_sources=['pip'], local=True) # optional - build internet + sage: bs4_info = L['beautifulsoup4'] # optional - build internet + sage: bs4_info['type'] # optional - build internet + 'optional' + sage: bs4_info['source'] # optional - build internet + 'pip' Check the option ``exclude_pip``:: - sage: list_packages('pip', exclude_pip=True) # optional - build - {} + sage: [p for p, d in list_packages('optional', exclude_pip=True).items() # optional - build + ....: if d['source'] == 'pip'] + [] """ if not pkg_types: - pkg_types = ('standard', 'optional', 'experimental', 'pip') - elif any(pkg_type not in ('standard', 'optional', 'experimental', 'pip') for pkg_type in pkg_types): - raise ValueError("Each pkg_type must be one of 'standard', 'optional', 'experimental', 'pip'") + pkg_types = ('base', 'standard', 'optional', 'experimental') + elif any(pkg_type not in ('base', 'standard', 'optional', 'experimental') for pkg_type in pkg_types): + raise ValueError("Each pkg_type must be one of 'base', 'standard', 'optional', 'experimental'") + pkg_sources = opts.pop('pkg_sources', + ('normal', 'pip', 'script')) local = opts.pop('local', False) ignore_URLError = opts.pop('ignore_URLError', False) @@ -231,9 +234,17 @@ def list_packages(*pkg_types, **opts): installed = installed_packages(exclude_pip) - pkgs = {} SAGE_PKGS = sage.env.SAGE_PKGS - for p in os.listdir(SAGE_PKGS): + if not SAGE_PKGS: + return {} + + try: + lp = os.listdir(SAGE_PKGS) + except FileNotFoundError: + return {} + + pkgs = {} + for p in lp: try: f = open(os.path.join(SAGE_PKGS, p, "type")) except IOError: @@ -246,23 +257,35 @@ def list_packages(*pkg_types, **opts): if typ not in pkg_types: continue - pkg = {'name': p, 'type': typ, 'installed_version': installed.get(p)} + if os.path.isfile(os.path.join(SAGE_PKGS, p, "requirements.txt")): + src = 'pip' + elif os.path.isfile(os.path.join(SAGE_PKGS, p, "checksums.ini")): + src = 'normal' + else: + src = 'script' + + if src not in pkg_sources: + continue + + pkg = {'name': p, 'type': typ, 'source': src, 'installed_version': installed.get(p)} pkg['installed'] = pkg['installed_version'] is not None - if pkg['type'] == 'pip': + if pkg['source'] == 'pip': if exclude_pip: continue if not local: pkg['remote_version'] = pip_remote_version(p, ignore_URLError=ignore_URLError) else: pkg['remote_version'] = None - else: + elif pkg['source'] == 'normal': # If package-version.txt does not exist, that is an error # in the build system => we just propagate the exception package_filename = os.path.join(SAGE_PKGS, p, "package-version.txt") with open(package_filename) as f: pkg['remote_version'] = f.read().strip() pkg['installed_version'] = installed.get(p) + else: + pkg['remote_version'] = 'none' pkgs[p] = pkg @@ -280,8 +303,12 @@ def installed_packages(exclude_pip=True): EXAMPLES:: - sage: installed_packages() # optional - build - {...'brial': ...'pynac': ...} + sage: sorted(installed_packages().keys()) # optional - build + [...'alabaster', ...'sage_conf', ...] + sage: installed_packages()['alabaster'] # optional - build, random + '0.7.12' + sage: installed_packages()['sage_conf'] # optional - build + 'none' .. SEEALSO:: @@ -291,8 +318,13 @@ def installed_packages(exclude_pip=True): if not exclude_pip: installed.update(pip_installed_packages()) # Sage packages should override pip packages (Trac #23997) - installed.update(pkgname_split(pkgname) - for pkgname in os.listdir(sage.env.SAGE_SPKG_INST)) + SAGE_SPKG_INST = sage.env.SAGE_SPKG_INST + if SAGE_SPKG_INST: + try: + lp = os.listdir(SAGE_SPKG_INST) + installed.update(pkgname_split(pkgname) for pkgname in lp) + except FileNotFoundError: + pass return installed @@ -324,8 +356,8 @@ def is_package_installed(package, exclude_pip=True): Check that the option ``exclude_pip`` is turned on by default:: sage: from sage.misc.package import list_packages - sage: for pkg in list_packages('pip', local=True): # optional - build - ....: assert not is_package_installed(pkg) + sage: for pkg in list_packages(pkg_sources=('pip'), local=True): # optional - build + ....: assert not is_package_installed(pkg), "pip package is installed: {}".format(pkg) .. NOTE:: @@ -429,7 +461,6 @@ def optional_packages(): True """ pkgs = list_packages('optional', local=True) - pkgs.update(list_packages('pip', local=True)) pkgs = pkgs.values() return (sorted(pkg['name'] for pkg in pkgs if pkg['installed']), sorted(pkg['name'] for pkg in pkgs if not pkg['installed'])) diff --git a/src/sage/misc/parser.pyx b/src/sage/misc/parser.pyx index dc97c2c7302..244abd8f3a2 100644 --- a/src/sage/misc/parser.pyx +++ b/src/sage/misc/parser.pyx @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ A parser for symbolic equations and expressions @@ -94,22 +95,21 @@ def token_to_str(int token): return chr(token) -cdef inline bint is_alphanumeric(char c): - return 'a' <= c <= 'z' or 'A' <= c <= 'Z' or '0' <= c <= '9' or c == '_' +cdef inline bint is_alphanumeric(c): + return c.isalnum() or c == '_' -cdef inline bint is_whitespace(char c): - return (c != 0) & (strchr(" \t\n\r", c) != NULL) +cdef inline bint is_whitespace(c): + return c.isspace() cdef class Tokenizer: - cdef char *s - cdef string_obj + cdef str s cdef int token cdef int pos cdef int last_pos def __init__(self, s): - """ + r""" This class takes a string and turns it into a list of tokens for use by the parser. @@ -152,6 +152,11 @@ cdef class Tokenizer: sage: Tokenizer("a a1 _a_24").test() ['NAME(a)', 'NAME(a1)', 'NAME(_a_24)'] + There is special handling for matrices:: + + sage: Tokenizer("matrix(a)").test() + ['MATRIX', '(', 'NAME(a)', ')'] + Anything else is an error:: sage: Tokenizer("&@~").test() @@ -163,12 +168,17 @@ cdef class Tokenizer: [')', ')', '(', 'FLOAT(5e5)', 'NAME(e5)'] sage: Tokenizer("?$%").test() ['ERROR', 'ERROR', 'ERROR'] + + TESTS: + + Check support for unicode characters (:trac:`29280`):: + + sage: Tokenizer("λ+α_β0 Γ^ω").test() + ['NAME(λ)', '+', 'NAME(α_β0)', 'NAME(Γ)', '^', 'NAME(ω)'] """ - s = str_to_bytes(s) self.pos = 0 self.last_pos = 0 self.s = s - self.string_obj = s # so it doesn't get deallocated before self def test(self): """ @@ -233,51 +243,53 @@ cdef class Tokenizer: """ cdef bint seen_exp, seen_decimal cdef int type - cdef char* s = self.s + cdef str s = self.s cdef int pos = self.pos + cdef int s_len = len(s) # skip whitespace - if is_whitespace(s[pos]): - while is_whitespace(s[pos]): + if pos < s_len and is_whitespace(s[pos]): + while pos < s_len and is_whitespace(s[pos]): pos += 1 self.pos = pos # end of string - if s[pos] == 0: + if pos == s_len: return EOS - # dipthongs - if s[pos+1] == '=': - if s[pos] == '<': + # diphthongs + if pos+1 < s_len: + if s[pos+1] == '=': + if s[pos] == '<': + self.pos += 2 + return LESS_EQ + elif s[pos] == '>': + self.pos += 2 + return GREATER_EQ + elif s[pos] == '!': + self.pos += 2 + return NOT_EQ + elif s[pos] == '=': + self.pos += 2 + return '=' + + elif s[pos] == '*' and s[pos+1] == '*': self.pos += 2 - return LESS_EQ - elif s[pos] == '>': - self.pos += 2 - return GREATER_EQ - elif s[pos] == '!': - self.pos += 2 - return NOT_EQ - elif s[pos] == '=': - self.pos += 2 - return '=' - - elif s[pos] == '*' and s[pos+1] == '*': - self.pos += 2 - return '^' + return '^' # simple tokens - if strchr("+-*/^()=<>,[]{}!", s[pos]): - type = s[pos] + if s[pos] in "+-*/^()=><,[]{}!": + type = ord(s[pos]) self.pos += 1 return type # numeric literals - if '0' <= s[pos] <= '9' or s[pos] == '.': + if s[pos].isdigit() or s[pos] == '.': type = INT seen_exp = False seen_decimal = False - while True: - if '0' <= s[pos] <= '9': + while pos < s_len: + if s[pos].isdigit(): pass elif s[pos] == '.': if seen_decimal or seen_exp: @@ -298,16 +310,17 @@ cdef class Tokenizer: self.pos = pos return type else: - self.pos = pos - return type + break pos += 1 + self.pos = pos + return type # name literals if is_alphanumeric(s[pos]): - while is_alphanumeric(s[pos]): + while pos < s_len and is_alphanumeric(s[pos]): pos += 1 # matrices - if s[self.pos:pos] == b'matrix': + if s[self.pos:pos] == 'matrix': self.pos = pos return MATRIX self.pos = pos @@ -334,7 +347,7 @@ cdef class Tokenizer: sage: token_to_str(t.next()) 'EOS' """ - while is_whitespace(self.s[self.pos]): + while self.pos < len(self.s) and is_whitespace(self.s[self.pos]): self.pos += 1 self.last_pos = self.pos self.token = self.find() @@ -429,9 +442,7 @@ cdef class Tokenizer: sage: t.last_token_string() '1e5' """ - s = PyBytes_FromStringAndSize(&self.s[self.last_pos], - self.pos - self.last_pos) - return bytes_to_str(s) + return self.s[self.last_pos:self.pos] cdef class Parser: diff --git a/src/sage/misc/persist.pyx b/src/sage/misc/persist.pyx index 828f15bafaf..97c2c0f33db 100644 --- a/src/sage/misc/persist.pyx +++ b/src/sage/misc/persist.pyx @@ -29,6 +29,7 @@ save member functions and commands. import io import os +import pickle import sys from textwrap import dedent @@ -38,8 +39,6 @@ from textwrap import dedent import zlib; comp = zlib import bz2; comp_other = bz2 -from six.moves import cPickle as pickle - from .misc import SAGE_DB from .sage_unittest import TestSuite @@ -834,7 +833,7 @@ class SagePickler(_BasePickler): sage: loads(b'x\x9ck`J\x8e\x8f\xcfM\xcc\xcc\x8b\x8f\xe7r\xcb\xcf\xe7*d\x0cej`/dj\r*d\xd6\x03\x00\x89\xc5\x08{', encoding='ASCII') #py3 Traceback (most recent call last): ... - UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 0: ordinal not in range(128) + UnicodeDecodeError: 'ascii' codec can...t decode byte 0x80 in position 0: ordinal not in range(128) """ @@ -1024,7 +1023,7 @@ def loads(s, compress=True, **kwargs): sage: loads(b'x\x9ck`J\x8e\x8f\xcfM\xcc\xcc\x8b\x8f\xe7r\xcb\xcf\xe7*d\x0cej`/dj\r*d\xd6\x03\x00\x89\xc5\x08{', encoding='ASCII') #py3 Traceback (most recent call last): ... - UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 0: ordinal not in range(128) + UnicodeDecodeError: 'ascii' codec can...t decode byte 0x80 in position 0: ordinal not in range(128) """ if not isinstance(s, bytes): diff --git a/src/sage/misc/prandom.py b/src/sage/misc/prandom.py index 9812cf82a32..b473d79aa3a 100644 --- a/src/sage/misc/prandom.py +++ b/src/sage/misc/prandom.py @@ -173,7 +173,6 @@ def sample(population, k): sage: sample(["Here", "I", "come", "to", "save", "the", "day"], 3) ['Here', 'to', 'day'] - sage: from six.moves import range sage: sample(range(2^30), 7) [357009070, 558990255, 196187132, 752551188, 85926697, 954621491, 624802848] """ diff --git a/src/sage/misc/random_testing.py b/src/sage/misc/random_testing.py index f8c26317a3b..9126d93edc8 100644 --- a/src/sage/misc/random_testing.py +++ b/src/sage/misc/random_testing.py @@ -9,7 +9,6 @@ decorator to help write random testers that meet these goals. """ from __future__ import print_function, absolute_import -from six.moves import range from functools import wraps diff --git a/src/sage/misc/remote_file.py b/src/sage/misc/remote_file.py index ef2d6e91725..297c5376e55 100644 --- a/src/sage/misc/remote_file.py +++ b/src/sage/misc/remote_file.py @@ -3,6 +3,7 @@ from __future__ import absolute_import import os +from urllib.request import Request, urlopen def get_remote_file(filename, verbose=True): @@ -38,8 +39,7 @@ def get_remote_file(filename, verbose=True): # so do not import it in the module scope. # import compatible with py2 and py3 - from six.moves.urllib.request import Request, urlopen - req = Request(filename, headers={"User-Agent": "sage-doctest"}) + req = Request(filename, headers={"User-Agent":"sage-doctest"}) if verbose: print("Loading started") diff --git a/src/sage/misc/repr.py b/src/sage/misc/repr.py new file mode 100644 index 00000000000..a65fd7d9269 --- /dev/null +++ b/src/sage/misc/repr.py @@ -0,0 +1,193 @@ +""" +Repr formatting support +""" + + +def coeff_repr(c, is_latex=False): + r""" + String representing coefficients in a linear combination. + + INPUT: + + - ``c`` -- a coefficient (i.e., an element of a ring) + + OUTPUT: + + A string + + EXAMPLES:: + + sage: from sage.misc.repr import coeff_repr + sage: coeff_repr(QQ(1/2)) + '1/2' + sage: coeff_repr(-x^2) + '(-x^2)' + sage: coeff_repr(QQ(1/2), is_latex=True) + '\\frac{1}{2}' + sage: coeff_repr(-x^2, is_latex=True) + '\\left(-x^{2}\\right)' + """ + if not is_latex: + try: + return c._coeff_repr() + except AttributeError: + pass + if isinstance(c, (int, float)): + return str(c) + if is_latex and hasattr(c, '_latex_'): + s = c._latex_() + else: + s = str(c).replace(' ', '') + if s.find("+") != -1 or s.find("-") != -1: + if is_latex: + return "\\left(%s\\right)" % s + else: + return "(%s)" % s + return s + + +def repr_lincomb(terms, is_latex=False, scalar_mult="*", strip_one=False, + repr_monomial=None, latex_scalar_mult=None): + """ + Compute a string representation of a linear combination of some + formal symbols. + + INPUT: + + - ``terms`` -- list of terms, as pairs (support, coefficient) + - ``is_latex`` -- whether to produce latex (default: ``False``) + - ``scalar_mult`` -- string representing the multiplication (default:``'*'``) + - ``latex_scalar_mult`` -- latex string representing the multiplication + (default: ``''`` if ``scalar_mult`` is ``'*'``; otherwise ``scalar_mult``) + - ``coeffs`` -- for backward compatibility + + OUTPUT: + + - ``str`` - a string + + EXAMPLES:: + + sage: repr_lincomb([('a',1), ('b',-2), ('c',3)]) + 'a - 2*b + 3*c' + sage: repr_lincomb([('a',0), ('b',-2), ('c',3)]) + '-2*b + 3*c' + sage: repr_lincomb([('a',0), ('b',2), ('c',3)]) + '2*b + 3*c' + sage: repr_lincomb([('a',1), ('b',0), ('c',3)]) + 'a + 3*c' + sage: repr_lincomb([('a',-1), ('b','2+3*x'), ('c',3)]) + '-a + (2+3*x)*b + 3*c' + sage: repr_lincomb([('a', '1+x^2'), ('b', '2+3*x'), ('c', 3)]) + '(1+x^2)*a + (2+3*x)*b + 3*c' + sage: repr_lincomb([('a', '1+x^2'), ('b', '-2+3*x'), ('c', 3)]) + '(1+x^2)*a + (-2+3*x)*b + 3*c' + sage: repr_lincomb([('a', 1), ('b', -2), ('c', -3)]) + 'a - 2*b - 3*c' + sage: t = PolynomialRing(RationalField(),'t').gen() + sage: repr_lincomb([('a', -t), ('s', t - 2), ('', t^2 + 2)]) + '-t*a + (t-2)*s + (t^2+2)' + + Examples for ``scalar_mult``:: + + sage: repr_lincomb([('a',1), ('b',2), ('c',3)], scalar_mult='*') + 'a + 2*b + 3*c' + sage: repr_lincomb([('a',2), ('b',0), ('c',-3)], scalar_mult='**') + '2**a - 3**c' + sage: repr_lincomb([('a',-1), ('b',2), ('c',3)], scalar_mult='**') + '-a + 2**b + 3**c' + + Examples for ``scalar_mult`` and ``is_latex``:: + + sage: repr_lincomb([('a',-1), ('b',2), ('c',3)], is_latex=True) + '-a + 2b + 3c' + sage: repr_lincomb([('a',-1), ('b',-1), ('c',3)], is_latex=True, scalar_mult='*') + '-a - b + 3c' + sage: repr_lincomb([('a',-1), ('b',2), ('c',-3)], is_latex=True, scalar_mult='**') + '-a + 2**b - 3**c' + sage: repr_lincomb([('a',-2), ('b',-1), ('c',-3)], is_latex=True, latex_scalar_mult='*') + '-2*a - b - 3*c' + + Examples for ``strip_one``:: + + sage: repr_lincomb([ ('a',1), (1,-2), ('3',3) ]) + 'a - 2*1 + 3*3' + sage: repr_lincomb([ ('a',-1), (1,1), ('3',3) ]) + '-a + 1 + 3*3' + sage: repr_lincomb([ ('a',1), (1,-2), ('3',3) ], strip_one = True) + 'a - 2 + 3*3' + sage: repr_lincomb([ ('a',-1), (1,1), ('3',3) ], strip_one = True) + '-a + 1 + 3*3' + sage: repr_lincomb([ ('a',1), (1,-1), ('3',3) ], strip_one = True) + 'a - 1 + 3*3' + + Examples for ``repr_monomial``:: + + sage: repr_lincomb([('a',1), ('b',2), ('c',3)], repr_monomial = lambda s: s+"1") + 'a1 + 2*b1 + 3*c1' + """ + # Setting scalar_mult: symbol used for scalar multiplication + if is_latex: + if latex_scalar_mult is not None: + scalar_mult = latex_scalar_mult + elif scalar_mult == "*": + scalar_mult = "" + + if repr_monomial is None: + if is_latex: + + def repr_monomial(monomial): + return monomial._latex_() if hasattr(monomial, '_latex_') else str(monomial) + else: + repr_monomial = str + + s = "" + first = True + + if scalar_mult is None: + scalar_mult = "" if is_latex else "*" + + for (monomial, c) in terms: + if c != 0: + coeff = coeff_repr(c) + negative = False + if len(coeff) and coeff[0] == "-": + negative = True + try: + if c < 0: + negative = True + except (NotImplementedError, TypeError): + # comparisons may not be implemented for some coefficients + pass + if negative: + coeff = coeff_repr(-c, is_latex) + else: + coeff = coeff_repr(c, is_latex) + if coeff == "1": + coeff = "" + if coeff != "0": + if negative: + if first: + sign = "-" # add trailing space? + else: + sign = " - " + else: + if first: + sign = "" + else: + sign = " + " + b = repr_monomial(monomial) + if len(b): + if coeff != "": + if b == "1" and strip_one: + b = "" + else: + b = scalar_mult + b + s += "%s%s%s" % (sign, coeff, b) + first = False + if first: + return "0" + # this can happen only if are only terms with coeff_repr(c) == "0" + # elif s == "": + # return "1" # is empty string representation invalid? + else: + return s diff --git a/src/sage/misc/rest_index_of_methods.py b/src/sage/misc/rest_index_of_methods.py index fbac3d0161d..4547316eb1f 100644 --- a/src/sage/misc/rest_index_of_methods.py +++ b/src/sage/misc/rest_index_of_methods.py @@ -11,8 +11,6 @@ import inspect -from six import PY2 - from sage.misc.sageinspect import _extract_embedded_position @@ -175,7 +173,7 @@ def gen_rest_table_index(obj, names=None, sort=True, only_local_functions=True): link = ":meth:`~{module}.{cls}.{func}`".format( module=e.im_class.__module__, cls=e.im_class.__name__, func=fname(e)) - elif not PY2 and inspect.isfunction(e) and inspect.isclass(obj): + elif inspect.isfunction(e) and inspect.isclass(obj): link = ":meth:`~{module}.{cls}.{func}`".format( module=obj.__module__, cls=obj.__name__, func=fname(e)) elif inspect.isfunction(e): diff --git a/src/sage/misc/sage_eval.py b/src/sage/misc/sage_eval.py index 14020fc56e0..63fa92b3f58 100644 --- a/src/sage/misc/sage_eval.py +++ b/src/sage/misc/sage_eval.py @@ -11,7 +11,6 @@ #***************************************************************************** from __future__ import absolute_import, division -import six from copy import copy import sage.repl.preparse as preparser @@ -181,7 +180,7 @@ def sage_eval(source, locals=None, cmds='', preparse=True): locals = copy(source[2]) source = source[1] - if not isinstance(source, six.string_types): + if not isinstance(source, str): raise TypeError("source must be a string.") if locals is None: diff --git a/src/sage/misc/sage_input.py b/src/sage/misc/sage_input.py index 3b1b978d37b..a9997770a0d 100644 --- a/src/sage/misc/sage_input.py +++ b/src/sage/misc/sage_input.py @@ -174,9 +174,6 @@ from __future__ import print_function, absolute_import -from six import iteritems, integer_types, string_types - - def sage_input(x, preparse=True, verify=False, allow_locals=False): r""" Return a sequence of commands that can be used to rebuild the object ``x``. @@ -395,24 +392,6 @@ def __call__(self, x, coerced=False): sage: sage_input(-11r, preparse=None, verify=True) # Verified -int(11) - sage: sage_input(long(-5), verify=True) # py2 - # Verified - -long(5) - sage: sage_input(long(-7), preparse=False, verify=True) - # Verified - -7L - sage: sage_input(long(11), preparse=None, verify=True) # py2 - # Verified - long(11) - sage: sage_input(long(2^70), verify=True) - # Verified - 1180591620717411303424r - sage: sage_input(-long(2^80), preparse=False, verify=True) - # Verified - -1208925819614629174706176 - sage: sage_input(long(2^75), preparse=None, verify=True) # py2 - # Verified - long(37778931862957161709568) sage: sage_input(float(-infinity), preparse=True, verify=True) # Verified -float(infinity) @@ -481,8 +460,7 @@ def __call__(self, x, coerced=False): if isinstance(x, bool): return SIE_literal_stringrep(self, str(x)) - if (isinstance(x, int) or - (isinstance(x, integer_types) and not isinstance(int(x), int))): + if isinstance(x, int): # For longs that don't fit in an int, we just use the int # code; it will get extended to long automatically. if self._preparse is True: @@ -493,27 +471,10 @@ def __call__(self, x, coerced=False): elif self._preparse is False: return self.int(x) else: - tyname = 'int' if isinstance(x, int) else 'long' - if x < 0: - return -self.name(tyname)(self.int(-x)) - else: - return self.name(tyname)(self.int(x)) - - if isinstance(x, integer_types): - # This must be a long that does fit in an int, so we need either - # long(x) or an 'L' suffix. - # With the current preparser, 1Lr does not work. - # 1rL does work; but that's just ugly, so I don't use it. - if self._preparse is False: if x < 0: - return -SIE_literal_stringrep(self, str(-x) + 'L') + return -self.name('int')(self.int(-x)) else: - return SIE_literal_stringrep(self, str(x) + 'L') - else: - if x < 0: - return -self.name('long')(self.int(-x)) - else: - return self.name('long')(self.int(x)) + return self.name('int')(self.int(x)) if isinstance(x, float): # floats could often have prettier output, @@ -536,7 +497,7 @@ def __call__(self, x, coerced=False): return self.name('float')(self.int(ZZ(rrx))) return self.name('float')(RR(x)) - if isinstance(x, string_types): + if isinstance(x, str): return SIE_literal_stringrep(self, repr(x)) if isinstance(x, tuple): @@ -598,10 +559,6 @@ def int(self, n): sage: sib.result(sib.int(-3^50)) -717897987691852588770249 - sage: sib = SageInputBuilder() - sage: sib.result(sib.int(long(2^65))) - 36893488147419103232 - sage: sib = SageInputBuilder() sage: sib.result(sib.int(-42r)) -42 @@ -1215,7 +1172,9 @@ def result(self, e): return SageInputAnswer(sif._commands, sif.format(e, 0)) # Python's precedence levels. Hand-transcribed from section 5.14 of -# the Python reference manual. +# the Python 2 reference manual. In the Python 3 reference manual +# this is section 6.16. +# See https://docs.python.org/3/reference/expressions.html _prec_lambda = 2 _prec_or = 4 _prec_and = 6 @@ -1538,8 +1497,6 @@ def __truediv__(self, other): """ return self._sie_binop('/', other) - __div__ = __truediv__ - def __add__(self, other): r""" Compute an expression tree for ``self + other``. @@ -1602,6 +1559,20 @@ def __neg__(self): """ return self._sie_unop('-') + def __pos__(self): + r""" + Compute an expression tree for ``+self``. + + EXAMPLES:: + + sage: from sage.misc.sage_input import SageInputBuilder + sage: sib = SageInputBuilder() + sage: sie = sib(3) + sage: +sie + {unop:+ {atomic:3}} + """ + return self._sie_unop('+') + def __invert__(self): r""" Compute an expression tree for ``~self``. @@ -1896,7 +1867,7 @@ def __repr__(self): func = repr(self._sie_func) args = [repr(arg) for arg in self._sie_args] kwargs = sorted(k + '=' + repr(v) - for k, v in iteritems(self._sie_kwargs)) + for k, v in self._sie_kwargs.items()) all_args = ', '.join(args + kwargs) return "{call: %s(%s)}" % (func, all_args) @@ -1937,7 +1908,7 @@ def _sie_format(self, sif): func = sif.format(self._sie_func, _prec_attribute) args = [sif.format(arg, 0) for arg in self._sie_args] kwargs = sorted(k + '=' + sif.format(v, 0) - for k, v in iteritems(self._sie_kwargs)) + for k, v in self._sie_kwargs.items()) all_args = ', '.join(args + kwargs) return ('%s(%s)' % (func, all_args), _prec_funcall) @@ -2593,6 +2564,16 @@ def _sie_format(self, sif): --x sage: sib.result(x-(-y)) x - -y + sage: sib.result((+x)+y) + +x + y + sage: sib.result(x+(+y)) + x + +y + sage: sib.result(+(x+y)) + +(x + y) + sage: sib.result(+(+x)) + ++x + sage: sib.result(x+(+y)) + x + +y We assume that -(x*y) is always equal to (-x)*y. Using this assumption, we print -(x*y) as -x*y, which parses as (-x)*y.:: @@ -2603,6 +2584,12 @@ def _sie_format(self, sif): -x*y sage: sib.result(x*(-y)) x*-y + + We do not do that for unary +, assuming that the user really + means to express something by using unary +:: + + sage: sib.result(+(x*y)) + +(x*y) """ op = self._sie_op fop = op @@ -2612,6 +2599,8 @@ def _sie_format(self, sif): # (-a)*b. prec = _prec_muldiv rprec = _prec_negate + elif op == '+': + prec = _prec_negate elif op == '~': prec = _prec_bitnot else: @@ -3603,5 +3592,5 @@ def __repr__(self): locals = self[2] locals_text = ''.join(' %s: %r\n' % (k, v) - for k, v in iteritems(locals)) + for k, v in locals.items()) return 'LOCALS:\n' + locals_text + self[0] + self[1] diff --git a/src/sage/misc/sage_ostools.pyx b/src/sage/misc/sage_ostools.pyx index a272097876f..37fd4cf18b8 100644 --- a/src/sage/misc/sage_ostools.pyx +++ b/src/sage/misc/sage_ostools.pyx @@ -8,6 +8,7 @@ from cpython.exc cimport PyErr_SetFromErrno import os import contextlib + def have_program(program, path=None): """ Return ``True`` if a ``program`` executable is found in the path @@ -65,7 +66,7 @@ def restore_cwd(chdir=None): sage: from sage.misc.misc import SAGE_TMP sage: cwd = os.getcwd() sage: with restore_cwd(str(SAGE_TMP)): - ....: print(os.getcwd() == SAGE_TMP) + ....: print(os.getcwd() == os.path.realpath(SAGE_TMP)) True sage: cwd == os.getcwd() True @@ -183,12 +184,8 @@ cdef class redirection: TESTS:: - sage: from six.moves import cStringIO as StringIO - sage: redirection(sys.stdout, StringIO()) # py2 - Traceback (most recent call last): - ... - TypeError: <...> must be a Python file or an integer - sage: redirection(sys.stdout, StringIO()) # py3 + sage: import io + sage: redirection(sys.stdout, io.StringIO()) Traceback (most recent call last): ... io.UnsupportedOperation: fileno @@ -306,3 +303,43 @@ cdef class redirection: if self.close_dest: self.dest_file.close() self.dest_fd = -1 + + +IF PY_PLATFORM == 'cygwin': + from libc.stddef cimport wchar_t + + cdef extern from "Windows.h": + int SetDllDirectoryW(wchar_t* lpPathName) + + cdef extern from "sqlite3.h": + int sqlite3_initialize() + + def fix_for_ticket_30157(): + """ + Cygwin-only workaround for an issue caused by the sqlite3 library. See + trac:`30157`. + + The issue here is that when the sqlite3 library is first initialized + it modifies Windows' default DLL search path order, which can possibly + break the correct search path for subsequent DLL loads. + + This workaround ensures that the sqlite3 library is initialized very + early on (this does not add any significant overhead) and then + immediately undoes its deleterious effects. In particular, calling + SetDllDirectoryW(NULL) restores the default DLL search path. + + To be clear, there's no reason sqlite3 needs this to function + correctly; it's just a poorly-considered hack that attempted to work + around a problem that doesn't affect us. + + Returns 0 if it succeeeds or a non-zero value if not. + """ + + ret = sqlite3_initialize() + + if ret != 0: + # Library initialization failed for some reason + return ret + + # SetDllDirectory returns 1 if it succeeds. + return not SetDllDirectoryW(NULL) diff --git a/src/sage/misc/sage_timeit.py b/src/sage/misc/sage_timeit.py index f6b33cacad9..f82300651dc 100644 --- a/src/sage/misc/sage_timeit.py +++ b/src/sage/misc/sage_timeit.py @@ -17,8 +17,6 @@ -- William Stein, based on code by Fernando Perez included in IPython """ -import six - class SageTimeitResult(object): r""" @@ -89,9 +87,13 @@ def __repr__(self): sage: from sage.misc.sage_timeit import SageTimeitResult sage: stats = (1, 2, int(3), pi, 'ns') sage: SageTimeitResult(stats) #indirect doctest - 1 loops, best of 2: 3.14 ns per loop + 1 loop, best of 2: 3.14 ns per loop """ - s = u"%d loops, best of %d: %.*g %s per loop" % self.stats + if self.stats[0] > 1: + s = u"%d loops, best of %d: %.*g %s per loop" % self.stats + else: + s = u"%d loop, best of %d: %.*g %s per loop" % self.stats + if isinstance(s, str): return s return s.encode("utf-8") @@ -199,7 +201,7 @@ def sage_timeit(stmt, globals_dict=None, preparse=None, number=0, repeat=3, prec sage: gc.isenabled() True """ - import time, math + import math import timeit as timeit_ import sage.repl.interpreter as interpreter @@ -224,12 +226,8 @@ def sage_timeit(stmt, globals_dict=None, preparse=None, number=0, repeat=3, prec # but is there a better way to achieve that the code stmt has access # to the shell namespace? - if six.PY2: - src = timeit_.template % {'stmt': timeit_.reindent(stmt, 8), - 'setup': "pass", 'init': ''} - else: - src = timeit_.template.format(stmt=timeit_.reindent(stmt, 8), - setup="pass", init='') + src = timeit_.template.format(stmt=timeit_.reindent(stmt, 8), + setup="pass", init='') code = compile(src, "", "exec") ns = {} if not globals_dict: @@ -269,3 +267,4 @@ def sage_timeit(stmt, globals_dict=None, preparse=None, number=0, repeat=3, prec order = 3 stats = (number, repeat, precision, best * scaling[order], units[order]) return SageTimeitResult(stats,series=series) + diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index e7e5c1af8e8..a6a1b95a6d7 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -24,27 +24,28 @@ ....: for line in fobj: ....: if "#sage.symbolic.expression.Expression.numerical_approx" in line: ....: print(line) - numerical_approx(prec=None, digits=None, algorithm=None)... + numerical_approx(prec=None, digits=None, algorithm=None)... Check that sphinx is not imported at Sage start-up:: sage: "sphinx" in sys.modules False """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from __future__ import print_function from __future__ import absolute_import -from six import string_types, text_type -import os, re, sys +import os +import re +import sys import pydoc from sage.misc.temporary_file import tmp_dir from .viewer import browser @@ -62,6 +63,19 @@ # the strings raw: r'\\blah'. math_substitutes = [ (r'\\to', '-->'), + (r'\\rightarrow', '-->'), + (r'\\leftarrow', '<--'), + (r'\\leftrightarrow', '<->'), + (r'\\longrightarrow', '--->'), + (r'\\longleftarrow', '<---'), + (r'\\longleftrightarrow', '<-->'), + (r'\\Rightarrow', '==>'), + (r'\\Leftarrow', '<=='), + (r'\\Leftrightarrow', '<=>'), + (r'\\Longrightarrow', '===>'), + (r'\\Longleftarrow', '<==='), + (r'\\Longleftrightarrow', '<==>'), + (r'\\colon', ':'), (r'\\left', ''), (r'\\right', ''), (r'\\bigl', ''), @@ -79,6 +93,7 @@ (r'\\times', ' x'), (r'\\backslash','\\'), (r'\\mapsto', ' |--> '), + (r'\\longmapsto', ' |---> '), (r'\\lvert', '|'), (r'\\rvert', '|'), (r'\\mid', '|'), @@ -205,8 +220,8 @@ def detex(s, embedded=False): '`a, b, c, \\ldots, z`' sage: detex(r'`\left(\lvert x\ast y \rvert\right]`') '(| x * y |]\n' - sage: detex(r'`\left(\leq\le\leftarrow \rightarrow\to`') - '(<=<=leftarrow rightarrow-->\n' + sage: detex(r'`\left(\leq\le\leftarrow \rightarrow\unknownmacro\to`') + '(<=<=<-- -->\\unknownmacro-->\n' """ s = _rmcmd(s, 'url') s = _rmcmd(s, 'code') @@ -234,7 +249,6 @@ def detex(s, embedded=False): # test to make sure the next character is not a letter. for a,b in math_substitutes: s = re.sub(a+'([^a-zA-Z])', b+'\\1', s) - s = s.replace('\\','') # nuke backslashes return s def skip_TESTS_block(docstring): @@ -245,7 +259,7 @@ def skip_TESTS_block(docstring): - ``docstring``, a string - A "TESTS" block is a block starting with "TEST:" or "TESTS:" (or + A "TESTS" block is a block starting "TESTS:" (or the same with two colons), on a line on its own, and ending either with a line indented less than "TESTS", or with a line with the same level of indentation -- not more -- matching one of the @@ -271,7 +285,7 @@ def skip_TESTS_block(docstring): sage: from sage.misc.sagedoc import skip_TESTS_block sage: start = ' Docstring\n\n' - sage: test = ' TEST: \n\n Here is a test::\n sage: 2+2 \n 5 \n\n' + sage: test = ' TESTS: \n\n Here is a test::\n sage: 2+2 \n 5 \n\n' sage: test2 = ' TESTS:: \n\n sage: 2+2 \n 6 \n\n' Test lines starting with "REFERENCES:":: @@ -658,8 +672,17 @@ def format(s, embedded=False): Return the n x n identity matrix over the given ring. ... + Check that backslashes are preserved in code blocks (:trac:`29140`):: + + sage: format('::\n' + ....: '\n' + ....: r' sage: print(r"\\\\.")' '\n' + ....: r' \\\\.') + ' sage: print(r"\\\\\\\\.")\n \\\\\\\\.\n' + sage: format(r'inline code ``\\\\.``') + 'inline code "\\\\\\\\."\n' """ - if not isinstance(s, string_types): + if not isinstance(s, str): raise TypeError("s must be a string") # Leading empty lines must be removed, since we search for directives @@ -688,9 +711,11 @@ def format(s, embedded=False): i_0 = 0 while True: i = s[i_0:].find("<<<") - if i == -1: break + if i == -1: + break j = s[i_0+i+3:].find('>>>') - if j == -1: break + if j == -1: + break obj = s[i_0+i+3 : i_0+i+3+j] if obj in docs: t = '' @@ -741,15 +766,17 @@ def format_src(s): sage: format_src('<<>>')[5:15] 'Sq(*nums):' """ - if not isinstance(s, string_types): + if not isinstance(s, str): raise TypeError("s must be a string") docs = set([]) import sage.all while True: i = s.find("<<<") - if i == -1: break + if i == -1: + break j = s[i+3:].find('>>>') - if j == -1: break + if j == -1: + break obj = s[i+3:i+3+j] if obj in docs: t = '' @@ -926,7 +953,7 @@ def _search_src_or_doc(what, string, extra1='', extra2='', extra3='', else: # Pass through the IPython pager in a mime bundle from IPython.core.page import page - if not isinstance(text_results, text_type): + if not isinstance(text_results, str): text_results = text_results.decode('utf-8', 'replace') page({ @@ -1037,7 +1064,7 @@ def search_src(string, extra1='', extra2='', extra3='', extra4='', sage: print(search_src(" fetch(", "def", interact=False)) # py3 Traceback (most recent call last): ... - re.error: missing ), unterminated subpattern at position 6 + error: missing ), unterminated subpattern at position 6 To fix this, *escape* the parenthesis with a backslash:: diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index c5310a456ae..8e73143fe89 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -25,8 +25,8 @@ sage: sage_getdoc(sage.rings.rational).lstrip() 'Rational Numbers...' - sage: sage_getsource(sage.rings.rational)[5:] - 'Rational Numbers...' + sage: sage_getsource(sage.rings.rational) + '# distutils: ...Rational Numbers...' Python modules:: @@ -114,15 +114,10 @@ def foo(unsigned int x=1, a=')"', b={not (2+1==3):'bar'}, *args, **kwds): return """ from __future__ import print_function, absolute_import -import six -from six import iteritems, string_types, class_types -from six.moves import range - import ast import inspect import functools import os -import sys import tokenize import re EMBEDDED_MODE = False @@ -146,14 +141,8 @@ def loadable_module_extension(): sage: sage.structure.sage_object.__file__.endswith(loadable_module_extension()) True """ - if six.PY2: - if sys.platform == 'cygwin': - return os.path.extsep + 'dll' - else: - return os.path.extsep + 'so' - else: - # Return the full platform-specific extension module suffix - return import_machinery.EXTENSION_SUFFIXES[0] + # Return the full platform-specific extension module suffix + return import_machinery.EXTENSION_SUFFIXES[0] def isclassinstance(obj): @@ -167,9 +156,9 @@ def isclassinstance(obj): sage: from sage.misc.sageinspect import isclassinstance sage: isclassinstance(int) False - sage: isclassinstance(FreeModule) - True sage: class myclass: pass + sage: isclassinstance(myclass()) + True sage: isclassinstance(myclass) False sage: class mymetaclass(type): pass @@ -377,12 +366,8 @@ def _getblock(lines): """ blockfinder = BlockFinder() iter_lines = iter(lines) - if six.PY2: - tokenizer = tokenize.generate_tokens - readline = lambda: next(iter_lines) - else: - tokenizer = tokenize.tokenize - readline = lambda: next(iter_lines).encode('utf-8') + tokenizer = tokenize.tokenize + readline = lambda: next(iter_lines).encode('utf-8') try: for tok in tokenizer(readline): blockfinder.tokeneater(*tok) @@ -413,7 +398,7 @@ def _extract_source(lines, lineno): raise ValueError("Line numbering starts at 1! (tried to extract line {})".format(lineno)) lineno -= 1 - if isinstance(lines, string_types): + if isinstance(lines, str): lines = lines.splitlines(True) # true keeps the '\n' if len(lines): # Fixes an issue with getblock @@ -475,73 +460,64 @@ def visit_Name(self, node): sage: [type(vis(n)) for n in ['foo', 'bar']] # py3 [, ] """ - if six.PY2: - what = node.id - if what == 'None': - return None - elif what == 'True': - return True - elif what == 'False': - return False return node.id - if six.PY3: - def visit_NameConstant(self, node): - """ - Visit a Python AST :class:`ast.NameConstant` node. + def visit_NameConstant(self, node): + """ + Visit a Python AST :class:`ast.NameConstant` node. - This is an optimization added in Python 3.4 for the special cases - of True, False, and None. + This is an optimization added in Python 3.4 for the special cases + of True, False, and None. - INPUT: + INPUT: - - ``node`` - the node instance to visit + - ``node`` - the node instance to visit - OUTPUT: + OUTPUT: - - None, True, False. + - None, True, False. - EXAMPLES:: + EXAMPLES:: - sage: import ast, sage.misc.sageinspect as sms # py3 - sage: visitor = sms.SageArgSpecVisitor() # py3 - sage: vis = lambda x: visitor.visit_NameConstant(ast.parse(x).body[0].value) # py3 - sage: [vis(n) for n in ['True', 'False', 'None']] # py3 - [True, False, None] - sage: [type(vis(n)) for n in ['True', 'False', 'None']] # py3 - [, , ] - """ + sage: import ast, sage.misc.sageinspect as sms # py3 + sage: visitor = sms.SageArgSpecVisitor() # py3 + sage: vis = lambda x: visitor.visit_NameConstant(ast.parse(x).body[0].value) # py3 + sage: [vis(n) for n in ['True', 'False', 'None']] # py3 + [True, False, None] + sage: [type(vis(n)) for n in ['True', 'False', 'None']] # py3 + [, , ] + """ - return node.value + return node.value - def visit_arg(self, node): - r""" - Visit a Python AST :class:`ast.arg` node. + def visit_arg(self, node): + r""" + Visit a Python AST :class:`ast.arg` node. - This node type is only on Python 3, where function arguments are - more complex than just an identifier (e.g. they may also include - annotations). + This node type is only on Python 3, where function arguments are + more complex than just an identifier (e.g. they may also include + annotations). - For now we simply return the argument identifier as a string. + For now we simply return the argument identifier as a string. - INPUT: + INPUT: - - ``node`` -- the node instance to visit + - ``node`` -- the node instance to visit - OUTPUT: + OUTPUT: - the argument name + the argument name - EXAMPLES:: + EXAMPLES:: - sage: import ast, sage.misc.sageinspect as sms # py3 - sage: s = "def f(a, b=2, c={'a': [4, 5.5, False]}, d=(None, True)):\n return" # py3 - sage: visitor = sms.SageArgSpecVisitor() # py3 - sage: args = ast.parse(s).body[0].args.args # py3 - sage: [visitor.visit_arg(n) for n in args] # py3 - ['a', 'b', 'c', 'd'] - """ - return node.arg + sage: import ast, sage.misc.sageinspect as sms # py3 + sage: s = "def f(a, b=2, c={'a': [4, 5.5, False]}, d=(None, True)):\n return" # py3 + sage: visitor = sms.SageArgSpecVisitor() # py3 + sage: args = ast.parse(s).body[0].args.args # py3 + sage: [visitor.visit_arg(n) for n in args] # py3 + ['a', 'b', 'c', 'd'] + """ + return node.arg def visit_Num(self, node): """ @@ -1104,13 +1080,9 @@ def _sage_getargspec_from_ast(source): args = [visitor.visit(a) for a in ast_args.args] defaults = [visitor.visit(d) for d in ast_args.defaults] - if six.PY2: - vararg = ast_args.vararg - kwarg = ast_args.kwarg - else: - # vararg and kwarg may be None - vararg = getattr(ast_args.vararg, 'arg', None) - kwarg = getattr(ast_args.kwarg, 'arg', None) + # vararg and kwarg may be None + vararg = getattr(ast_args.vararg, 'arg', None) + kwarg = getattr(ast_args.kwarg, 'arg', None) return inspect.ArgSpec(args, vararg, kwarg, tuple(defaults) if defaults else None) @@ -1693,7 +1665,6 @@ def formatannotation(annotation, base_module=None): sage: import inspect sage: def foo(a, *, b:int, **kwargs): # py3 ....: pass - ....: sage: s = inspect.signature(foo) # py3 sage: a = s.parameters['a'].annotation # py3 @@ -1752,10 +1723,8 @@ def sage_formatargspec(args, varargs=None, varkw=None, defaults=None, sage: defaults = [3] sage: sage_formatargspec(args, defaults=defaults) '(a, b, c=3)' - sage: formatargspec(args, defaults=defaults) == sage_formatargspec(args, defaults=defaults) # py2 - True - sage: formatargspec(args, defaults=defaults) == sage_formatargspec(args, defaults=defaults) # py3 - doctest:...: DeprecationWarning: `formatargspec` is deprecated since Python 3.5. Use `signature` and the `Signature` object directly + sage: import warnings; warnings.simplefilter('ignore') # py3: ignore DeprecationWarning + sage: formatargspec(args, defaults=defaults) == sage_formatargspec(args, defaults=defaults) True """ def formatargandannotation(arg): @@ -1922,7 +1891,7 @@ def _sage_getdoc_unformatted(obj): # Check if the __doc__ attribute was actually a string, and # not a 'getset_descriptor' or similar. - if isinstance(r, string_types): + if isinstance(r, str): return r else: # Not a string of any kind @@ -1999,7 +1968,7 @@ def sage_getdoc_original(obj): """ # typ is the type corresponding to obj, which is obj itself if # that was a type or old-style class - if isinstance(obj, class_types): + if isinstance(obj, type): typ = obj else: typ = type(obj) @@ -2511,7 +2480,7 @@ def sage_getvariablename(self, omit_underscore_names=True): """ result = [] for frame in inspect.stack(): - for name, obj in iteritems(frame[0].f_globals): + for name, obj in frame[0].f_globals.items(): if obj is self: result.append(name) if len(result) == 1: diff --git a/src/sage/misc/session.pyx b/src/sage/misc/session.pyx index 9e600373a49..2036e7f3db5 100644 --- a/src/sage/misc/session.pyx +++ b/src/sage/misc/session.pyx @@ -59,11 +59,11 @@ AUTHOR: ############################################################################# # Standard python imports +import builtins import os import types # We want the caller's locals, but locals() is emulated in Cython -from six.moves import builtins cdef caller_locals = builtins.locals # Sage imports diff --git a/src/sage/misc/six.py b/src/sage/misc/six.py deleted file mode 100644 index 30c34ef9074..00000000000 --- a/src/sage/misc/six.py +++ /dev/null @@ -1,100 +0,0 @@ -# -*- encoding: utf-8 -*- -""" -Python 2 and 3 Compatibility - -TESTS: - -Test the functionality of ``six.with_metaclass`` and various issues -which came up with it. Sage used to have a custom version of -``with_metaclass``, but this is now fixed upstream. :: - - sage: from six import with_metaclass - sage: class Meta(type): pass - sage: class X(with_metaclass(Meta)): pass - sage: type(X) is Meta - True - sage: issubclass(X, object) - True - sage: class Base(object): pass - sage: class X(with_metaclass(Meta, Base)): pass - sage: type(X) is Meta - True - sage: issubclass(X, Base) - True - sage: class Base2(object): pass - sage: class X(with_metaclass(Meta, Base, Base2)): pass - sage: type(X) is Meta - True - sage: issubclass(X, Base) - True - sage: issubclass(X, Base2) - True - sage: X.__mro__ == (X, Base, Base2, object) or X.__mro__ - True - -Check that :trac:`18503` is fixed, i.e. that with_metaclass -works with cdef'ed metaclasses:: - - sage: from sage.misc.classcall_metaclass import ClasscallMetaclass - sage: class X(with_metaclass(ClasscallMetaclass)): pass - sage: type(X) is ClasscallMetaclass - True - sage: X.__mro__ == (X, object) or X.__mro__ - True - -Check a fix for :trac:`16074`:: - - sage: from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass - sage: from sage.modules.with_basis.morphism import ModuleMorphismByLinearity - sage: from sage.structure.unique_representation import UniqueRepresentation - sage: class ExteriorAlgebraDifferential(with_metaclass( - ....: InheritComparisonClasscallMetaclass, - ....: ModuleMorphismByLinearity, UniqueRepresentation - ....: )): - ....: pass -""" - -#***************************************************************************** -# Copyright (C) 2017 Jeroen Demeyer -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from __future__ import absolute_import - -from six import * - - -def u(x): - r""" - Convert `x` to unicode, assuming UTF-8 encoding. - - Python2 behaviour: - - If input is unicode, returns the input. - - If input is str (assumed to be utf-8 encoded), convert to unicode. - - Python3 behaviour: - - If input is str, returns the input. - - If input is bytes (assumed to be utf-8 encoded), convert to unicode. - - EXAMPLES:: - - sage: from sage.misc.six import u - sage: u("500 €") - u'500 \u20ac' - sage: u(u"500 \u20ac") - u'500 \u20ac' - """ - if isinstance(x, text_type): # py2 unicode and py3 str - return x - if isinstance(x, bytes): - return x.decode("utf-8") - raise TypeError('input has no conversion to unicode') diff --git a/src/sage/misc/sphinxify.py b/src/sage/misc/sphinxify.py index b9e630f4d95..d3db30be44b 100644 --- a/src/sage/misc/sphinxify.py +++ b/src/sage/misc/sphinxify.py @@ -22,9 +22,11 @@ # **************************************************************************** from __future__ import absolute_import, print_function +import builtins import os import re import shutil +import sys from tempfile import mkdtemp from sphinx.application import Sphinx @@ -105,8 +107,8 @@ def sphinxify(docstring, format='html'): """) staticdir = os.path.join(confdir, 'static') os.makedirs(staticdir) - with open(os.path.join(staticdir, 'empty'), 'w') as filed: pass - + with open(os.path.join(staticdir, 'empty'), 'w') as filed: + pass with open(os.path.join(srcdir, 'docutils.conf'), 'w') as filed: filed.write(r""" [parsers] @@ -114,7 +116,6 @@ def sphinxify(docstring, format='html'): doctreedir = os.path.join(srcdir, 'doctrees') confoverrides = {'html_context': {}, 'master_doc': 'docstring'} - import sys old_sys_path = list(sys.path) # Sphinx modifies sys.path # Sphinx constructor: Sphinx(srcdir, confdir, outdir, doctreedir, # buildername, confoverrides, status, warning, freshenv). @@ -124,7 +125,6 @@ def sphinxify(docstring, format='html'): sys.path = old_sys_path # We need to remove "_" from __builtin__ that the gettext module installs - from six.moves import builtins builtins.__dict__.pop('_', None) if os.path.exists(output_name): @@ -158,7 +158,6 @@ def sphinxify(docstring, format='html'): if __name__ == '__main__': - import sys if len(sys.argv) == 2: print(sphinxify(sys.argv[1])) else: diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index 8392b471034..a7c30d297ff 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -23,7 +23,6 @@ # https://www.gnu.org/licenses/ ######################################################################## from __future__ import print_function, absolute_import -from six import iteritems from warnings import warn import inspect @@ -53,7 +52,6 @@ def _check_trac_number(trac_number): ... ValueError: 0 is not a valid trac issue number sage: _check_trac_number(int(10)) - sage: _check_trac_number(long(1000)) sage: _check_trac_number(10.0) Traceback (most recent call last): ... @@ -374,13 +372,12 @@ def __name__(self): ....: r" return 1", ....: r" old_cython_meth = deprecated_function_alias(13109, new_cython_meth)" ....: ])) - ....: sage: cython_cls().old_cython_meth.__name__ 'old_cython_meth' """ # first look through variables in stack frames for frame in inspect.stack(): - for name, obj in iteritems(frame[0].f_globals): + for name, obj in frame[0].f_globals.items(): if obj is self: return name # then search object that contains self as method @@ -396,7 +393,7 @@ def is_class(gc_ref): for ref in gc.get_referrers(search_for): if is_class(ref) and ref is not self.__dict__: ref_copy = copy.copy(ref) - for key, val in iteritems(ref_copy): + for key, val in ref_copy.items(): if val is search_for: return key raise AttributeError("The name of this deprecated function can not be determined") diff --git a/src/sage/misc/table.py b/src/sage/misc/table.py index 18fdc1c1be7..cdb7d23f865 100644 --- a/src/sage/misc/table.py +++ b/src/sage/misc/table.py @@ -10,8 +10,8 @@ - John H. Palmieri (2012-11) """ from __future__ import absolute_import -from six.moves import cStringIO as StringIO -from six.moves import range, zip + +from io import StringIO from sage.structure.sage_object import SageObject from sage.misc.cachefunc import cached_method @@ -774,7 +774,7 @@ def _html_table_row(self, file, row, header=False): EXAMPLES:: sage: T = table([['a', 'bb', 'ccccc'], [10, -12, 0], [1, 2, 3]]) - sage: from six import StringIO + sage: from io import StringIO sage: s = StringIO() sage: T._html_table_row(s, ['a', 2, '$x$']) sage: print(s.getvalue()) diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index da8e199eb88..91bd88642b5 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -10,23 +10,21 @@ - Jeroen Demeyer (2013-03-17): add :class:`atomic_write`, see :trac:`14292`. """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Volker Braun # Copyright (C) 2012 Jeroen Demeyer # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from __future__ import print_function import io import os import tempfile import atexit -import six def delete_tmpfiles(): @@ -153,59 +151,6 @@ def tmp_filename(name="tmp_", ext=""): return name -def graphics_filename(ext='.png'): - """ - Deprecated SageNB graphics filename - - You should just use :meth:`tmp_filename`. - - When run from the Sage notebook, return the next available canonical - filename for a plot/graphics file in the current working directory. - Otherwise, return a temporary file inside ``SAGE_TMP``. - - INPUT: - - - ``ext`` -- (default: ``".png"``) A file extension (including the dot) - for the filename. - - OUTPUT: - - The path of the temporary file created. In the notebook, this is - a filename without path in the current directory. Otherwise, this - an absolute path. - - EXAMPLES:: - - sage: from sage.misc.temporary_file import graphics_filename - sage: print(graphics_filename()) # random, typical filename for sagenb - sage0.png - - TESTS: - - When doctesting, this returns instead a random temporary file. - We check that it's a file inside ``SAGE_TMP`` and that the extension - is correct:: - - sage: fn = graphics_filename(ext=".jpeg") - sage: fn.startswith(str(SAGE_TMP)) - True - sage: fn.endswith('.jpeg') - True - """ - import sage.plot.plot - if sage.plot.plot.EMBEDDED_MODE: - # Don't use this unsafe function except in the notebook, #15515 - i = 0 - while os.path.exists('sage%d%s'%(i,ext)): - i += 1 - filename = 'sage%d%s'%(i,ext) - return filename - else: - from sage.misc.superseded import deprecation - deprecation(17234,'use tmp_filename instead') - return tmp_filename(ext=ext) - - ################################################################# # write to a temporary file and move it in place ################################################################# @@ -397,7 +342,7 @@ def __init__(self, target_filename, append=False, mode=0o666, # 'binary' mode is the default on Python 2, whereas 'text' mode is the # default on Python 3--this reflects consistent handling of the default # str type on the two platforms - self.binary = six.PY2 if binary is None else binary + self.binary = False if binary is None else binary self.kwargs = kwargs def __enter__(self): diff --git a/src/sage/misc/test_class_pickling.py b/src/sage/misc/test_class_pickling.py index ba9c1e625cc..e03c911c054 100644 --- a/src/sage/misc/test_class_pickling.py +++ b/src/sage/misc/test_class_pickling.py @@ -1,5 +1,6 @@ from __future__ import absolute_import -from six.moves import copyreg + +import copyreg class bar: @@ -49,10 +50,10 @@ class Metaclass(type): sage: from sage.misc.test_class_pickling import metaclass, bar sage: c = metaclass("foo", (bar, object)) constructing class - sage: from six.moves import cPickle - sage: s = cPickle.dumps(c) + sage: import pickle + sage: s = pickle.dumps(c) reducing a class - sage: c2 = cPickle.loads(s) + sage: c2 = pickle.loads(s) constructing class sage: c == c2 calling __eq__ defined in Metaclass diff --git a/src/sage/misc/unknown.py b/src/sage/misc/unknown.py index 3971488da5a..faa4cf94b48 100644 --- a/src/sage/misc/unknown.py +++ b/src/sage/misc/unknown.py @@ -1,55 +1,124 @@ """ The Unknown truth value +The ``Unknown`` object is used in Sage in several places as return value +in addition to ``True`` and ``False``, in order to signal uncertainty +about or inability to compute the result. ``Unknown`` can be identified +using ``is``, or by catching :class:`UnknownError` from a boolean operation. + +.. WARNING:: + + Calling ``bool()`` with ``Unknown`` as argument will throw an + ``UnknownError``. This also means that in the following cases, + ``and``, ``not``, and ``or`` fail or return a somewhat wrong value:: + + sage: not Unknown # should return Unknown + Traceback (most recent call last): + ... + UnknownError: Unknown does not evaluate in boolean context + sage: Unknown and False # should return False + Traceback (most recent call last): + ... + UnknownError: Unknown does not evaluate in boolean context + sage: Unknown or False # should return Unknown + Traceback (most recent call last): + ... + UnknownError: Unknown does not evaluate in boolean context + +EXAMPLES:: + + sage: def func(n): + ....: if n > 0: + ....: return True + ....: elif n < 0: + ....: return False + ....: else: + ....: return Unknown + +Using direct identification:: + + sage: for n in [-3, 0, 12]: + ....: res = func(n) + ....: if res is True: + ....: print("n={} is positive".format(n)) + ....: elif res is False: + ....: print("n={} is negative".format(n)) + ....: else: + ....: print("n={} is neither positive nor negative".format(n)) + n=-3 is negative + n=0 is neither positive nor negative + n=12 is positive + +Using ``UnknownError``:: + + sage: for n in [-3, 0, 12]: + ....: try: + ....: if func(n): + ....: print("n={} is positive".format(n)) + ....: else: + ....: print("n={} is negative".format(n)) + ....: except UnknownError: + ....: print("n={} is neither positive nor negative".format(n)) + n=-3 is negative + n=0 is neither positive nor negative + n=12 is positive + AUTHORS: - Florent Hivert (2010): initial version. +- Ralf Stephan, Vincent Delecroix (2018-2020): redesign """ +# **************************************************************************** +# Copyright (C) 2010 Florent Hivert +# 2018-2020 Ralf Stefan +# 2018-2020 Vincent Delecroix <20100.delecroix@gmail.com> +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + from __future__ import print_function -from sage.structure.sage_object import SageObject from sage.structure.unique_representation import UniqueRepresentation from sage.structure.richcmp import richcmp_method, rich_to_bool - -@richcmp_method -class UnknownClass(UniqueRepresentation, SageObject): +class UnknownError(TypeError): """ - TESTS:: + Raised whenever :class:`Unknown` is used in a boolean operation. - sage: TestSuite(Unknown).run() - """ - def __init__(self): - """ - The ``Unknown`` truth value + EXAMPLES:: - EXAMPLES:: + sage: not Unknown + Traceback (most recent call last): + ... + UnknownError: Unknown does not evaluate in boolean context + """ + pass - sage: l = [False, Unknown, True] - sage: for a in l: print([a and b for b in l]) - [False, False, False] - [Unknown, Unknown, Unknown] - [False, Unknown, True] +@richcmp_method +class UnknownClass(UniqueRepresentation): + """ + The Unknown truth value - sage: for a in l: print([a or b for b in l]) - [False, Unknown, True] - [False, Unknown, True] - [True, True, True] + The ``Unknown`` object is used in Sage in several places as return value + in addition to ``True`` and ``False``, in order to signal uncertainty + about or inability to compute the result. ``Unknown`` can be identified + using ``is``, or by catching :class:`UnknownError` from a boolean + operation. - .. WARNING:: + .. WARNING:: - Unless PEP 335 is accepted, in the following cases, - ``and``, ``not`` and ``or`` return a somewhat wrong value:: + Calling ``bool()`` with ``Unknown`` as argument will throw an + ``UnknownError``. This also means that applying ``and``, ``not``, + and ``or`` to ``Unknown`` might fail. - sage: not Unknown # should return Unknown - True - sage: Unknown and False # should return False - Unknown - sage: Unknown or False # should return Unknown - False - """ + TESTS:: - def _repr_(self): + sage: TestSuite(Unknown).run() + """ + def __repr__(self): """ TESTS:: @@ -60,27 +129,26 @@ def _repr_(self): def __bool__(self): """ - When evaluated in a boolean context ``Unknown()`` is evaluated into - ``False``. + When evaluated in a boolean context ``Unknown`` raises a ``UnknownError``. EXAMPLES:: sage: bool(Unknown) - False + Traceback (most recent call last): + ... + UnknownError: Unknown does not evaluate in boolean context sage: not Unknown - True + Traceback (most recent call last): + ... + UnknownError: Unknown does not evaluate in boolean context """ - return False + raise UnknownError('Unknown does not evaluate in boolean context') __nonzero__ = __bool__ def __and__(self, other): """ - The ``and`` logical connector. - - .. WARNING:: - - This is not used by ``and`` unless PEP 335 is accepted. + The ``&`` logical operation. EXAMPLES:: @@ -91,23 +159,19 @@ def __and__(self, other): sage: Unknown & True Unknown - Compare with:: - - sage: Unknown and False # should return False - Unknown + sage: Unknown.__or__(3) + NotImplemented """ if other is False: return False - else: + elif other is True or other is Unknown: return self + else: + return NotImplemented def __or__(self, other): """ - The ``or`` logical connector. - - .. WARNING:: - - This is not used by ``or`` unless PEP 335 is accepted. + The ``|`` logical connector. EXAMPLES:: @@ -118,35 +182,15 @@ def __or__(self, other): sage: Unknown | True True - Compare with:: - - sage: Unknown or False # should return Unknown - False + sage: Unknown.__or__(3) + NotImplemented """ - if other: + if other is True: return True - else: + elif other is False or other is Unknown: return self - - def __not__(self): - """ - The ``not`` logical connector. - - .. WARNING:: - - This is not used by ``not`` unless PEP 335 is accepted. - - EXAMPLES:: - - sage: Unknown.__not__() - Unknown - - Compare with:: - - sage: not Unknown # should return Unknown - True - """ - return self + else: + return NotImplemented def __richcmp__(self, other, op): """ diff --git a/src/sage/misc/verbose.py b/src/sage/misc/verbose.py new file mode 100644 index 00000000000..b7016d08116 --- /dev/null +++ b/src/sage/misc/verbose.py @@ -0,0 +1,269 @@ +# -*- coding: utf-8 -*- +r""" +Verbosity System and Logging in SageMath + +Howto: Logging +============== + +Using Python's Logging Module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Import it:: + + sage: import logging + sage: logging.basicConfig() # only needed once + +Setting the level:: + + sage: logging.getLogger().setLevel(logging.INFO) + +Log something:: + + sage: logger = logging.getLogger(__name__) + sage: logger.info('Hello. I am talking to you.') + INFO:__main__:Hello. I am talking to you. + +If we haven't set the logging level to ``logging.INFO``, then the previous +wouldn't have been shown. +:: + + sage: logger.debug('Hello. I am really talking a lot.') + +The latter is not shown as the current logging level is only +``logging.INFO`` and not ``logging.DEBUG``. + +Reset the level:: + + sage: logging.getLogger().setLevel(logging.WARNING) + +Warnings are still shown at this default level (``logging.WARNING``):: + + sage: logger.warning('Hello. I am warning you.') + WARNING:__main__:Hello. I am warning you. + +And that's all. + +There are a lot more features, see +:python:`Logging facility for Python`. + + +Using SageMath's Verbosity System +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Alternatively, this module provides +:func:`verbose`, :func:`set_verbose`, :func:`get_verbose` which can +be used as follows:: + + sage: from sage.misc.verbose import verbose, set_verbose, get_verbose + sage: set_verbose(1) + sage: t = verbose("This is SageMath.", level=0) + verbose 0 () This is SageMath. + sage: t = verbose("This is SageMath.", level=1) + verbose 1 () This is SageMath. + sage: t = verbose("This is SageMath.", level=2) + + +Logging Levels of SageMath and Python +===================================== + +.. csv-table:: + :class: contentstable + :widths: 20, 20 + :delim: | + + SageMath | Python + `-2` | ``logging.CRITICAL`` + `-1` | ``logging.ERROR`` + `0` | ``logging.WARNING`` + `1` | ``logging.INFO`` + `2` | ``logging.DEBUG`` + + +Various +======= + +AUTHORS: + +- Daniel Krenn (2016) + + +Functions +========= +""" +#***************************************************************************** +# Copyright (C) 2006, 2007 William Stein +# Copyright (C) 2006 Gonzalo Tornaria +# Copyright (C) 2008 John H. Palmieri +# Copyright (C) 2009 Mike Hansen +# Copyright (C) 2016 Daniel Krenn +# Copyright (C) 2018 Frédéric Chapoton +# +# 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 3 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +import sys +import os + +LEVEL = 0 # default + +verbose_files = [] + + +def verbose(mesg="", t=0, level=1, caller_name=None): + """ + Print a message if the current verbosity is at least level. + + INPUT: + + + - ``mesg`` - str, a message to print + + - ``t`` - int, optional, if included, will also print + cputime(t), - which is the time since time t. Thus t should have + been obtained with t=cputime() + + - ``level`` - int, (default: 1) the verbosity level of + what we are printing + + - ``caller_name`` - string (default: None), the name + of the calling function; in most cases Python can deduce this, so + it need not be provided. + + + OUTPUT: possibly prints a message to stdout; also returns + cputime() + + EXAMPLES:: + + sage: set_verbose(1) + sage: t = cputime() + sage: t = verbose("This is Sage.", t, level=1, caller_name="william") # not tested + VERBOSE1 (william): This is Sage. (time = 0.0) + sage: set_verbose(0) + """ + from sage.misc.misc import cputime + if level > LEVEL: + return cputime() + + frame = sys._getframe(1).f_code + file_name = frame.co_filename + lineno = frame.co_firstlineno + if 'all' in verbose_files or level <= 0: + show = True + else: + show = False + for X in verbose_files: + if file_name.find(X) != -1: + show = True + break + + if not show: + return cputime() + + if t != 0 and mesg == "": + mesg = "Finished." + + # see recipe 14.7 in Python Cookbook + if caller_name is None: + caller_name = frame.co_name + if caller_name == "?: ": + caller_name = "" + short_file_name = os.path.split(frame.co_filename)[1] + if '<' in short_file_name and '>' in short_file_name: + s = "verbose %s (%s) %s" % (level, caller_name, mesg) + else: + s = "verbose %s (%s: %s, %s) %s" % (level, lineno, + short_file_name, caller_name, mesg) + if t != 0: + s = s + " (time = %s)" % cputime(t) + print(s) + sys.stdout.flush() + return cputime() + + +def set_verbose(level, files='all'): + """ + Set the global Sage verbosity level. + + INPUT: + + - ``level`` -- an integer between 0 and 2, inclusive. + + - ``files`` (default: 'all'): list of files to make verbose, or + 'all' to make ALL files verbose (the default). + + OUTPUT: changes the state of the verbosity flag and possibly + appends to the list of files that are verbose. + + EXAMPLES:: + + sage: set_verbose(2) + sage: verbose("This is Sage.", level=1) # not tested + VERBOSE1 (?): This is Sage. + sage: verbose("This is Sage.", level=2) # not tested + VERBOSE2 (?): This is Sage. + sage: verbose("This is Sage.", level=3) # not tested + [no output] + sage: set_verbose(0) + """ + if level is None: + level = -1 + if isinstance(level, str): + set_verbose_files([level]) + global LEVEL + LEVEL = level + if isinstance(files, str): + files = [files] + set_verbose_files(files) + + +def set_verbose_files(file_name): + """ + + """ + if not isinstance(file_name, list): + file_name = [file_name] + global verbose_files + verbose_files = file_name + + +def get_verbose_files(): + """ + + """ + return verbose_files + + +def unset_verbose_files(file_name): + """ + + """ + if not isinstance(file_name, list): + file_name = [file_name] + for X in file_name: + verbose_files.remove(X) + + +def get_verbose(): + """ + Return the global Sage verbosity level. + + INPUT: int level: an integer between 0 and 2, inclusive. + + OUTPUT: changes the state of the verbosity flag. + + EXAMPLES:: + + sage: get_verbose() + 0 + sage: set_verbose(2) + sage: get_verbose() + 2 + sage: set_verbose(0) + """ + global LEVEL + return LEVEL diff --git a/src/sage/misc/weak_dict.pyx b/src/sage/misc/weak_dict.pyx index 0dc1d203cfe..b2d6fc1f857 100644 --- a/src/sage/misc/weak_dict.pyx +++ b/src/sage/misc/weak_dict.pyx @@ -119,7 +119,6 @@ See :trac:`13394` for a discussion of some of the design considerations. # **************************************************************************** import weakref -import six from weakref import KeyedRef from copy import deepcopy @@ -348,10 +347,10 @@ cdef class WeakValueDictionary(dict): True """ try: - data = six.iteritems(data) + data = data.items() except AttributeError: pass - for (k, v) in data: + for k, v in data: self._set_item(k, v) def __copy__(self): diff --git a/src/sage/modular/abvar/constructor.py b/src/sage/modular/abvar/constructor.py index 4255061b6c0..df2518d34dc 100644 --- a/src/sage/modular/abvar/constructor.py +++ b/src/sage/modular/abvar/constructor.py @@ -11,7 +11,6 @@ # http://www.gnu.org/licenses/ # ########################################################################### from __future__ import absolute_import -from six import integer_types import weakref @@ -172,7 +171,7 @@ def AbelianVariety(X): ... TypeError: X must be an integer, string, newform, modsym space, congruence subgroup or tuple of congruence subgroups """ - if isinstance(X, integer_types + (Integer,)): + if isinstance(X, (int, Integer)): X = Gamma0(X) if is_CongruenceSubgroup(X): X = X.modular_symbols().cuspidal_submodule() diff --git a/src/sage/modular/all.py b/src/sage/modular/all.py index 5dfa44415a7..e9c96d04cda 100644 --- a/src/sage/modular/all.py +++ b/src/sage/modular/all.py @@ -1,14 +1,3 @@ -""" -Test for deprecations of imports into global namespace:: - - sage: buzzard_tpslopes - doctest:warning...: - DeprecationWarning: - Importing buzzard_tpslopes from here is deprecated. If you need to use it, please import it directly from sage.modular.buzzard - See https://trac.sagemath.org/27066 for details. - -""" -from __future__ import absolute_import from sage.misc.lazy_import import lazy_import from .quatalg.all import * @@ -37,11 +26,11 @@ dimension_modular_forms, sturm_bound) -lazy_import("sage.modular.buzzard", 'buzzard_tpslopes', deprecation=27066) - from .etaproducts import (EtaGroup, EtaProduct, EtaGroupElement, AllCusps, CuspFamily) +lazy_import('sage.modular.multiple_zeta', ['Multizeta', 'Multizetas']) + from .overconvergent.all import * from .local_comp.all import * @@ -51,5 +40,3 @@ from .btquotients.all import * from .pollack_stevens.all import * - -del absolute_import diff --git a/src/sage/modular/arithgroup/arithgroup_generic.py b/src/sage/modular/arithgroup/arithgroup_generic.py index f2d9d51d396..7001bbfa869 100644 --- a/src/sage/modular/arithgroup/arithgroup_generic.py +++ b/src/sage/modular/arithgroup/arithgroup_generic.py @@ -13,7 +13,6 @@ # ################################################################################ from __future__ import absolute_import -from six.moves import range from sage.groups.old import Group from sage.categories.groups import Groups @@ -686,6 +685,7 @@ def cusps(self, algorithm='default'): r""" Return a sorted list of inequivalent cusps for self, i.e. a set of representatives for the orbits of self on `\mathbb{P}^1(\QQ)`. + These should be returned in a reduced form where this makes sense. INPUT: @@ -709,19 +709,20 @@ def cusps(self, algorithm='default'): """ try: return copy(self._cusp_list[algorithm]) - except (AttributeError,KeyError): + except (AttributeError, KeyError): self._cusp_list = {} from .congroup_sl2z import is_SL2Z - if is_SL2Z(self): - s = [Cusp(1,0)] - if algorithm == 'default': - s = self._find_cusps() + if is_SL2Z(self): + s = [Cusp(1, 0)] + else: + s = self._find_cusps() elif algorithm == 'modsym': - s = sorted([self.reduce_cusp(c) for c in self.modular_symbols().cusps()]) + s = sorted(self.reduce_cusp(c) + for c in self.modular_symbols().cusps()) else: - raise ValueError("unknown algorithm: %s"%algorithm) + raise ValueError("unknown algorithm: %s" % algorithm) self._cusp_list[algorithm] = s return copy(s) diff --git a/src/sage/modular/arithgroup/arithgroup_perm.py b/src/sage/modular/arithgroup/arithgroup_perm.py index 65593272951..7b9aca55627 100644 --- a/src/sage/modular/arithgroup/arithgroup_perm.py +++ b/src/sage/modular/arithgroup/arithgroup_perm.py @@ -98,13 +98,11 @@ # ################################################################################ from __future__ import print_function, absolute_import -from six.moves import range from .all import SL2Z from .arithgroup_generic import ArithmeticSubgroup from sage.rings.all import ZZ from sage.misc.cachefunc import cached_method -from sage.misc.misc import verbose import sage.arith.all as arith from sage.groups.perm_gps.permgroup_element import PermutationGroupElement @@ -1427,6 +1425,7 @@ def is_congruence(self): sage: G.to_even_subgroup() == Gamma0(6) True """ + from sage.misc.verbose import verbose if self.index() == 1: # the group is SL2Z (trivial case) return True diff --git a/src/sage/modular/arithgroup/congroup_gamma.py b/src/sage/modular/arithgroup/congroup_gamma.py index 232005ebf2a..bc7a13c2f65 100644 --- a/src/sage/modular/arithgroup/congroup_gamma.py +++ b/src/sage/modular/arithgroup/congroup_gamma.py @@ -10,7 +10,6 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from .congroup_generic import CongruenceSubgroup from sage.misc.all import prod diff --git a/src/sage/modular/arithgroup/congroup_gamma0.py b/src/sage/modular/arithgroup/congroup_gamma0.py index ebd881bc556..2c50834fe20 100644 --- a/src/sage/modular/arithgroup/congroup_gamma0.py +++ b/src/sage/modular/arithgroup/congroup_gamma0.py @@ -8,9 +8,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# https://www.gnu.org/licenses/ -# **************************************************************************** -from six.moves import range +# http://www.gnu.org/licenses/ +#***************************************************************************** from .congroup_gammaH import GammaH_class from .congroup_gamma1 import is_Gamma1 diff --git a/src/sage/modular/arithgroup/congroup_gammaH.py b/src/sage/modular/arithgroup/congroup_gammaH.py index aa91f91db92..f989b8e5b95 100644 --- a/src/sage/modular/arithgroup/congroup_gammaH.py +++ b/src/sage/modular/arithgroup/congroup_gammaH.py @@ -20,7 +20,6 @@ # http://www.gnu.org/licenses/ # ################################################################################ -from six.moves import range from sage.arith.all import euler_phi, lcm, gcd, divisors, get_inverse_mod, get_gcd, factor, xgcd from sage.modular.modsym.p1list import lift_to_sl2z diff --git a/src/sage/modular/arithgroup/tests.py b/src/sage/modular/arithgroup/tests.py index 8321a6f3b00..51c94f86df9 100644 --- a/src/sage/modular/arithgroup/tests.py +++ b/src/sage/modular/arithgroup/tests.py @@ -13,7 +13,6 @@ # ################################################################################ from __future__ import print_function, absolute_import -from six.moves import range from .arithgroup_perm import ArithmeticSubgroup_Permutation, EvenArithmeticSubgroup_Permutation, OddArithmeticSubgroup_Permutation from sage.modular.arithgroup.all import Gamma, Gamma0, Gamma1, GammaH diff --git a/src/sage/modular/btquotients/btquotient.py b/src/sage/modular/btquotients/btquotient.py index c6c11d45480..2ab15f9388c 100644 --- a/src/sage/modular/btquotients/btquotient.py +++ b/src/sage/modular/btquotients/btquotient.py @@ -58,7 +58,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.modular.dirichlet import DirichletGroup from sage.modular.arithgroup.congroup_gammaH import GammaH_constructor -from sage.misc.misc import verbose +from sage.misc.verbose import verbose class DoubleCosetReduction(SageObject): diff --git a/src/sage/modular/btquotients/pautomorphicform.py b/src/sage/modular/btquotients/pautomorphicform.py index cbaca6a0114..0bd83a4447c 100644 --- a/src/sage/modular/btquotients/pautomorphicform.py +++ b/src/sage/modular/btquotients/pautomorphicform.py @@ -41,8 +41,6 @@ """ from __future__ import print_function, division -from six.moves import zip - from sage.modular.btquotients.btquotient import DoubleCosetReduction from sage.structure.unique_representation import UniqueRepresentation from sage.structure.richcmp import op_EQ, op_NE @@ -59,7 +57,7 @@ from sage.modular.hecke.all import (AmbientHeckeModule, HeckeModuleElement) from sage.rings.infinity import Infinity import sage.modular.hecke.hecke_operator -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.rings.real_mpfr import RR from sage.modular.pollack_stevens.sigma0 import Sigma0ActionAdjuster from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk diff --git a/src/sage/modular/cusps.py b/src/sage/modular/cusps.py index 47c3f8286d8..9727adf6d7b 100644 --- a/src/sage/modular/cusps.py +++ b/src/sage/modular/cusps.py @@ -28,7 +28,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six import integer_types from sage.rings.all import Rational, Integer, ZZ, QQ from sage.rings.infinity import Infinity, InfinityRing @@ -169,7 +168,7 @@ def __init__(self, a, b=None, parent=None, check=True): elif isinstance(a, Cusp): self.__a = a.__a self.__b = a.__b - elif isinstance(a, integer_types): + elif isinstance(a, int): self.__a = ZZ(a) self.__b = ZZ.one() elif isinstance(a, (tuple, list)): @@ -220,7 +219,7 @@ def __init__(self, a, b=None, parent=None, check=True): self.__a = ZZ.one() self.__b = ZZ.zero() return - elif isinstance(a, integer_types): + elif isinstance(a, int): r = ZZ(a) / b elif isinstance(a, (tuple, list)): if len(a) != 2: diff --git a/src/sage/modular/cusps_nf.py b/src/sage/modular/cusps_nf.py index da0cb670cc0..b2a19d94eec 100644 --- a/src/sage/modular/cusps_nf.py +++ b/src/sage/modular/cusps_nf.py @@ -70,7 +70,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # **************************************************************************** -from six import integer_types from sage.structure.parent import Parent from sage.structure.element import Element, is_InfinityElement @@ -454,7 +453,7 @@ def __init__(self, number_field, a, b=None, parent=None, lreps=None): elif is_InfinityElement(a): self.__a = R.one() self.__b = R.zero() - elif isinstance(a, integer_types): + elif isinstance(a, int): self.__a = R(a) self.__b = R.one() elif isinstance(a, (tuple, list)): @@ -510,7 +509,7 @@ def __init__(self, number_field, a, b=None, parent=None, lreps=None): self.__a = R.zero() self.__b = R.one() return - if (b in R or isinstance(b, integer_types)) and (a in R or isinstance(a, integer_types)): + if (b in R or isinstance(b, int)) and (a in R or isinstance(a, int)): self.__a = R(a) self.__b = R(b) else: @@ -526,7 +525,7 @@ def __init__(self, number_field, a, b=None, parent=None, lreps=None): self.__b = R.zero() return r = a.__a / (a.__b * b) - elif isinstance(a, integer_types): + elif isinstance(a, int): r = R(a) / b elif isinstance(a, (tuple, list)): if len(a) != 2: @@ -1100,7 +1099,7 @@ def number_of_Gamma0_NFCusps(N): OUTPUT: - ingeter -- the number of orbits of cusps under Gamma0(N)-action. + integer -- the number of orbits of cusps under Gamma0(N)-action. EXAMPLES:: diff --git a/src/sage/modular/dims.py b/src/sage/modular/dims.py index 59369ba02f1..6b6bc675f96 100644 --- a/src/sage/modular/dims.py +++ b/src/sage/modular/dims.py @@ -45,7 +45,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import -from six import integer_types from sage.arith.all import factor, is_prime, valuation @@ -315,7 +314,7 @@ def dimension_new_cusp_forms(X, k=2, p=0): # Gamma1(N) for N<=2 just returns Gamma0(N), which has no # eps parameter. See trac #12640. return Gamma1(N).dimension_new_cusp_forms(k, eps=X, p=p) - elif isinstance(X, integer_types + (Integer,)): + elif isinstance(X, (int, Integer)): return Gamma0(X).dimension_new_cusp_forms(k, p=p) else: raise TypeError("X (=%s) must be an integer, a Dirichlet character or a congruence subgroup of type Gamma0, Gamma1 or GammaH" % X) @@ -426,7 +425,7 @@ def dimension_cusp_forms(X, k=2): return Gamma1(N).dimension_cusp_forms(k, X) elif is_ArithmeticSubgroup(X): return X.dimension_cusp_forms(k) - elif isinstance(X, (Integer,) + integer_types): + elif isinstance(X, (int, Integer)): return Gamma0(X).dimension_cusp_forms(k) else: raise TypeError("argument 1 must be a Dirichlet character, an integer " @@ -509,7 +508,7 @@ def dimension_eis(X, k=2): return X.dimension_eis(k) elif isinstance(X, dirichlet.DirichletCharacter): return Gamma1(X.modulus()).dimension_eis(k, X) - elif isinstance(X, integer_types + (Integer,)): + elif isinstance(X, (int, Integer)): return Gamma0(X).dimension_eis(k) else: raise TypeError("argument in dimension_eis must be an integer, a Dirichlet character, or a finite index subgroup of SL2Z (got %s)" % X) @@ -551,7 +550,7 @@ def dimension_modular_forms(X, k=2): sage: dimension_modular_forms(11,2) 2 """ - if isinstance(X, integer_types + (Integer,)): + if isinstance(X, (int, Integer)): return Gamma0(X).dimension_modular_forms(k) elif is_ArithmeticSubgroup(X): return X.dimension_modular_forms(k) @@ -597,5 +596,5 @@ def sturm_bound(level, weight=2): else: raise ValueError("no Sturm bound defined for noncongruence " "subgroups") - if isinstance(level, integer_types + (Integer,)): + if isinstance(level, (int, Integer)): return Gamma0(level).sturm_bound(weight) diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index dd5332c879c..0313f303357 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -57,7 +57,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function -from six.moves import range, zip import sage.categories.all as cat from sage.misc.all import prod @@ -66,7 +65,8 @@ import sage.modules.free_module_element as free_module_element import sage.rings.all as rings import sage.rings.number_field.number_field as number_field - +from sage.libs.pari import pari + from sage.categories.map import Map from sage.rings.rational_field import is_RationalField from sage.rings.complex_field import is_ComplexField @@ -879,6 +879,109 @@ def extend(self, M): H = DirichletGroup(M, self.base_ring()) return H(self) + def _pari_conversion(self): + r""" + Prepare data for the conversion of the character to Pari. + + OUTPUT: + + pair (G, v) where G is `(\ZZ / N \ZZ)^*` where `N` is the modulus + + EXAMPLES:: + + sage: chi4 = DirichletGroup(4).gen() + sage: chi4._pari_conversion() + ([[4, [0]], [2, [2], [3]], [[2]~, Vecsmall([2])], + [[4], [[1, matrix(0,2)]], Mat(1), [3], [2], [0]], Mat(1)], [1]) + + sage: chi = DirichletGroup(24)([1,-1,-1]); chi + Dirichlet character modulo 24 of conductor 24 + mapping 7 |--> 1, 13 |--> -1, 17 |--> -1 + sage: chi._pari_conversion() + ([[24, [0]], [8, [2, 2, 2], [7, 13, 17]], + [[2, 2, 3]~, Vecsmall([3, 3, 1])], + [[8, 8, 3], [[1, matrix(0,2)], [1, matrix(0,2)], [2, Mat([2, 1])]], + [1, 0, 0; 0, 1, 0; 0, 0, 1], [7, 13, 17], [2, 2, 2], [0, 0, 0]], + [1, 0, 0; 0, 1, 0; 0, 0, 1]], [0, 1, 1]) + """ + G = pari.znstar(self.modulus(), 1) + + pari_orders = G[1][1] + pari_gens = G[1][2] + # one should use the following, but this does not work + # pari_orders = G.cyc() + # pari_gens = G.gen() + + values_on_gens = (self(x) for x in pari_gens) + + # now compute the input for pari (list of exponents) + P = self.parent() + if is_ComplexField(P.base_ring()): + zeta = P.zeta() + zeta_argument = zeta.argument() + v = [int(x.argument() / zeta_argument) for x in values_on_gens] + else: + dlog = P._zeta_dlog + v = [dlog[x] for x in values_on_gens] + + m = P.zeta_order() + v = [(vi * oi) // m for vi, oi in zip(v, pari_orders)] + return (G, v) + + def conrey_number(self): + r""" + Return the Conrey number for this character. + + This is a positive integer coprime to q that identifies a + Dirichlet character of modulus q. + + See https://www.lmfdb.org/knowledge/show/character.dirichlet.conrey + + EXAMPLES:: + + sage: chi4 = DirichletGroup(4).gen() + sage: chi4.conrey_number() + 3 + sage: chi = DirichletGroup(24)([1,-1,-1]); chi + Dirichlet character modulo 24 of conductor 24 + mapping 7 |--> 1, 13 |--> -1, 17 |--> -1 + sage: chi.conrey_number() + 5 + + sage: chi = DirichletGroup(60)([1,-1,I]) + sage: chi.conrey_number() + 17 + + sage: chi = DirichletGroup(420)([1,-1,-I,1]) + sage: chi.conrey_number() + 113 + + TESTS:: + + sage: eps1 = DirichletGroup(5)([-1]) + sage: eps2 = DirichletGroup(5,QQ)([-1]) + sage: eps1.conrey_number() == eps2.conrey_number() + True + """ + G, v = self._pari_conversion() + return pari.znconreyexp(G, v).sage() + + def lmfdb_page(self): + r""" + Open the LMFDB web page of the character in a browser. + + See https://www.lmfdb.org + + EXAMPLES:: + + sage: E = DirichletGroup(4).gen() + sage: E.lmfdb_page() # optional -- webbrowser + """ + import webbrowser + lmfdb_url = 'https://www.lmfdb.org/Character/Dirichlet/{}/{}' + url = lmfdb_url.format(self.modulus(), self.conrey_number()) + webbrowser.open(url) + def galois_orbit(self, sort=True): r""" Return the orbit of this character under the action of the absolute @@ -1239,34 +1342,49 @@ def kloosterman_sum(self, a=1, b=0): sage: e.kloosterman_sum(3,5) -2*zeta6 + 1 sage: G = DirichletGroup(20) - sage: e = G([1 for u in G.unit_gens()]) + sage: e = G([1 for u in G.unit_gens()]) sage: e.kloosterman_sum(7,17) -2*zeta20^6 + 2*zeta20^4 + 4 + TESTS:: + + sage: G = DirichletGroup(20, UniversalCyclotomicField()) + sage: e = G([1 for u in G.unit_gens()]) + sage: e.kloosterman_sum(7,17) + -2*E(5) - 4*E(5)^2 - 4*E(5)^3 - 2*E(5)^4 + + sage: G = DirichletGroup(12, QQbar) + sage: e = G.gens()[0] + sage: e.kloosterman_sum(5,11) + Traceback (most recent call last): + ... + NotImplementedError: Kloosterman sums not implemented over this ring """ G = self.parent() - K = G.base_ring() - if not (number_field.is_CyclotomicField(K) or is_RationalField(K)): - raise NotImplementedError("Kloosterman sums only currently implemented when the base ring is a cyclotomic field or QQ.") - g = 0 + zo = G.zeta_order() m = G.modulus() - L = rings.CyclotomicField(lcm(m,G.zeta_order())) + g = 0 + L = rings.CyclotomicField(m.lcm(zo)) zeta = L.gen(0) + try: + self(1) * zeta**(a+b) + except TypeError: + raise NotImplementedError('Kloosterman sums not implemented ' + 'over this ring') n = zeta.multiplicative_order() - zeta = zeta ** (n // m) - for c in range(1,m): - if gcd(c,m)==1: - e = rings.Mod(c,m) - z = zeta ** int(a*e + b*(e**(-1))) - g += self(c)*z + zeta = zeta**(n // m) + for c in m.coprime_integers(m): + e = rings.Mod(c, m) + g += self(c) * zeta**int(a*e + b*e**(-1)) return g - def kloosterman_sum_numerical(self, prec=53, a=1,b=0): + def kloosterman_sum_numerical(self, prec=53, a=1, b=0): r""" Return the Kloosterman sum associated to this Dirichlet character as - an approximate complex number with prec bits of precision. See also - :meth:`.kloosterman_sum`, which calculates the sum exactly (which is - generally slower). + an approximate complex number with prec bits of precision. + + See also :meth:`.kloosterman_sum`, which calculates the sum + exactly (which is generally slower). INPUT: @@ -1300,12 +1418,10 @@ def kloosterman_sum_numerical(self, prec=53, a=1,b=0): g = 0 m = G.modulus() zeta = CC.zeta(m) - - for c in range(1,m): - if gcd(c,m)==1: - e = rings.Mod(c,m) - z = zeta ** int(a*e + b*(e**(-1))) - g += phi(self(c))*z + for c in m.coprime_integers(m): + e = rings.Mod(c, m) + z = zeta ** int(a*e + b*(e**(-1))) + g += phi(self(c))*z return g @cached_method diff --git a/src/sage/modular/etaproducts.py b/src/sage/modular/etaproducts.py index 3f8dd838bae..2184fcd3c39 100644 --- a/src/sage/modular/etaproducts.py +++ b/src/sage/modular/etaproducts.py @@ -230,7 +230,6 @@ def basis(self, reduce=True): pass it to the reduce_basis() function which performs LLL-reduction to give a more manageable basis. """ - from six.moves import range N = self.level() divs = divisors(N)[:-1] s = len(divs) @@ -298,7 +297,6 @@ def reduce_basis(self, long_etas): [Eta product of level 4 : (eta_1)^8 (eta_4)^-8, Eta product of level 4 : (eta_1)^-8 (eta_2)^24 (eta_4)^-16] """ - from six.moves import range N = self.level() cusps = AllCusps(N) r = matrix(ZZ, [[et.order_at_cusp(c) for c in cusps] for et in long_etas]) @@ -712,7 +710,6 @@ def AllCusps(N): ... ValueError: N must be positive """ - from six.moves import range N = ZZ(N) if N <= 0: raise ValueError("N must be positive") @@ -1037,7 +1034,6 @@ def _eta_relations_helper(eta1, eta2, degree, qexp_terms, labels, verbose): sage: _eta_relations_helper(EtaProduct(26, {2:2,13:2,26:-2,1:-2}),EtaProduct(26, {2:4,13:2,26:-4,1:-2}),3,12,['a','b'],False) # not enough terms, will return rubbish [1] """ - from six.moves import range indices = [(i,j) for j in range(degree) for i in range(degree)] inf = CuspFamily(eta1.level(), 1) diff --git a/src/sage/modular/hecke/algebra.py b/src/sage/modular/hecke/algebra.py index 13a4b4750eb..3a586bed1d9 100644 --- a/src/sage/modular/hecke/algebra.py +++ b/src/sage/modular/hecke/algebra.py @@ -26,7 +26,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import -from six.moves import range import sage.arith.all as arith import sage.rings.infinity diff --git a/src/sage/modular/hecke/ambient_module.py b/src/sage/modular/hecke/ambient_module.py index 4bdfb271a1d..f9934c502ae 100644 --- a/src/sage/modular/hecke/ambient_module.py +++ b/src/sage/modular/hecke/ambient_module.py @@ -28,8 +28,6 @@ import sage.rings.all -import sage.misc.misc as misc - import sage.arith.all as arith import sage.matrix.matrix_space as matrix_space @@ -519,12 +517,13 @@ def hecke_bound(self): sage: ModularSymbols(Gamma1(17), 4).hecke_bound() # wrong! 15 """ + from sage.misc.verbose import verbose try: if self.is_cuspidal(): return Gamma0(self.level()).sturm_bound(self.weight()) except AttributeError: pass - misc.verbose("WARNING: ambient.py -- hecke_bound; returning unproven guess.") + verbose("WARNING: ambient.py -- hecke_bound; returning unproven guess.") return Gamma0(self.level()).sturm_bound(self.weight()) + 2*Gamma0(self.level()).dimension_eis(self.weight()) + 5 def hecke_module_of_level(self, level): diff --git a/src/sage/modular/hecke/hecke_operator.py b/src/sage/modular/hecke/hecke_operator.py index d32b5b96cb1..060a92d7435 100644 --- a/src/sage/modular/hecke/hecke_operator.py +++ b/src/sage/modular/hecke/hecke_operator.py @@ -17,7 +17,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six import integer_types from sage.structure.element import AlgebraElement @@ -607,7 +606,7 @@ def __init__(self, parent, n): Hecke operator T_10604499373 on Modular Symbols space of dimension 5 for Gamma_0(21) of weight 2 with sign 0 over Rational Field """ HeckeAlgebraElement.__init__(self, parent) - if not isinstance(n, integer_types + (Integer,)): + if not isinstance(n, (int, Integer)): raise TypeError("n must be an int") self.__n = int(n) diff --git a/src/sage/modular/hecke/module.py b/src/sage/modular/hecke/module.py index 17cd67eadb5..d991caa38e1 100644 --- a/src/sage/modular/hecke/module.py +++ b/src/sage/modular/hecke/module.py @@ -15,7 +15,7 @@ import sage.rings.all import sage.arith.all as arith -import sage.misc.misc as misc +from sage.misc.verbose import verbose import sage.modules.module from sage.structure.all import Sequence import sage.matrix.matrix_space as matrix_space @@ -388,7 +388,7 @@ def is_full_hecke_module(self): # now compute whether invariant under Hecke operators of index # dividing the level - misc.verbose("Determining if Hecke module is full.") + verbose("Determining if Hecke module is full.") N = self.level() for p in arith.prime_divisors(N): if not self.is_hecke_invariant(p): @@ -683,7 +683,7 @@ def _is_hecke_equivariant_free_module(self, submodule): sage: M._is_hecke_equivariant_free_module(M.cuspidal_submodule().free_module()) True """ - misc.verbose("Determining if free module is Hecke equivariant.") + verbose("Determining if free module is Hecke equivariant.") bound = self.hecke_bound() for p in arith.primes(bound + 1): try: @@ -995,7 +995,7 @@ def decomposition(self, bound=None, anemic=True, height_guess=1, sort_by_basis = is_rational = self.base_ring() == sage.rings.all.QQ - time = misc.verbose("Decomposing %s" % self) + time = verbose("Decomposing %s" % self) T = self.ambient_hecke_module().hecke_algebra() if bound is None: bound = self.ambient_hecke_module().hecke_bound() @@ -1003,11 +1003,11 @@ def decomposition(self, bound=None, anemic=True, height_guess=1, sort_by_basis = U = [self.free_module()] p = 2 while U and p <= bound: - misc.verbose(mesg="p=%s" % p, t=time) + verbose(mesg="p=%s" % p, t=time) if anemic: while arith.GCD(p, self.level()) != 1: p = arith.next_prime(p) - misc.verbose("Decomposition using p=%s" % p) + verbose("Decomposition using p=%s" % p) t = T.hecke_operator(p).matrix() Uprime = [] for i in range(len(U)): diff --git a/src/sage/modular/hecke/submodule.py b/src/sage/modular/hecke/submodule.py index 26f814d4d5c..79bbc2ad8c0 100644 --- a/src/sage/modular/hecke/submodule.py +++ b/src/sage/modular/hecke/submodule.py @@ -20,7 +20,7 @@ from __future__ import absolute_import import sage.arith.all as arith -import sage.misc.misc as misc +from sage.misc.verbose import verbose from sage.misc.cachefunc import cached_method from sage.structure.richcmp import richcmp_method, richcmp_not_equal import sage.modules.all @@ -356,7 +356,7 @@ def complement(self, bound=None): # TODO: optimize in some cases by computing image of # complementary factor instead of kernel...? - misc.verbose("computing") + verbose("computing") N = self.level() A = self.ambient_hecke_module() V = A.free_module() @@ -366,7 +366,7 @@ def complement(self, bound=None): while True: if anemic: while N % p == 0: p = arith.next_prime(p) - misc.verbose("using T_%s"%p) + verbose("using T_%s"%p) f = self.hecke_polynomial(p) T = A.hecke_matrix(p) g = T.charpoly('x') @@ -385,7 +385,7 @@ def complement(self, bound=None): # the following naive approach: decompose the ambient space, # decompose self, and sum the pieces of ambient that are not # subspaces of self - misc.verbose("falling back on naive algorithm") + verbose("falling back on naive algorithm") D = A.decomposition() C = A.zero_submodule() for X in D: @@ -515,12 +515,12 @@ def dual_free_module(self, bound=None, anemic=True, use_star=True): # if we know the complement we can read off the dual module if self.complement.is_in_cache(): - misc.verbose('This module knows its complement already -- cheating in dual_free_module') + verbose('This module knows its complement already -- cheating in dual_free_module') C = self.complement() V = C.basis_matrix().right_kernel() return V - misc.verbose("computing dual") + verbose("computing dual") A = self.ambient_hecke_module() @@ -570,7 +570,7 @@ def dual_free_module(self, bound=None, anemic=True, use_star=True): while True: if anemic: while N % p == 0: p = arith.next_prime(p) - misc.verbose("using T_%s"%p) + verbose("using T_%s"%p) f = self.hecke_polynomial(p) T = A.dual_hecke_matrix(p) V = T.kernel_on(V, poly=f, check=False) diff --git a/src/sage/modular/hypergeometric_misc.pxd b/src/sage/modular/hypergeometric_misc.pxd index b601a29db24..d031601666f 100644 --- a/src/sage/modular/hypergeometric_misc.pxd +++ b/src/sage/modular/hypergeometric_misc.pxd @@ -1,4 +1,3 @@ -from cpython cimport array - -cpdef hgm_coeffs(long long p, int f, int prec, gamma, array.array m, int D, +cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, gtable, int gtable_prec, bint use_longs) + diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx index df44371d263..0faa55e3701 100644 --- a/src/sage/modular/hypergeometric_misc.pyx +++ b/src/sage/modular/hypergeometric_misc.pyx @@ -2,7 +2,10 @@ Some utility routines for the hypergeometric motives package that benefit significantly from Cythonization. """ -cpdef hgm_coeffs(long long p, int f, int prec, gamma, array.array m, int D, +from cpython cimport array +from cysignals.signals cimport sig_check + +cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, gtable, int gtable_prec, bint use_longs): r""" Compute coefficients for the hypergeometric trace formula. @@ -12,16 +15,14 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, array.array m, int D, TESTS:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: import array sage: from sage.modular.hypergeometric_misc import hgm_coeffs sage: H = Hyp(cyclotomic=([3],[4])) sage: H.euler_factor(2, 7, cache_p=True) 7*T^2 - 3*T + 1 sage: gamma = H.gamma_array() sage: prec, gtable = H.gauss_table(7, 1, 2) - sage: m = array.array('i', [0]*6) sage: D = 1 - sage: hgm_coeffs(7, 1, 2, gamma, m, D, gtable, prec, False) + sage: hgm_coeffs(7, 1, 2, gamma, [0]*6, D, gtable, prec, False) [7, 2*7, 6*7, 7, 6, 4*7] """ from sage.rings.padics.factory import Zp @@ -34,7 +35,7 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, array.array m, int D, gl = len(gamma) cdef array.array gamma_array1 = array.array('i', gamma.keys()) cdef array.array gamma_array2 = array.array('i', gamma.values()) - cdef array.array r_array = array.array('i', [0]) * gl + r_array = [0] * gl cdef array.array digit_count = array.array('i', [0]) * q1 cdef array.array gtab2 @@ -60,13 +61,19 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, array.array m, int D, else: for r in range(q1): r1 = r - digit_count[r] = 0 + w = 0 for i in range(f): - digit_count[r] += r1 % p + w += r1 % p r1 //= p + digit_count[r] = w Rz = R.zero() + ans = [None] * q1 for r in range(q1): - # First determine whether this term is forced to be zero + sig_check() + # Skip this coefficient if we already have it by symmetry. + if ans[r] is not None: + continue + # Determine whether this term is forced to be zero # for divisibility reasons. If so, skip the p-adic arithmetic. i = 0 for k in range(gl): @@ -78,34 +85,54 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, array.array m, int D, i //= (p - 1) l = i + f * (D + m[0] - m[r]) if l >= prec: - ans.append(Rz) - continue - # Keep numerator and denominator separate for efficiency. - if use_longs: - w = 1 - w1 = 1 + ans[r] = Rz else: - u = R.one() - u1 = R.one() - for k in range(gl): - gv = gamma_array2[k] - r1 = r_array[k] - if flip: - gv = -gv + # Keep numerator and denominator separate for efficiency. if use_longs: - w2 = gtab2[r1] # cast to long long to avoid overflow - if gv > 0: - for j in range(gv): w = w * w2 % q2 - else: - for j in range(-gv): w1 = w1 * w2 % q2 + w = 1 + w1 = 1 else: - if gv > 0: - for j in range(gv): u *= gtable[r1] + u = R.one() + u1 = R.one() + for k in range(gl): + gv = gamma_array2[k] + r1 = r_array[k] + if flip: + gv = -gv + if use_longs: + w2 = gtab2[r1] # cast to long long to avoid overflow + if gv > 0: + for j in range(gv): + w = w * w2 % q2 + else: + for j in range(-gv): + w1 = w1 * w2 % q2 else: - for j in range(-gv): u1 *= gtable[r1] - if use_longs: - u = R(w) - u1 = R(w1) - if i % 2: u = -u - ans.append((u / u1) << l) - return ans + w2 = gtable[r1] + if gv > 0: + for j in range(gv): + u *= w2 + else: + for j in range(-gv): + u1 *= w2 + if use_longs: + u = R(w) + u1 = R(w1) + if i % 2: + u = -u + ans[r] = (u / u1) << l + if f > 1: + r1 = r + for j in range(f-1): + r1 = r1 * p % q1 + if ans[r1] is not None: + break + ans[r1] = ans[r] + if f == 1: + return ans + # Consolidate down to p-1 terms. + ans2 = [Rz] * (p-1) + for r1 in range(p-1): + for r in range(r1, q1, p-1): + ans2[r1] += ans[r] + return ans2 diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 645c3a3542e..2bca0d85f87 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -61,8 +61,6 @@ from collections import defaultdict from itertools import combinations -import array - from sage.arith.misc import divisors, gcd, euler_phi, moebius, is_prime from sage.arith.misc import gauss_sum, kronecker_symbol from sage.combinat.integer_vector_weighted import WeightedIntegerVectors @@ -650,6 +648,21 @@ def gamma_list(self): resu += [sgn(n) * v] * abs(n) return resu + def wild_primes(self): + r""" + Return the wild primes. + + EXAMPLES:: + + sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp + sage: Hyp(cyclotomic=([3],[4])).wild_primes() + [2, 3] + sage: Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6],[1,1,4,5,9])).wild_primes() + [2, 3, 5] + """ + gamma = self.gamma_array() + return sorted(set([p for n in gamma.keys() for (p, _) in n.factor()])) + def zigzag(self, x, flip_beta=False): r""" Count ``alpha``'s at most ``x`` minus ``beta``'s at most ``x``. @@ -1098,7 +1111,8 @@ def gauss_table(self, p, f, prec): """ try: prec1, gtab = self._gauss_table[p, f] - if prec1 < prec: raise KeyError + if prec1 < prec: + raise KeyError except KeyError: use_longs = (p ** prec < 2 ** 31) gtab = gauss_table(p, f, prec, use_longs) @@ -1210,6 +1224,29 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): sage: H.padic_H_value(101, 2, 2) -1560629 + Check issue from :trac:`29778`:: + + sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) + sage: try: + ....: print(H.padic_H_value(373, 4, 2)) + ....: except ValueError as s: + ....: print(s) + p^f cannot exceed 2^31 + + Check error handling for wild and tame primes:: + + sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) + sage: try: + ....: print(H.padic_H_value(5, 1, 2)) + ....: except NotImplementedError as s: + ....: print(s) + p is wild + sage: try: + ....: print(H.padic_H_value(3, 1, 3)) + ....: except NotImplementedError as s: + ....: print(s) + p is tame + REFERENCES: - [MagmaHGM]_ @@ -1217,16 +1254,24 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): alpha = self._alpha beta = self._beta t = QQ(t) + if not is_prime(p): + raise ValueError('p not prime') + if not all(x.denominator() % p for x in self._alpha + self._beta): + raise NotImplementedError('p is wild') + if (t.numerator()*t.denominator() % p == 0 or (t-1) % p == 0): + raise NotImplementedError('p is tame') + if 0 in alpha: return self._swap.padic_H_value(p, f, ~t, prec) q = p ** f if q > 2 ** 31: - return ValueError("p^f cannot exceed 2^31") + raise ValueError("p^f cannot exceed 2^31") - m = array.array('i', [0]) * int(q - 1) + m = defaultdict(int) for b in beta: u = b * (q - 1) - if u.is_integer(): m[u] += 1 + if u.is_integer(): + m[u] += 1 M = self.M_value() D = -min(self.zigzag(x, flip_beta=True) for x in alpha + beta) # also: D = (self.weight() + 1 - m[0]) // 2 @@ -1246,14 +1291,16 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): else: gtab = gauss_table(p, f, prec, use_longs) trcoeffs = hgm_coeffs(p, f, prec, gamma, m, D, gtab, prec, use_longs) - sigma = trcoeffs[q-2] + sigma = trcoeffs[p-2] p_ring = sigma.parent() teich = p_ring.teichmuller(M/t) - for i in range(q-3, -1, -1): + for i in range(p-3, -1, -1): sigma = sigma * teich + trcoeffs[i] resu = ZZ(-1) ** m[0] * sigma / (1 - q) return IntegerModRing(p**prec)(resu).lift_centered() + trace = padic_H_value + @cached_method def H_value(self, p, f, t, ring=None): """ @@ -1315,6 +1362,31 @@ def H_value(self, p, f, t, ring=None): sage: [H2.H_value(5,1,QQ(i)) for i in range(2,5)] [-4, 1, -4] + TESTS: + + Check issue from :trac:`29778`:: + + sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) + sage: try: + ....: print(H.padic_H_value(373, 4, 2)) + ....: except ValueError as s: + ....: print(s) + p^f cannot exceed 2^31 + + Check error handling for wild and tame primes:: + + sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) + sage: try: + ....: print(H.padic_H_value(5, 1, 2)) + ....: except NotImplementedError as s: + ....: print(s) + p is wild + sage: try: + ....: print(H.padic_H_value(3, 1, 3)) + ....: except NotImplementedError as s: + ....: print(s) + p is tame + REFERENCES: - [BeCoMe]_ (Theorem 1.3) @@ -1323,6 +1395,13 @@ def H_value(self, p, f, t, ring=None): alpha = self._alpha beta = self._beta t = QQ(t) + if not is_prime(p): + raise ValueError('p not prime') + if not all(x.denominator() % p for x in self._alpha + self._beta): + raise NotImplementedError('p is wild') + if (t.numerator()*t.denominator() % p == 0 or (t-1) % p == 0): + raise NotImplementedError('p is tame') + if 0 in alpha: return self._swap.H_value(p, f, ~t, ring) if ring is None: @@ -1493,13 +1572,37 @@ def euler_factor(self, t, p, cache_p=False): sage: H.euler_factor(2, 11, cache_p=True) -T^4 + T^3 - T + 1 + Check issue from :trac:`29778`:: + + sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) + sage: try: + ....: print(H.euler_factor(2, 373)) + ....: except ValueError as s: + ....: print(s) + p^f cannot exceed 2^31 + + Check error handling for wild and tame primes:: + + sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) + sage: try: + ....: print(H.euler_factor(2, 5)) + ....: except NotImplementedError as s: + ....: print(s) + p is wild + sage: try: + ....: print(H.euler_factor(3, 3)) + ....: except NotImplementedError as s: + ....: print(s) + p is tame + REFERENCES: - [Roberts2015]_ - [Watkins]_ """ - if t not in QQ or t in [0, 1]: - raise ValueError('wrong t') + t = QQ(t) + if t in [0, 1]: + raise ValueError('invalid t') alpha = self._alpha if 0 in alpha: return self._swap.euler_factor(~t, p) @@ -1508,11 +1611,14 @@ def euler_factor(self, t, p, cache_p=False): raise ValueError('p not prime') if not all(x.denominator() % p for x in self._alpha + self._beta): raise NotImplementedError('p is wild') - if (t.valuation(p) or (t - 1).valuation(p) > 0): + if (t.numerator()*t.denominator() % p == 0 or (t-1) % p == 0): raise NotImplementedError('p is tame') # now p is good d = self.degree() bound = d // 2 + if p ** bound > 2 ** 31: + raise ValueError("p^f cannot exceed 2^31") + traces = [self.padic_H_value(p, i + 1, t, cache_p=cache_p) for i in range(bound)] diff --git a/src/sage/modular/local_comp/smoothchar.py b/src/sage/modular/local_comp/smoothchar.py index fff3cdd0fc3..6dedb26e7ee 100644 --- a/src/sage/modular/local_comp/smoothchar.py +++ b/src/sage/modular/local_comp/smoothchar.py @@ -40,7 +40,6 @@ sage: chi.multiplicative_order() +Infinity """ -from six.moves import range import operator from sage.structure.element import MultiplicativeGroupElement, parent diff --git a/src/sage/modular/local_comp/type_space.py b/src/sage/modular/local_comp/type_space.py index 5497d529c38..a256113b3d4 100644 --- a/src/sage/modular/local_comp/type_space.py +++ b/src/sage/modular/local_comp/type_space.py @@ -14,7 +14,6 @@ """ from __future__ import absolute_import -from six.moves import range import operator from sage.modular.arithgroup.all import GammaH diff --git a/src/sage/modular/modform/cuspidal_submodule.py b/src/sage/modular/modform/cuspidal_submodule.py index f3a62ca63b1..fdf50a58922 100644 --- a/src/sage/modular/modform/cuspidal_submodule.py +++ b/src/sage/modular/modform/cuspidal_submodule.py @@ -38,9 +38,9 @@ # # http://www.gnu.org/licenses/ ######################################################################### -from six.moves import range from sage.rings.all import QQ, Integer -from sage.misc.all import verbose, cached_method +from sage.misc.all import cached_method +from sage.misc.verbose import verbose from sage.matrix.all import Matrix, identity_matrix from .submodule import ModularFormsSubmodule @@ -85,6 +85,7 @@ def __init__(self, ambient_space): sage: S == loads(dumps(S)) True """ + from sage.misc.verbose import verbose verbose('creating cuspidal submodule of %s'%ambient_space) d = ambient_space._dim_cuspidal() V = ambient_space.module() diff --git a/src/sage/modular/modform/eis_series.py b/src/sage/modular/modform/eis_series.py index 9765adf420a..9e55c9f396c 100644 --- a/src/sage/modular/modform/eis_series.py +++ b/src/sage/modular/modform/eis_series.py @@ -12,9 +12,8 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import -from six import integer_types -from sage.misc.all import verbose, cputime +from sage.misc.all import cputime import sage.modular.dirichlet as dirichlet from sage.modular.arithgroup.congroup_gammaH import GammaH_class from sage.rings.all import Integer, CyclotomicField, ZZ, QQ @@ -215,6 +214,8 @@ def __find_eisen_chars(character, k): ((-1, 1), (1, 1), 9), ((-1, -1), (1, -1), 1)] """ + from sage.misc.verbose import verbose + N = character.modulus() if character.is_trivial(): if k % 2: @@ -361,18 +362,18 @@ def __find_eisen_chars_gamma1(N, k): #end if triples = [] - D = divisors(N) for chi, psi in pairs: c_chi = chi.conductor() c_psi = psi.conductor() - D = divisors(N/(c_chi * c_psi)) - if (k==2 and chi.is_trivial() and psi.is_trivial()): + D = divisors(N // (c_chi * c_psi)) + if k == 2 and chi.is_trivial() and psi.is_trivial(): D.remove(1) chi, psi = __common_minimal_basering(chi, psi) for t in D: triples.append((chi, psi, t)) return triples + def eisenstein_series_lseries(weight, prec=53, max_imaginary_part=0, max_asymp_coeffs=40): @@ -485,7 +486,7 @@ def compute_eisenstein_params(character, k): sage: len(sage.modular.modform.eis_series.compute_eisenstein_params(GammaH(15, [4]), 3)) 8 """ - if isinstance(character, integer_types + (Integer,)): + if isinstance(character, (int, Integer)): return __find_eisen_chars_gamma1(character, k) elif isinstance(character, GammaH_class): return __find_eisen_chars_gammaH(character.level(), character._generators_for_H(), k) diff --git a/src/sage/modular/modform/eisenstein_submodule.py b/src/sage/modular/modform/eisenstein_submodule.py index 1ef3c6ff4c8..a6ba150e739 100644 --- a/src/sage/modular/modform/eisenstein_submodule.py +++ b/src/sage/modular/modform/eisenstein_submodule.py @@ -3,10 +3,9 @@ The Eisenstein Subspace """ from __future__ import absolute_import -from six.moves import range from sage.structure.all import Sequence -from sage.misc.all import verbose, cached_method +from sage.misc.all import cached_method import sage.rings.all as rings from sage.categories.all import Objects from sage.matrix.all import Matrix @@ -34,6 +33,7 @@ def __init__(self, ambient_space): sage: E == loads(dumps(E)) True """ + from sage.misc.verbose import verbose verbose('creating eisenstein submodule of %s'%ambient_space) d = ambient_space._dim_eisenstein() V = ambient_space.module() diff --git a/src/sage/modular/modform/element.py b/src/sage/modular/modform/element.py index 65e6034d7b1..f8cf1a8c0eb 100644 --- a/src/sage/modular/modform/element.py +++ b/src/sage/modular/modform/element.py @@ -25,7 +25,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import, division -from six.moves import range import sage.modular.hecke.element as element @@ -34,7 +33,7 @@ from sage.matrix.constructor import matrix from sage.misc.all import prod from sage.misc.cachefunc import cached_method -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.modular.dirichlet import DirichletGroup from sage.modular.modsym.modsym import ModularSymbols from sage.modular.modsym.p1list import lift_to_sl2z diff --git a/src/sage/modular/modform/find_generators.py b/src/sage/modular/modform/find_generators.py index b55f73e3adf..2145b50f045 100644 --- a/src/sage/modular/modform/find_generators.py +++ b/src/sage/modular/modform/find_generators.py @@ -18,12 +18,11 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import -from six.moves import range -from six import integer_types from sage.structure.richcmp import richcmp_method, richcmp from sage.rings.all import Integer, QQ, ZZ -from sage.misc.all import prod, verbose +from sage.misc.all import prod +from sage.misc.verbose import verbose from sage.misc.cachefunc import cached_method from sage.modular.arithgroup.all import Gamma0, is_CongruenceSubgroup from .constructor import ModularForms @@ -113,7 +112,8 @@ def _span_of_forms_in_weight(forms, weight, prec, stop_dim=None, use_random=Fals for c in range(N): w = V(prod(shortforms[i]**wts[c][i] for i in range(n)).padded_list(prec)) - if w in W: continue + if w in W: + continue W = V.span(list(W.gens()) + [w]) if stop_dim and W.rank() == stop_dim: if R != ZZ or W.index_in_saturation() == 1: @@ -218,7 +218,7 @@ def __init__(self, group, base_ring=QQ): ... ValueError: Base ring (=Univariate Polynomial Ring in x over Integer Ring) should be QQ, ZZ or a finite prime field """ - if isinstance(group, integer_types + (Integer,)): + if isinstance(group, (int, Integer)): group = Gamma0(group) elif not is_CongruenceSubgroup(group): raise ValueError("Group (=%s) should be a congruence subgroup" % group) @@ -543,7 +543,8 @@ def _find_generators(self, maxweight, start_gens, start_weight): G.append((k, f, F)) k = start_weight - if increment == 2 and (k % 2) == 1: k += 1 + if increment == 2 and (k % 2) == 1: + k += 1 while k <= maxweight: @@ -551,12 +552,12 @@ def _find_generators(self, maxweight, start_gens, start_weight): k += increment continue - verbose('Looking at k = %s'%k) + verbose('Looking at k = %s' % k) M = self.modular_forms_of_weight(k) # 1. Multiply together all forms in G that give an element # of M. - if G != []: + if G: F = _span_of_forms_in_weight(G, k, M.sturm_bound(), None, False) else: F = (self.base_ring() ** M.sturm_bound()).zero_submodule() @@ -576,7 +577,8 @@ def _find_generators(self, maxweight, start_gens, start_weight): # try adding basis elements of M into G. verbose("Known generators span a subspace of dimension %s of space of dimension %s" % (F.dimension(), M.dimension())) - if self.base_ring() == ZZ: verbose("saturation index is %s" % F.index_in_saturation()) + if self.base_ring() == ZZ: + verbose("saturation index is %s" % F.index_in_saturation()) t = verbose("Computing more modular forms at weight %s" % k) kprec = M.sturm_bound() @@ -652,7 +654,8 @@ def q_expansion_basis(self, weight, prec=None, use_random=True): [1 + O(q^5), q + O(q^5), q^2 + O(q^5), q^3 + O(q^5), q^4 + O(q^5), O(q^5), O(q^5), O(q^5), O(q^5), O(q^5)] """ d = self.modular_forms_of_weight(weight).dimension() - if d == 0: return [] + if d == 0: + return [] if prec is None: prec=self.modular_forms_of_weight(weight).sturm_bound() @@ -788,7 +791,8 @@ def cuspidal_submodule_q_expansion_basis(self, weight, prec=None): True """ d = self.modular_forms_of_weight(weight).cuspidal_submodule().dimension() - if d == 0: return [] + if d == 0: + return [] minprec = self.modular_forms_of_weight(weight).sturm_bound() if prec is None: diff --git a/src/sage/modular/modform/numerical.py b/src/sage/modular/modform/numerical.py index fac22c1b80b..933b334d3ea 100644 --- a/src/sage/modular/modform/numerical.py +++ b/src/sage/modular/modform/numerical.py @@ -11,11 +11,10 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six import integer_types from sage.arith.all import prime_range from sage.matrix.constructor import matrix -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.misc.cachefunc import cached_method from sage.misc.prandom import randint from sage.modular.arithgroup.all import Gamma0 @@ -101,7 +100,7 @@ def __init__(self, group, weight=2, eps=1e-20, sage: numerical_eigenforms(61) # indirect doctest Numerical Hecke eigenvalues for Congruence Subgroup Gamma0(61) of weight 2 """ - if isinstance(group, integer_types + (Integer,)): + if isinstance(group, (int, Integer)): group = Gamma0(Integer(group)) self._group = group self._weight = Integer(weight) diff --git a/src/sage/modular/modform/space.py b/src/sage/modular/modform/space.py index a217949c7fa..2a73b899bd4 100644 --- a/src/sage/modular/modform/space.py +++ b/src/sage/modular/modform/space.py @@ -1077,6 +1077,11 @@ def _element_constructor_(self, x, check=True): sage: N(M.basis()[0]) q - q^3 - 2*q^4 + q^5 + O(q^6) + TESTS:: + + sage: M = ModularForms(13, 4) + sage: M(M([1, 2, 3, 4, 5]), check=True) + 4 + 6*q + 47*q^2 + 143*q^3 + 358*q^4 + 630*q^5 + O(q^6) """ if isinstance(x, self.element_class): if x.parent() is self: diff --git a/src/sage/modular/modform/theta.py b/src/sage/modular/modform/theta.py index 203f4f8b448..e41532e2da2 100644 --- a/src/sage/modular/modform/theta.py +++ b/src/sage/modular/modform/theta.py @@ -5,7 +5,6 @@ William Stein """ -from six.moves import range from sage.rings.all import Integer, ZZ, PowerSeriesRing from math import sqrt diff --git a/src/sage/modular/modform/vm_basis.py b/src/sage/modular/modform/vm_basis.py index ca9e07da858..bc4b412dd19 100644 --- a/src/sage/modular/modform/vm_basis.py +++ b/src/sage/modular/modform/vm_basis.py @@ -27,14 +27,13 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range import math from sage.rings.all import QQ, ZZ, Integer, \ PolynomialRing, PowerSeriesRing, O as bigO from sage.structure.all import Sequence from sage.libs.flint.fmpz_poly import Fmpz_poly -from sage.misc.all import verbose +from sage.misc.verbose import verbose from .eis_series_cython import eisenstein_series_poly diff --git a/src/sage/modular/modform/weight1.py b/src/sage/modular/modform/weight1.py index 1a6e0a3db6f..28a89796dd1 100644 --- a/src/sage/modular/modform/weight1.py +++ b/src/sage/modular/modform/weight1.py @@ -13,7 +13,7 @@ from sage.misc.cachefunc import cached_function from sage.rings.all import PowerSeriesRing, ZZ -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.structure.sequence import Sequence from sage.modular.arithgroup.all import Gamma0, GammaH from sage.modular.arithgroup.arithgroup_generic import ArithmeticSubgroup diff --git a/src/sage/modular/modform_hecketriangle/abstract_space.py b/src/sage/modular/modform_hecketriangle/abstract_space.py index d0cbe4c5df8..0e429a25268 100644 --- a/src/sage/modular/modform_hecketriangle/abstract_space.py +++ b/src/sage/modular/modform_hecketriangle/abstract_space.py @@ -1837,7 +1837,7 @@ def _quasi_form_matrix(self, min_exp=0, order_1=ZZ(0), incr_prec_by=0): # of the column size until A has maximal rank: if (A.rank() < column_size): if (incr_prec_by == 0): - from sage.misc.misc import verbose + from sage.misc.verbose import verbose verbose("Encountered a base change matrix with not-yet-maximal rank (rare, please report)!") incr_prec_by += column_size//ZZ(5) + 1 return self._quasi_form_matrix(min_exp=min_exp, order_1=order_1, incr_prec_by=incr_prec_by) diff --git a/src/sage/modular/modform_hecketriangle/graded_ring_element.py b/src/sage/modular/modform_hecketriangle/graded_ring_element.py index 4b6ac52bfb1..edc715fafb3 100644 --- a/src/sage/modular/modform_hecketriangle/graded_ring_element.py +++ b/src/sage/modular/modform_hecketriangle/graded_ring_element.py @@ -17,7 +17,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.misc import six from sage.rings.all import ZZ, infinity, LaurentSeries, O from sage.functions.all import exp from sage.rings.number_field.number_field import QuadraticField @@ -42,10 +41,8 @@ # corresponding operations (e.g. __pow__) even though the category # (and class) of the parent is in some cases not # CommutativeAlgebras but Modules -class FormsRingElement(six.with_metaclass( - InheritComparisonClasscallMetaclass, - CommutativeAlgebraElement, UniqueRepresentation - )): +class FormsRingElement(CommutativeAlgebraElement, UniqueRepresentation, + metaclass=InheritComparisonClasscallMetaclass): r""" Element of a FormsRing. """ diff --git a/src/sage/modular/modform_hecketriangle/readme.py b/src/sage/modular/modform_hecketriangle/readme.py index 6dd4d227e24..c5455b39a88 100644 --- a/src/sage/modular/modform_hecketriangle/readme.py +++ b/src/sage/modular/modform_hecketriangle/readme.py @@ -602,7 +602,7 @@ - Specifying the form as a rational function in the basic generators (see below) - For weakly holomorphic modular forms it is possible to exactly determine the form by specifying (sufficiently many) initial coefficients of its Fourier expansion. - - There is even hope (no garantuee) to determine a (exact) form from + - There is even hope (no guarantee) to determine a (exact) form from the initial numerical coefficients (see below). - By specifying the coefficients with respect to a basis of the space (if the corresponding space supports coordinate vectors) @@ -633,15 +633,18 @@ sage: MF = ModularForms(k=12, ep=1) sage: (x,y,z,d) = MF.pol_ring().gens() - Using existing functions: + Using existing functions:: + sage: CF.Delta() q + 17/(56*d)*q^2 + 88887/(2458624*d^2)*q^3 + 941331/(481890304*d^3)*q^4 + O(q^5) - Using rational function in the basic generators: + Using rational function in the basic generators:: + sage: MF(x^3) 1 + 720*q + 179280*q^2 + 16954560*q^3 + 396974160*q^4 + O(q^5) - Using Fourier expansions: + Using Fourier expansions:: + sage: qexp = CF.Delta().q_expansion(prec=2) sage: qexp q + O(q^2) @@ -650,11 +653,13 @@ sage: MF(qexp) q - 24*q^2 + 252*q^3 - 1472*q^4 + O(q^5) - Using coordinate vectors: + Using coordinate vectors:: + sage: MF([0,1]) == MF.f_inf() True - Using arithmetic expressions: + Using arithmetic expressions:: + sage: d = CF.get_d() sage: CF.f_rho()^7 / (d*CF.f_rho()^7 - d*CF.f_i()^2) == CF.j_inv() True @@ -741,7 +746,8 @@ sage: ModularFormsRing(n=5).Delta() == ModularFormsRing(n=5).f_inf()*ModularFormsRing(n=5).f_rho()^4 True - The basic generators in some arithmetic cases: + The basic generators in some arithmetic cases:: + sage: ModularForms(n=3, k=6).E6() 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 + O(q^5) sage: ModularForms(n=4, k=6).E6() @@ -749,7 +755,8 @@ sage: ModularForms(n=infinity, k=4).E4() 1 + 16*q + 112*q^2 + 448*q^3 + 1136*q^4 + O(q^5) - General Eisenstein series in some arithmetic cases: + General Eisenstein series in some arithmetic cases:: + sage: ModularFormsRing(n=4).EisensteinSeries(k=8) (-25*f_rho^4 - 9*f_i^2)/(-34) sage: ModularForms(n=3, k=12).EisensteinSeries() @@ -799,7 +806,8 @@ sage: QuasiModularForms(n=4, k=2, ep=-1).E2() 1 - 8*q - 40*q^2 - 32*q^3 - 104*q^4 + O(q^5) - A quasi weak form can be constructed by using its initial Laurent expansion: + A quasi weak form can be constructed by using its initial Laurent expansion:: + sage: QF = QuasiWeakModularForms(n=8, k=10/3, ep=-1) sage: qexp = (QF.quasi_part_gens(min_exp=-1)[4]).q_expansion(prec=5) sage: qexp @@ -811,7 +819,8 @@ sage: QF(qexp).reduced_parent() QuasiWeakModularForms(n=8, k=10/3, ep=-1) over Integer Ring - Derivatives of (quasi weak) modular forms are again quasi (weak) modular forms: + Derivatives of (quasi weak) modular forms are again quasi (weak) modular forms:: + sage: CF.f_inf().derivative() == CF.f_inf()*CF.E2() True diff --git a/src/sage/modular/modsym/ambient.py b/src/sage/modular/modsym/ambient.py index 8476db2c9a9..ef8d789639e 100644 --- a/src/sage/modular/modsym/ambient.py +++ b/src/sage/modular/modsym/ambient.py @@ -70,12 +70,11 @@ class ``ModularSymbolsAmbient``, derived from # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ################################################################################ -from six.moves import range # Sage packages import sage.misc.latex as latex -import sage.misc.misc as misc +from sage.misc.verbose import verbose import sage.matrix.matrix_space as matrix_space from sage.modular.arithgroup.arithgroup_element import M2Z @@ -85,7 +84,7 @@ class ``ModularSymbolsAmbient``, derived from import sage.modular.dirichlet as dirichlet import sage.modular.hecke.all as hecke from sage.rings.all import Integer, QQ, ZZ, Ring -from sage.arith.all import is_prime, gcd, divisors, number_of_divisors, crt +from sage.arith.all import is_prime, divisors, number_of_divisors, crt import sage.rings.polynomial.multi_polynomial_element import sage.structure.formal_sum as formal_sum import sage.categories.all as cat @@ -982,7 +981,7 @@ def _compute_hecke_matrix_prime(self, p, rows=None): self._hecke_matrices = {} except KeyError: pass - tm = misc.verbose("Computing Hecke operator T_%s"%p) + tm = verbose("Computing Hecke operator T_%s"%p) if is_prime(p): H = heilbronn.HeilbronnCremona(p) @@ -1007,15 +1006,15 @@ def _compute_hecke_matrix_prime(self, p, rows=None): # W[j,f] = W[j,f] + s*K(x) W.add_to_entry(j, f, s * K(x)) j += 1 - tm = misc.verbose("start matrix multiply",tm) + tm = verbose("start matrix multiply",tm) if hasattr(W, '_matrix_times_matrix_dense'): Tp = W._matrix_times_matrix_dense(R) - misc.verbose("done matrix multiply and computing Hecke operator",tm) + verbose("done matrix multiply and computing Hecke operator",tm) else: Tp = W * R - tm = misc.verbose("done matrix multiply",tm) + tm = verbose("done matrix multiply",tm) Tp = Tp.dense_matrix() - misc.verbose("done making Hecke operator matrix dense",tm) + verbose("done making Hecke operator matrix dense",tm) self._hecke_matrices[(p,rows)] = Tp return Tp @@ -2182,21 +2181,15 @@ def twisted_winding_element(self, i, eps): sage: M = ModularSymbols(37,2,0,K) sage: M.twisted_winding_element(0,eps) 2*(1,23) - 2*(1,32) + 2*(1,34) - """ - if not dirichlet.is_DirichletCharacter(eps): raise TypeError("eps must be a Dirichlet character.") - if (i < 0) or (i > self.weight()-2): + if (i < 0) or (i > self.weight() - 2): raise ValueError("i must be between 0 and k-2.") m = eps.modulus() - s = self(0) - - for a in ([ x for x in range(1,m) if gcd(x, m) == 1 ]): - s += eps(a) * self.modular_symbol([i, Cusp(0), Cusp(a/m)]) - - return s + return self.sum(eps(a) * self.modular_symbol([i, Cusp(0), Cusp(a / m)]) + for a in m.coprime_integers(m)) ###################################################################### # Z-module of integral modular symbols. @@ -2851,7 +2844,7 @@ def _compute_hecke_matrix_prime(self, p, rows=None): self._hecke_matrices = {} except KeyError: pass - tm = misc.verbose("Computing Hecke operator T_%s"%p) + tm = verbose("Computing Hecke operator T_%s"%p) H = heilbronn.HeilbronnCremona(p) ##H = heilbronn.HeilbronnMerel(p) @@ -2865,7 +2858,7 @@ def _compute_hecke_matrix_prime(self, p, rows=None): R = self.manin_gens_to_basis() W = R.new_matrix(nrows=len(B), ncols = R.nrows()) # the 0 with given number of rows and cols. j = 0 - tm = misc.verbose("Matrix non-reduced", tm) + tm = verbose("Matrix non-reduced", tm) for i in B: # The following step is where most of the time is spent. c,d = P1[i] @@ -2888,17 +2881,17 @@ def _compute_hecke_matrix_prime(self, p, rows=None): if s != 0: W[j,f] = W[j,f] + s*m j += 1 - tm = misc.verbose("done making non-reduced matrix",tm) - misc.verbose("start matrix-matrix (%s x %s) times (%s x %s) multiply to get Tp"%(W.nrows(), W.ncols(), + tm = verbose("done making non-reduced matrix",tm) + verbose("start matrix-matrix (%s x %s) times (%s x %s) multiply to get Tp"%(W.nrows(), W.ncols(), R.nrows(), R.ncols())) if hasattr(W, '_matrix_times_matrix_dense'): Tp = W._matrix_times_matrix_dense(R) - misc.verbose("done matrix multiply and computing Hecke operator",tm) + verbose("done matrix multiply and computing Hecke operator",tm) else: Tp = W * R - tm = misc.verbose("done multiplying",tm) + tm = verbose("done multiplying",tm) Tp = Tp.dense_matrix() - misc.verbose("done making Hecke operator dense", tm) + verbose("done making Hecke operator dense", tm) if rows is None: self._hecke_matrices[(p,rows)] = Tp return Tp diff --git a/src/sage/modular/modsym/apply.pyx b/src/sage/modular/modsym/apply.pyx index a4a1bf6470b..ff22f208c00 100644 --- a/src/sage/modular/modsym/apply.pyx +++ b/src/sage/modular/modsym/apply.pyx @@ -1,3 +1,5 @@ +# distutils: extra_compile_args = -D_XPG6 + """ Monomial expansion of `(aX + bY)^i (cX + dY)^{j-i}` """ diff --git a/src/sage/modular/modsym/boundary.py b/src/sage/modular/modsym/boundary.py index 55f3b46a823..736d4fd0e50 100644 --- a/src/sage/modular/modsym/boundary.py +++ b/src/sage/modular/modsym/boundary.py @@ -89,9 +89,8 @@ # **************************************************************************** from __future__ import absolute_import -from six.moves import range -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.structure.richcmp import richcmp_method, richcmp import sage.modules.free_module as free_module diff --git a/src/sage/modular/modsym/element.py b/src/sage/modular/modsym/element.py index fd7504a6a3b..bfeffc20ead 100644 --- a/src/sage/modular/modsym/element.py +++ b/src/sage/modular/modsym/element.py @@ -22,7 +22,7 @@ import sage.modules.free_module_element -import sage.misc.misc as misc +from sage.misc.repr import repr_lincomb import sage.structure.formal_sum as formal_sum import sage.modular.hecke.all as hecke import sage.misc.latex as latex @@ -144,7 +144,7 @@ def _repr_(self): m = self.manin_symbol_rep() elif _print_mode == "modular": m = self.modular_symbol_rep() - return misc.repr_lincomb([(t,c) for c,t in m]) + return repr_lincomb([(t,c) for c,t in m]) def _latex_(self): r""" diff --git a/src/sage/modular/modsym/g1list.py b/src/sage/modular/modsym/g1list.py index 67649e0300e..1515f7bf935 100644 --- a/src/sage/modular/modsym/g1list.py +++ b/src/sage/modular/modsym/g1list.py @@ -18,7 +18,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from sage.arith.all import GCD from sage.structure.richcmp import richcmp_method, richcmp from sage.structure.sage_object import SageObject diff --git a/src/sage/modular/modsym/heilbronn.pyx b/src/sage/modular/modsym/heilbronn.pyx index 823458318e3..89a88c25aa1 100644 --- a/src/sage/modular/modsym/heilbronn.pyx +++ b/src/sage/modular/modsym/heilbronn.pyx @@ -1,3 +1,5 @@ +# distutils: extra_compile_args = -D_XPG6 + """ Heilbronn matrix computation """ @@ -19,7 +21,7 @@ Heilbronn matrix computation from cysignals.memory cimport check_allocarray, sig_malloc, sig_free from cysignals.signals cimport sig_on, sig_off, sig_check -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.arith.misc import is_prime from sage.libs.gmp.mpz cimport * diff --git a/src/sage/modular/modsym/manin_symbol_list.py b/src/sage/modular/modsym/manin_symbol_list.py index 154c09d6ed0..c6d948e8e56 100644 --- a/src/sage/modular/modsym/manin_symbol_list.py +++ b/src/sage/modular/modsym/manin_symbol_list.py @@ -33,7 +33,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import -from six.moves import range import sage.modular.modsym.p1list as p1list import sage.modular.modsym.g1list as g1list diff --git a/src/sage/modular/modsym/modular_symbols.py b/src/sage/modular/modsym/modular_symbols.py index bc4dcd662a3..7b0f75daf23 100644 --- a/src/sage/modular/modsym/modular_symbols.py +++ b/src/sage/modular/modsym/modular_symbols.py @@ -31,7 +31,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range import sage.modular.cusps as cusps from sage.modular.modsym.apply import apply_to_monomial from sage.modular.modsym.manin_symbol import ManinSymbol diff --git a/src/sage/modular/modsym/p1list.pyx b/src/sage/modular/modsym/p1list.pyx index 841b920f68e..3e0b6d25421 100644 --- a/src/sage/modular/modsym/p1list.pyx +++ b/src/sage/modular/modsym/p1list.pyx @@ -1,3 +1,6 @@ +# distutils: libraries = gmp zn_poly +# distutils: extra_compile_args = -D_XPG6 + r""" Lists of Manin symbols (elements of `\mathbb{P}^1(\ZZ/N\ZZ)`) over `\QQ` """ diff --git a/src/sage/modular/modsym/relation_matrix.py b/src/sage/modular/modsym/relation_matrix.py index 150f65fad36..bc420221cd2 100644 --- a/src/sage/modular/modsym/relation_matrix.py +++ b/src/sage/modular/modsym/relation_matrix.py @@ -22,13 +22,13 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import -from six.moves import range import sage.matrix.matrix_space as matrix_space from sage.rings.all import Ring from sage.misc.search import search from sage.rings.rational_field import is_RationalField -import sage.misc.misc as misc +from sage.misc.verbose import verbose + from sage.modular.modsym.manin_symbol_list import ManinSymbolList SPARSE = True @@ -115,7 +115,7 @@ def modS_relations(syms): """ if not isinstance(syms, ManinSymbolList): raise TypeError("syms must be a ManinSymbolList") - tm = misc.verbose() + tm = verbose() # We will fill in this set with the relations x_i + s*x_j = 0, # where the notation is as in _sparse_2term_quotient. rels = set() @@ -126,7 +126,7 @@ def modS_relations(syms): rels.add(((i, 1), (j, s))) else: rels.add(((j, s), (i, 1))) - misc.verbose("finished creating S relations", tm) + verbose("finished creating S relations", tm) return rels @@ -181,7 +181,7 @@ def modI_relations(syms, sign): 1585 paper! Thus our +1 eigenspace is his -1 eigenspace, etc. We do this for consistency with MAGMA. """ - tm = misc.verbose() + tm = verbose() # We will fill in this set with the relations x_i - sign*s*x_j = 0, # where the notation is as in _sparse_2term_quotient. rels = set() @@ -189,7 +189,7 @@ def modI_relations(syms, sign): j, s = syms.apply_I(i) assert j != -1 rels.add(((i, 1), (j, -sign * s))) - misc.verbose("finished creating I relations", tm) + verbose("finished creating I relations", tm) return rels @@ -225,7 +225,7 @@ def T_relation_matrix_wtk_g0(syms, mod, field, sparse): sage: T_relation_matrix_wtk_g0(L, modS, GF(17), True) 72 x 216 sparse matrix over Finite Field of size 17 (use the '.str()' method to see the entries) """ - tm = misc.verbose() + tm = verbose() row = 0 entries = {} already_seen = set() @@ -254,7 +254,7 @@ def T_relation_matrix_wtk_g0(syms, mod, field, sparse): R = MAT(entries) if not sparse: R = R.dense_matrix() - misc.verbose("finished (number of rows=%s)" % row, tm) + verbose("finished (number of rows=%s)" % row, tm) return R @@ -301,22 +301,22 @@ def gens_to_basis_matrix(syms, relation_matrix, mod, field, sparse): if not isinstance(mod, list): raise TypeError("mod must be a list") - misc.verbose(str(relation_matrix.parent())) + verbose(str(relation_matrix.parent())) try: h = relation_matrix.height() except AttributeError: h = 9999999 - tm = misc.verbose("putting relation matrix in echelon form (height = %s)" % h) + tm = verbose("putting relation matrix in echelon form (height = %s)" % h) if h < 10: A = relation_matrix.echelon_form(algorithm='multimodular', height_guess=1) else: A = relation_matrix.echelon_form() A.set_immutable() - tm = misc.verbose('finished echelon', tm) + tm = verbose('finished echelon', tm) - tm = misc.verbose("Now creating gens --> basis mapping") + tm = verbose("Now creating gens --> basis mapping") basis_set = set(A.nonpivots()) pivots = A.pivots() @@ -328,9 +328,9 @@ def gens_to_basis_matrix(syms, relation_matrix, mod, field, sparse): ONE = field(1) - misc.verbose("done doing setup", tm) + verbose("done doing setup", tm) - tm = misc.verbose("now forming quotient matrix") + tm = verbose("now forming quotient matrix") M = matrix_space.MatrixSpace(field, len(syms), len(basis), sparse=sparse) B = M(0) @@ -346,18 +346,18 @@ def gens_to_basis_matrix(syms, relation_matrix, mod, field, sparse): # the non-pivot columns of A: B._set_row_to_negative_of_row_of_A_using_subset_of_columns(i, A, r, basis, cols_index) - misc.verbose("done making quotient matrix", tm) + verbose("done making quotient matrix", tm) # The following is very fast (over Q at least). - tm = misc.verbose('now filling in the rest of the matrix') + tm = verbose('now filling in the rest of the matrix') k = 0 for i in range(len(mod)): j, s = mod[i] if j != i and s != 0: # ignored in the above matrix k += 1 B.set_row_to_multiple_of_row(i, j, s) - misc.verbose("set %s rows" % k) - tm = misc.verbose("time to fill in rest of matrix", tm) + verbose("set %s rows" % k) + tm = verbose("time to fill in rest of matrix", tm) return B, basis @@ -555,7 +555,7 @@ def sparse_2term_quotient(rels, n, F): if not isinstance(F, Ring): raise TypeError("F must be a ring.") - tm = misc.verbose("Starting sparse 2-term quotient...") + tm = verbose("Starting sparse 2-term quotient...") free = list(range(n)) ONE = F.one() ZERO = F.zero() @@ -597,5 +597,5 @@ def sparse_2term_quotient(rels, n, F): coef[die] = ZERO mod = [(free[i], coef[i]) for i in range(len(free))] - misc.verbose("finished", tm) + verbose("finished", tm) return mod diff --git a/src/sage/modular/modsym/relation_matrix_pyx.pyx b/src/sage/modular/modsym/relation_matrix_pyx.pyx index 5ee982e4228..f87c796efa8 100644 --- a/src/sage/modular/modsym/relation_matrix_pyx.pyx +++ b/src/sage/modular/modsym/relation_matrix_pyx.pyx @@ -9,7 +9,7 @@ Optimized Cython code for computing relation matrices in certain cases # https://www.gnu.org/licenses/ ############################################################################# -import sage.misc.misc as misc +from sage.misc.verbose import verbose from sage.rings.rational cimport Rational @@ -54,7 +54,7 @@ def sparse_2term_quotient_only_pm1(rels, n): """ n = int(n) - tm = misc.verbose("Starting optimized integer sparse 2-term quotient...") + tm = verbose("Starting optimized integer sparse 2-term quotient...") cdef int c0, c1, i, die cdef list free = list(xrange(n)) @@ -103,5 +103,5 @@ def sparse_2term_quotient_only_pm1(rels, n): # without this special case.) mod = [(fi, Rational(ci)) for fi, ci in zip(free, coef)] - misc.verbose("finished", tm) + verbose("finished", tm) return mod diff --git a/src/sage/modular/modsym/space.py b/src/sage/modular/modsym/space.py index 220eac9eba4..67e0793c223 100644 --- a/src/sage/modular/modsym/space.py +++ b/src/sage/modular/modsym/space.py @@ -7,7 +7,7 @@ """ from __future__ import absolute_import -#***************************************************************************** +# **************************************************************************** # Sage: System for Algebra and Geometry Experimentation # # Copyright (C) 2005 William Stein @@ -21,33 +21,34 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** -from six.moves import range +# https://www.gnu.org/licenses/ +# **************************************************************************** +from sage.categories.fields import Fields import sage.modules.free_module as free_module import sage.matrix.matrix_space as matrix_space -from sage.modules.free_module_element import is_FreeModuleElement +from sage.modules.free_module_element import FreeModuleElement from sage.modules.free_module import EchelonMatrixKey -from sage.misc.all import verbose, prod +from sage.misc.all import prod import sage.modular.hecke.all as hecke -import sage.arith.all as arith -import sage.rings.fast_arith as fast_arith -from sage.rings.all import PowerSeriesRing, Integer, O, QQ, ZZ, infinity, Zmod +from sage.arith.all import divisors, next_prime +from sage.rings.fast_arith import prime_range +from sage.rings.all import PowerSeriesRing, Integer, QQ, ZZ, infinity, Zmod from sage.rings.number_field.number_field_base import is_NumberField -from sage.structure.all import Sequence, SageObject +from sage.structure.all import Sequence, SageObject from sage.structure.richcmp import (richcmp_method, richcmp, rich_to_bool, richcmp_not_equal) -from sage.modular.arithgroup.all import Gamma0, is_Gamma0 # for Sturm bound given a character +from sage.modular.arithgroup.all import Gamma0, is_Gamma0 # for Sturm bound given a character from sage.modular.modsym.element import ModularSymbolsElement from . import hecke_operator from sage.misc.cachefunc import cached_method + def is_ModularSymbolsSpace(x): r""" - Return True if x is a space of modular symbols. + Return ``True`` if ``x`` is a space of modular symbols. EXAMPLES:: @@ -86,7 +87,7 @@ def __init__(self, group, weight, character, sign, base_ring, category=None): def __richcmp__(self, other, op): """ - Compare self and other. + Compare ``self`` and ``other``. When spaces are in a common ambient space, we order lexicographically by the sequence of traces of Hecke operators @@ -154,7 +155,7 @@ def __richcmp__(self, other, op): # distinguish using Hecke operators, if possible. try: - for p in fast_arith.prime_range(self.hecke_bound()): + for p in prime_range(self.hecke_bound()): ap = self.hecke_matrix(p).trace() bp = other.hecke_matrix(p).trace() if ap != bp: @@ -167,7 +168,7 @@ def __richcmp__(self, other, op): def _hecke_operator_class(self): """ Return the class to be used for instantiating Hecke operators - acting on self. + acting on ``self``. EXAMPLES:: @@ -179,16 +180,17 @@ def _hecke_operator_class(self): def compact_system_of_eigenvalues(self, v, names='alpha', nz=None): r""" Return a compact system of eigenvalues `a_n` for - `n\in v`. This should only be called on simple factors of - modular symbols spaces. + `n\in v`. + + This should only be called on simple factors of modular + symbols spaces. INPUT: - ``v`` - a list of positive integers - - ``nz`` - (default: None); if given specifies a column index + - ``nz`` - (default: ``None``); if given specifies a column index such that the dual module has that column nonzero. - OUTPUT: - ``E`` - matrix such that E\*v is a vector with components @@ -237,7 +239,7 @@ def compact_system_of_eigenvalues(self, v, names='alpha', nz=None): def character(self): """ - Return the character associated to self. + Return the character associated to ``self``. EXAMPLES:: @@ -250,11 +252,11 @@ def character(self): def cuspidal_submodule(self): """ - Return the cuspidal submodule of self. + Return the cuspidal submodule of ``self``. - .. note:: + .. NOTE:: - This should be overridden by all derived classes. + This should be overridden by all derived classes. EXAMPLES:: @@ -354,9 +356,9 @@ def dual_star_involution_matrix(self): Return the matrix of the dual star involution, which is induced by complex conjugation on the linear dual of modular symbols. - .. note:: + .. NOTE:: - This should be overridden in all derived classes. + This should be overridden in all derived classes. EXAMPLES:: @@ -373,21 +375,18 @@ def dual_star_involution_matrix(self): def group(self): """ - Returns the group of this modular symbols space. + Return the group of this modular symbols space. INPUT: - - ``ModularSymbols self`` - an arbitrary space of modular symbols - OUTPUT: - ``CongruenceSubgroup`` - the congruence subgroup that this is a space of modular symbols for. - ALGORITHM: The group is recorded when this space is created. EXAMPLES:: @@ -400,7 +399,7 @@ def group(self): def is_ambient(self): """ - Return True if self is an ambient space of modular symbols. + Return ``True`` if ``self`` is an ambient space of modular symbols. EXAMPLES:: @@ -414,11 +413,11 @@ def is_ambient(self): def is_cuspidal(self): """ - Return True if self is a cuspidal space of modular symbols. + Return ``True`` if ``self`` is a cuspidal space of modular symbols. - .. note:: + .. NOTE:: - This should be overridden in all derived classes. + This should be overridden in all derived classes. EXAMPLES:: @@ -492,11 +491,11 @@ def multiplicity(self, S, check_simple=True): d = C.rank() n = S.rank() assert d % n == 0, "the dimension of intersection must be a multiple of dimension of simple space. bug!" - return d//n + return d // n def ngens(self): """ - The number of generators of self. + Return the number of generators of ``self``. INPUT: @@ -507,7 +506,6 @@ def ngens(self): - ``int`` - the number of generators, which is the same as the dimension of self. - ALGORITHM: Call the dimension function. EXAMPLES:: @@ -606,13 +604,13 @@ def set_precision(self, prec): def q_expansion_basis(self, prec=None, algorithm='default'): r""" - Returns a basis of q-expansions (as power series) to precision prec - of the space of modular forms associated to self. The q-expansions - are defined over the same base ring as self, and a put in echelon - form. + Return a basis of q-expansions (as power series) to precision prec + of the space of modular forms associated to ``self``. - INPUT: + The q-expansions are defined over the same base ring as ``self``, + and a put in echelon form. + INPUT: - ``self`` - a space of CUSPIDAL modular symbols @@ -714,7 +712,7 @@ def q_expansion_basis(self, prec=None, algorithm='default'): prec = Integer(prec) if prec < 1: - raise ValueError("prec (=%s) must be >= 1"%prec) + raise ValueError("prec (=%s) must be >= 1" % prec) if not self.is_cuspidal(): raise ArithmeticError("space must be cuspidal") @@ -736,18 +734,19 @@ def q_expansion_basis(self, prec=None, algorithm='default'): B1 = self._q_expansion_basis_hecke_dual(prec) B2 = self._q_expansion_basis_eigen(prec, 'alpha') if B1 != B2: - raise RuntimeError("There is a bug in q_expansion_basis -- basis computed differently with two algorithms:\n%s\n%s\n"%(B1, B2,)) + raise RuntimeError("There is a bug in q_expansion_basis -- basis computed differently with two algorithms:\n%s\n%s\n" % (B1, B2,)) return Sequence(B1, cr=True) else: - raise ValueError("no algorithm '%s'"%algorithm) + raise ValueError("no algorithm '%s'" % algorithm) - def q_expansion_module(self, prec = None, R=None): + def q_expansion_module(self, prec=None, R=None): r""" Return a basis over R for the space spanned by the coefficient - vectors of the `q`-expansions corresponding to self. If R - is not the base ring of self, returns the restriction of scalars - down to R (for this, self must have base ring `\QQ` - or a number field). + vectors of the `q`-expansions corresponding to ``self``. + + If R is not the base ring of ``self``, this returns the + restriction of scalars down to R (for this, self must have + base ring `\QQ` or a number field). INPUT: @@ -761,9 +760,11 @@ def q_expansion_module(self, prec = None, R=None): OUTPUT: A free module over R. - TODO - extend to more general R (though that is fairly easy for the - user to get by just doing base_extend or change_ring on the - output of this function). + .. TODO:: + + extend to more general R (though that is fairly easy for the + user to get by just doing base_extend or change_ring on the + output of this function). Note that the prec needed to distinguish elements of the restricted-down-to-R basis may be bigger than ``self.hecke_bound()``, @@ -886,7 +887,7 @@ def q_expansion_module(self, prec = None, R=None): EXAMPLES WITH SIGN 0 and R=QQ: - TODO - this doesn't work yet as it's not implemented!! + .. TODO:: This doesn't work yet as it's not implemented!! :: @@ -917,16 +918,17 @@ def q_expansion_module(self, prec = None, R=None): elif R == QQ: return self._q_expansion_module_rational(prec) elif R is None or R == self.base_ring(): - ## names is never used in this case + # names is never used in this case return self._q_expansion_module(prec) else: raise NotImplementedError("R must be ZZ, QQ, or the base ring of the modular symbols space.") def _q_eigenform_images(self, A, prec, names): """ - Return list of images in space corresponding to self of eigenform - corresponding to A under the degeneracy maps. This is mainly a - helper function for other internal functions. + Return list of images in space corresponding to ``self`` of eigenform + corresponding to A under the degeneracy maps. + + This is mainly a helper function for other internal functions. INPUT: @@ -937,7 +939,6 @@ def _q_eigenform_images(self, A, prec, names): - ``prec`` - a positive integer - EXAMPLES:: sage: M = ModularSymbols(33,2,sign=1) @@ -949,16 +950,17 @@ def _q_eigenform_images(self, A, prec, names): f = A.q_eigenform(prec, names) if A.level() == self.level(): return [f] - D = arith.divisors(self.level() // A.level()) + D = divisors(self.level() // A.level()) q = f.parent().gen() return [f] + [f(q**d) for d in D if d > 1] def _q_expansion_module(self, prec, algorithm='hecke'): """ - Return module spanned by the `q`-expansions corresponding to self. See - the docstring for ``q_expansion_module`` (without underscore) for - further details. Note that this will not work if ``algorithm=eigen`` - and the sign is 0. + Return module spanned by the `q`-expansions corresponding to ``self``. + + See the docstring for ``q_expansion_module`` (without + underscore) for further details. Note that this will not work + if ``algorithm=eigen`` and the sign is 0. EXAMPLES:: @@ -970,12 +972,11 @@ def _q_expansion_module(self, prec, algorithm='hecke'): Vector space of degree 4 and dimension 1 over Number Field in b with defining polynomial x^2 + 7 with b = 2.645751311064591?*I Basis matrix: [ 0 1 -2 -1] - """ if not self.is_cuspidal(): raise ValueError("self must be cuspidal") R = self.base_ring() - if not R.is_field(): + if R not in Fields(): if R == ZZ: return self._q_expansion_module_integral(prec) raise NotImplementedError("base ring must be a field (or ZZ).") @@ -985,10 +986,11 @@ def _q_expansion_module(self, prec, algorithm='hecke'): return V.span([f.padded_list(prec) for f in self.q_expansion_basis(prec, algorithm)]) if algorithm != 'eigen': - raise ValueError("unknown algorithm '%s'"%algorithm) + raise ValueError("unknown algorithm '%s'" % algorithm) def q_eigen_gens(d, f): - r""" Temporary function for internal use. + r""" + Temporary function for internal use. EXAMPLES:: @@ -1020,12 +1022,12 @@ def q_eigen_gens(d, f): def _q_expansion_module_rational(self, prec): r""" Return a vector space over `\QQ` for the space spanned by the - `q`-expansions corresponding to self. + `q`-expansions corresponding to ``self``. - The base ring of self must be - `\QQ` or a number field, and self must be cuspidal. The returned space - is a `\QQ`-vector space, where the coordinates are the coefficients of - `q`-expansions. + The base ring of ``self`` must be `\QQ` or a number field, and + ``self`` must be cuspidal. The returned space is a + `\QQ`-vector space, where the coordinates are the coefficients + of `q`-expansions. INPUT: @@ -1073,7 +1075,8 @@ def q_eigen_gens(d, f): # This looks like it might be really slow -- though # perhaps it's nothing compared to the time taken by # whatever computed this in the first place. - return [[(X[i].list())[j][k] for i in range(prec)] for j in range(d) for k in range(n)] + return [[(X[i].list())[j][k] for i in range(prec)] + for j in range(d) for k in range(n)] if self.sign() == 0: X = self.plus_submodule(compute_dual=True) else: @@ -1088,11 +1091,11 @@ def q_eigen_gens(d, f): def _q_expansion_module_integral(self, prec): r""" Return module over `\ZZ` for the space spanned by - the `q`-expansions corresponding to self. The base ring of - self must be `\QQ` or a number field, and self must - be cuspidal. The returned space is a `\ZZ`-module, - where the coordinates are the coefficients of - `q`-expansions. + the `q`-expansions corresponding to ``self``. + + The base ring of ``self`` must be `\QQ` or a number field, and + self must be cuspidal. The returned space is a `\ZZ`-module, + where the coordinates are the coefficients of `q`-expansions. EXAMPLES:: @@ -1110,11 +1113,10 @@ def _q_expansion_module_integral(self, prec): V = self.q_expansion_module(prec, QQ) return free_module.FreeModule(ZZ, V.degree()).span(V.basis()).saturation() - def congruence_number(self, other, prec=None): r""" Given two cuspidal spaces of modular symbols, compute the - congruence number, using prec terms of the `q`-expansions. + congruence number, using ``prec`` terms of the `q`-expansions. The congruence number is defined as follows. If `V` is the submodule of integral cusp forms corresponding to self (saturated in @@ -1123,7 +1125,7 @@ def congruence_number(self, other, prec=None): the congruence number is the index of `V+W` in its saturation in `\ZZ[[q]]`. - If prec is not given it is set equal to the max of the + If ``prec`` is not given it is set equal to the max of the ``hecke_bound`` function called on each space. EXAMPLES:: @@ -1140,9 +1142,9 @@ def congruence_number(self, other, prec=None): prec = max(self.hecke_bound(), other.hecke_bound()) prec = int(prec) - V = self.q_expansion_module(prec, ZZ) + V = self.q_expansion_module(prec, ZZ) W = other.q_expansion_module(prec, ZZ) - K = V+W + K = V + W return K.index_in_saturation() ######################################################################### @@ -1220,13 +1222,16 @@ def q_eigenform_character(self, names=None): G = DirichletGroup(self.level(), K) M = self.ambient_module() # act on right since v is a in the dual - b = [(M.diamond_bracket_matrix(u)*v)[i] / v[i] for u in G.unit_gens()] + b = [(M.diamond_bracket_matrix(u) * v)[i] / v[i] + for u in G.unit_gens()] return G(b) def q_eigenform(self, prec, names=None): """ - Returns the q-expansion to precision prec of a new eigenform - associated to self, where self must be new, cuspidal, and simple. + Return the q-expansion to precision ``prec`` of a new eigenform + associated to ``self``. + + Here ``self`` must be new, cuspidal, and simple. EXAMPLES:: @@ -1256,11 +1261,11 @@ def q_eigenform(self, prec, names=None): a2 = self.eigenvalue(2, names) R = PowerSeriesRing(a2.parent(), "q") q = R.gen(0) - f = q + a2*q**2 + O(q**3) + f = (q + a2 * q**2).O(3) if f.prec() < prec: R = f.parent() - ext = [self.eigenvalue(n, names) for n in range(f.prec(),prec)] + ext = [self.eigenvalue(n, names) for n in range(f.prec(), prec)] f = R(f.padded_list(f.prec()) + ext) self._q_expansion_dict[names] = f.add_bigoh(prec) return self._q_expansion_dict[names] @@ -1281,12 +1286,12 @@ def _q_expansion_basis_eigen(self, prec, names): # should we perhaps check at this point if self is new? f = self.q_eigenform(prec, names) R = PowerSeriesRing(self.base_ring(), 'q') - B = [R([f[i][j] for i in range(prec)],prec) for j in range(self.rank())] + B = [R([f[i][j] for i in range(prec)], prec) + for j in range(self.rank())] return B else: raise NotImplementedError - ######################################################################### # # Computation of a basis using linear functionals on the Hecke algebra. @@ -1295,12 +1300,12 @@ def _q_expansion_basis_eigen(self, prec, names): def q_expansion_cuspforms(self, prec=None): r""" - Returns a function f(i,j) such that each value f(i,j) is the + Return a function f(i,j) such that each value f(i,j) is the q-expansion, to the given precision, of an element of the - corresponding space `S` of cusp forms. Together these - functions span `S`. Here `i,j` are integers with - `0\leq i,j < d`, where `d` is the dimension of - self. + corresponding space `S` of cusp forms. + + Together these functions span `S`. Here `i,j` are integers + with `0\leq i,j < d`, where `d` is the dimension of ``self``. For a reduced echelon basis, use the function ``q_expansion_basis`` instead. @@ -1308,7 +1313,7 @@ def q_expansion_cuspforms(self, prec=None): More precisely, this function returns the `q`-expansions obtained by taking the `ij` entry of the matrices of the Hecke operators `T_n` acting on the subspace of the linear - dual of modular symbols corresponding to self. + dual of modular symbols corresponding to ``self``. EXAMPLES:: @@ -1348,9 +1353,9 @@ def q_expansion_cuspforms(self, prec=None): prec = self.default_prec() if not self.is_cuspidal(): raise ArithmeticError("self must be cuspidal") - T = [self.dual_hecke_matrix(n) for n in range(1,prec)] + T = [self.dual_hecke_matrix(n) for n in range(1, prec)] R = PowerSeriesRing(self.base_ring(), 'q') - return lambda i, j: R([0] + [t[i,j] for t in T], prec) + return lambda i, j: R([0] + [t[i, j] for t in T], prec) def _q_expansion_basis_hecke_dual(self, prec): r""" @@ -1366,38 +1371,38 @@ def _q_expansion_basis_hecke_dual(self, prec): sage: ModularSymbols(37, 2).cuspidal_submodule()._q_expansion_basis_hecke_dual(2) [q + O(q^2)] """ + from sage.misc.verbose import verbose d = self.dimension_of_associated_cuspform_space() prec = Integer(prec) if prec < 1: - raise ValueError("prec (=%s) must be >= 1"%prec) - if d >= prec-1: - d = prec-1 + raise ValueError("prec (=%s) must be >= 1" % prec) + if d >= prec - 1: + d = prec - 1 K = self.base_ring() - A = free_module.VectorSpace(K, prec-1) - M = matrix_space.MatrixSpace(K, prec-1, self.dimension()) + A = free_module.VectorSpace(K, prec - 1) + M = matrix_space.MatrixSpace(K, prec - 1, self.dimension()) V = A.zero_submodule() - i = self.dimension()-1 + i = self.dimension() - 1 j = 0 - t = verbose('computing basis to precision %s'%prec) + t = verbose('computing basis to precision %s' % prec) while V.dimension() < d and i >= 0: - v = [self.dual_hecke_matrix(n).column(i) for n in range(1,prec)] - t = verbose('iteration: %s'%j,t) + v = [self.dual_hecke_matrix(n).column(i) for n in range(1, prec)] + t = verbose('iteration: %s' % j, t) X = M(v).transpose() V += X.row_space() - t = verbose('addition of row space: %s'%j,t) + t = verbose('addition of row space: %s' % j, t) i -= 1 j += 1 R = PowerSeriesRing(K, 'q') B = V.basis() if len(B) < d: - B += [V(0)] * (d-len(B)) + B += [V(0)] * (d - len(B)) return [R([0] + b.list(), prec) for b in B] - ######################################################################### # # Decomposition of spaces @@ -1406,7 +1411,7 @@ def _q_expansion_basis_hecke_dual(self, prec): # def factorization(self): # """ -# Returns a list of pairs `(S,e)` where `S` is simple +# Return a list of pairs `(S,e)` where `S` is simple # spaces of modular symbols and self is isomorphic to the direct sum # of the `S^e` as a module over the *anemic* Hecke algebra # adjoin the star involution. @@ -1431,7 +1436,7 @@ def hecke_module_of_level(self, level): def sign(self): r""" - Return the sign of self. + Return the sign of ``self``. For efficiency reasons, it is often useful to compute in the (largest) quotient of modular symbols where the \* involution acts @@ -1439,15 +1444,12 @@ def sign(self): INPUT: - - ``ModularSymbols self`` - arbitrary space of modular symbols. - OUTPUT: - - - ``int`` - the sign of self, either -1, 0, or 1. + - ``int`` - the sign of ``self``, either -1, 0, or 1. - ``-1`` - if this is factor of quotient where \* acts as -1, @@ -1458,7 +1460,6 @@ def sign(self): - ``0`` - if this is full space of modular symbols (no quotient). - EXAMPLES:: sage: m = ModularSymbols(33) @@ -1481,10 +1482,11 @@ def sign(self): def simple_factors(self): """ - Returns a list modular symbols spaces `S` where `S` + Return a list modular symbols spaces `S` where `S` is simple spaces of modular symbols (for the anemic Hecke algebra) - and self is isomorphic to the direct sum of the `S` with + and ``self`` is isomorphic to the direct sum of the `S` with some multiplicities, as a module over the *anemic* Hecke algebra. + For the multiplicities use factorization() instead. ASSUMPTION: self is a module over the anemic Hecke algebra. @@ -1498,11 +1500,11 @@ def simple_factors(self): Modular Symbols subspace of dimension 1 of Modular Symbols space of dimension 3 for Gamma_0(1) of weight 16 with sign 0 over Finite Field of size 5, Modular Symbols subspace of dimension 1 of Modular Symbols space of dimension 3 for Gamma_0(1) of weight 16 with sign 0 over Finite Field of size 5] """ - return [S for S,_ in self.factorization()] + return [S for S, _ in self.factorization()] def star_eigenvalues(self): """ - Returns the eigenvalues of the star involution acting on self. + Return the eigenvalues of the star involution acting on ``self``. EXAMPLES:: @@ -1537,7 +1539,7 @@ def star_eigenvalues(self): def star_decomposition(self): r""" - Decompose self into subspaces which are eigenspaces for the star + Decompose ``self`` into subspaces which are eigenspaces for the star involution. EXAMPLES:: @@ -1591,14 +1593,16 @@ def integral_structure(self): def intersection_number(self, M): """ - Given modular symbols spaces self and M in some common ambient - space, returns the intersection number of these two spaces. This is - the index in their saturation of the sum of their underlying - integral structures. + Given modular symbols spaces ``self`` and ``M`` in some common ambient + space, returns the intersection number of these two spaces. + + This is the index in their saturation of the sum of their + underlying integral structures. - If self and M are of weight two and defined over QQ, and correspond - to newforms f and g, then this number equals the order of the - intersection of the modular abelian varieties attached to f and g. + If ``self`` and ``M`` are of weight two and defined over QQ, + and correspond to newforms f and g, then this number equals + the order of the intersection of the modular abelian varieties + attached to f and g. EXAMPLES:: @@ -1618,7 +1622,7 @@ def intersection_number(self, M): raise ValueError("self and M must be in the same ambient space.") A = self.integral_structure() B = M.integral_structure() - return (A+B).index_in_saturation() + return (A + B).index_in_saturation() def integral_basis(self): r""" @@ -1682,13 +1686,14 @@ def integral_basis(self): self.__integral_basis = tuple([self(b) for b in B]) return self.__integral_basis - def integral_hecke_matrix(self, n): r""" Return the matrix of the `n`th Hecke operator acting on the integral - structure on self (as returned by ``self.integral_structure()``). This - is often (but not always) different from the matrix returned by - ``self.hecke_matrix``, even if the latter has integral entries. + structure on ``self`` (as returned by ``self.integral_structure()``). + + This is often (but not always) different from the matrix + returned by ``self.hecke_matrix``, even if the latter has + integral entries. EXAMPLES:: @@ -1715,7 +1720,6 @@ def integral_hecke_matrix(self, n): self.__integral_hecke_matrix = {} except KeyError: pass - #raise NotImplementedError, "code past this point is broken / not done" # todo A = self.ambient_hecke_module() T = A.hecke_matrix(n) S = T.restrict(self.integral_structure()).change_ring(ZZ) @@ -1724,7 +1728,7 @@ def integral_hecke_matrix(self, n): def sturm_bound(self): r""" - Returns the Sturm bound for this space of modular symbols. + Return the Sturm bound for this space of modular symbols. Type ``sturm_bound?`` for more details. @@ -1753,19 +1757,15 @@ def sturm_bound(self): self.__sturm_bound = self.group().sturm_bound(self.weight()) return self.__sturm_bound - def plus_submodule(self, compute_dual=True): """ - Return the subspace of self on which the star involution acts as - +1. + Return the subspace of ``self`` on which the star involution acts as +1. INPUT: - - - ``compute_dual`` - bool (default: True) also + - ``compute_dual`` - bool (default: ``True``) also compute dual subspace. This are useful for many algorithms. - OUTPUT: subspace of modular symbols EXAMPLES:: @@ -1779,12 +1779,10 @@ def plus_submodule(self, compute_dual=True): def minus_submodule(self, compute_dual=True): """ - Return the subspace of self on which the star involution acts as - -1. + Return the subspace of ``self`` on which the star involution acts as -1. INPUT: - - ``compute_dual`` - bool (default: True) also compute dual subspace. This are useful for many algorithms. @@ -1801,15 +1799,16 @@ def minus_submodule(self, compute_dual=True): def _compute_sign_submodule(self, sign, compute_dual=True): r""" - Compute the submodule of self which is an eigenspace for the star involution with the given sign. + Compute the submodule of ``self`` which is an eigenspace for the star involution with the given sign. INPUT: - sign (integer): 1 or -1 - - compute_dual (True or False, default True): also compute the dual submodule (useful for some algorithms) + - compute_dual (boolean, default ``True``): also compute the dual + submodule (useful for some algorithms) - OUTPUT: a submodule of self + OUTPUT: a submodule of ``self`` EXAMPLES:: @@ -1853,24 +1852,21 @@ def _set_sign(self, sign): sage: M._set_sign(0) """ sign = int(sign) - if not (sign in [-1,0,1]): - raise ValueError("sign (=%s) must be -1, 0, or 1"%sign) + if sign not in [-1, 0, 1]: + raise ValueError("sign (=%s) must be -1, 0, or 1" % sign) self.__sign = sign def sign_submodule(self, sign, compute_dual=True): """ - Return the subspace of self that is fixed under the star - involution. + Return the subspace of ``self`` that is fixed under the star involution. INPUT: - - ``sign`` - int (either -1, 0 or +1) - - ``compute_dual`` - bool (default: True) also + - ``compute_dual`` - bool (default: ``True``) also compute dual subspace. This are useful for many algorithms. - OUTPUT: subspace of modular symbols EXAMPLES:: @@ -1884,7 +1880,7 @@ def sign_submodule(self, sign, compute_dual=True): -1 """ sign = int(sign) - if not sign in [-1, 0, 1]: + if sign not in [-1, 0, 1]: raise ValueError("sign must be -1, 0 or 1") if self.sign() == sign: # an easy case return self @@ -1902,14 +1898,15 @@ def sign_submodule(self, sign, compute_dual=True): pass P = self._compute_sign_submodule(sign, compute_dual) P.__star_eigenvalue = sign - self.__plus_submodule[(sign,compute_dual)] = P + self.__plus_submodule[(sign, compute_dual)] = P return P def star_involution(self): """ - Return the star involution on self, which is induced by complex - conjugation on modular symbols. Not implemented in this abstract base - class. + Return the star involution on ``self``, which is induced by complex + conjugation on modular symbols. + + Not implemented in this abstract base class. EXAMPLES:: @@ -1926,11 +1923,9 @@ def abelian_variety(self): INPUT: - - ``self`` - modular symbols space of weight 2 for a congruence subgroup such as Gamma0, Gamma1 or GammaH. - EXAMPLES:: sage: ModularSymbols(Gamma0(11)).cuspidal_submodule().abelian_variety() @@ -1965,13 +1960,13 @@ def abelian_variety(self): def rational_period_mapping(self): r""" - Return the rational period mapping associated to self. + Return the rational period mapping associated to ``self``. - This is a homomorphism to a vector space whose kernel is the same as - the kernel of the period mapping associated to self. For this to exist, - self must be Hecke equivariant. + This is a homomorphism to a vector space whose kernel is the + same as the kernel of the period mapping associated to + ``self``. For this to exist, self must be Hecke equivariant. - Use ``integral_period_mapping`` to obtain a homomorphism to a + Use :meth:`integral_period_mapping` to obtain a homomorphism to a `\ZZ`-module, normalized so the image of integral modular symbols is exactly `\ZZ^n`. @@ -2013,15 +2008,13 @@ def rational_period_mapping(self): self.__rational_period_mapping = R return R - def integral_period_mapping(self): r""" - Return the integral period mapping associated to self. + Return the integral period mapping associated to ``self``. This is a homomorphism to a vector space whose kernel is the same - as the kernel of the period mapping associated to self, normalized - so the image of integral modular symbols is exactly - `\ZZ^n`. + as the kernel of the period mapping associated to ``self``, normalized + so the image of integral modular symbols is exactly `\ZZ^n`. EXAMPLES:: @@ -2085,13 +2078,12 @@ def integral_period_mapping(self): @cached_method def modular_symbols_of_sign(self, sign, bound=None): """ - Returns a space of modular symbols with the same defining + Return a space of modular symbols with the same defining properties (weight, level, etc.) and Hecke eigenvalues as this space except with given sign. INPUT: - - ``self`` - a cuspidal space of modular symbols - ``sign`` - an integer, one of -1, 0, or 1 @@ -2099,7 +2091,6 @@ def modular_symbols_of_sign(self, sign, bound=None): - ``bound`` - integer (default: None); if specified only use Hecke operators up to the given bound. - EXAMPLES:: sage: S = ModularSymbols(Gamma0(11),2,sign=0).cuspidal_subspace() @@ -2149,22 +2140,22 @@ def modular_symbols_of_sign(self, sign, bound=None): return self if sign != 0: if self.sign() == 0: - d = d//2 + d = d // 2 elif sign == 0: # self has nonzero sign - d = 2*d + d = 2 * d B = self.ambient_module().modular_symbols_of_sign(sign) p = 2 if bound is None: bound = self.hecke_bound() while B.dimension() > d and p <= bound: while self.level() % p == 0: - p = arith.next_prime(p) + p = next_prime(p) f = self.hecke_polynomial(p) - g = prod(g for g,_ in f.factor()) # square free part + g = prod(g for g, _ in f.factor()) # square free part t = B.hecke_operator(p) s = g(t) B = s.kernel() - p = arith.next_prime(p) + p = next_prime(p) return B ######################################################### @@ -2178,8 +2169,8 @@ def abvarquo_cuspidal_subgroup(self): the relevant modular Jacobian attached to this modular symbols space. - We assume that self is defined over QQ and has weight 2. If - the sign of self is not 0, then the power of 2 may be wrong. + We assume that ``self`` is defined over QQ and has weight 2. If + the sign of ``self`` is not 0, then the power of 2 may be wrong. EXAMPLES:: @@ -2195,8 +2186,10 @@ def abvarquo_cuspidal_subgroup(self): sage: [A.abvarquo_cuspidal_subgroup().invariants() for A in D] [(), (), ()] """ - try: return self.__abvarquo_cuspidal_subgroup - except AttributeError: pass + try: + return self.__abvarquo_cuspidal_subgroup + except AttributeError: + pass if self.base_ring() != QQ: raise ValueError("base ring must be QQ") if self.weight() != 2: @@ -2209,14 +2202,14 @@ def abvarquo_cuspidal_subgroup(self): # Compute the images of the cusp classes (c)-(oo) in the # rational homology of the quotient modular abelian variety. - ims = [phi(M([c,infinity])) for c in P] + ims = [phi(M([c, infinity])) for c in P] # Take the span of the ims over ZZ A = phi.codomain().span(ims, ZZ) # The cuspidal subgroup is then the quotient of that module + # H_1(A) by H_1(A) - C = (A.ambient_module() + A)/A.ambient_module() + C = (A.ambient_module() + A) / A.ambient_module() self.__abvarquo_cuspidal_subgroup = C return C @@ -2226,12 +2219,14 @@ def abvarquo_rational_cuspidal_subgroup(self): Compute the rational subgroup of the cuspidal subgroup (as an abstract abelian group) of the abelian variety quotient A of the relevant modular Jacobian attached to this modular symbols - space. If C is the subgroup of A generated by differences of + space. + + If C is the subgroup of A generated by differences of cusps, then C is equipped with an action of Gal(Qbar/Q), and this function computes the fixed subgroup, i.e., C(Q). - We assume that self is defined over QQ and has weight 2. If - the sign of self is not 0, then the power of 2 may be wrong. + We assume that ``self`` is defined over QQ and has weight 2. If + the sign of ``self`` is not 0, then the power of 2 may be wrong. EXAMPLES: @@ -2282,8 +2277,10 @@ def abvarquo_rational_cuspidal_subgroup(self): sage: [A.abelian_variety().rational_torsion_subgroup().multiple_of_order() for A in D] [1, 5, 5] """ - try: return self.__abvarquo_rational_cuspidal_subgroup - except AttributeError: pass + try: + return self.__abvarquo_rational_cuspidal_subgroup + except AttributeError: + pass if self.base_ring() != QQ: raise ValueError("base ring must be QQ") if self.weight() != 2: @@ -2295,7 +2292,7 @@ def abvarquo_rational_cuspidal_subgroup(self): if N.is_squarefree(): return self.abvarquo_cuspidal_subgroup() - M = self.ambient_module() + M = self.ambient_module() phi = self.integral_period_mapping() # Make a list of all the finite cusps. @@ -2308,17 +2305,17 @@ def abvarquo_rational_cuspidal_subgroup(self): # Compute the images of the cusp classes (c)-(oo) in the # rational homology of the quotient modular abelian variety. - ims = [phi(M([c,infinity])) for c in P] + ims = [phi(M([c, infinity])) for c in P] # Take the span of the ims over ZZ A = phi.codomain().span(ims, ZZ) # The cuspidal subgroup is then the quotient of that module + # H_1(A) by H_1(A) - C = (A.ambient_module() + A)/A.ambient_module() + C = (A.ambient_module() + A) / A.ambient_module() # Make fgp module version of V. - D = V/V.zero_submodule() + D = V / V.zero_submodule() psi = D.hom([C(x) for x in ims]) # The rational cuspidal subgroup is got by intersecting kernels @@ -2327,7 +2324,8 @@ def abvarquo_rational_cuspidal_subgroup(self): CQ = C for t in G: T = self._matrix_of_galois_action(t, P) - 1 - if not T: continue + if not T: + continue im_gens = [psi(psi.lift(g).lift() * T) for g in CQ.gens()] h = CQ.hom(im_gens) CQ = h.kernel() @@ -2341,9 +2339,10 @@ def _matrix_of_galois_action(self, t, P): """ Compute the matrix of the action of the element of the cyclotomic Galois group defined by t on the set of cusps in P - (which is the set of finite cusps). This function is used - internally by the (rational) cuspidal subgroup and quotient - functions. + (which is the set of finite cusps). + + This function is used internally by the (rational) cuspidal + subgroup and quotient functions. INPUT: @@ -2386,15 +2385,18 @@ def _matrix_of_galois_action(self, t, P): d = c.galois_action(t, N) for j, e in enumerate(P): if d.is_gamma0_equiv(e, N, False): - A[i,j] = 1 + A[i, j] = 1 A.set_immutable() return A + class PeriodMapping(SageObject): r""" Base class for representing a period mapping attached to a space of modular - symbols. To be used via the derived classes RationalPeriodMapping and - IntegralPeriodMapping. + symbols. + + To be used via the derived classes :class:`RationalPeriodMapping` and + :class:`IntegralPeriodMapping`. """ def __init__(self, modsym, A): r""" @@ -2438,11 +2440,11 @@ def __call__(self, x): sage: M(vector([1,0,2])) (0, 9/4) """ - if is_FreeModuleElement(x): + if isinstance(x, FreeModuleElement): v = x else: v = self.__domain(x).element() - return v*self.__A + return v * self.__A def matrix(self): r""" @@ -2486,27 +2488,28 @@ def codomain(self): """ return self.__A.row_module() + class RationalPeriodMapping(PeriodMapping): def _repr_(self): """ - Return the string representation of self. + Return the string representation of ``self``. EXAMPLES:: sage: ModularSymbols(40,2).rational_period_mapping()._repr_() 'Rational period mapping associated to Modular Symbols space of dimension 13 for Gamma_0(40) of weight 2 with sign 0 over Rational Field' """ - return "Rational period mapping associated to %s"%self.modular_symbols_space() + return "Rational period mapping associated to %s" % self.modular_symbols_space() class IntegralPeriodMapping(PeriodMapping): def _repr_(self): """ - Return the string representation of self. + Return the string representation of ``self``. EXAMPLES:: sage: ModularSymbols(40,2).cuspidal_submodule().integral_period_mapping()._repr_() 'Integral period mapping associated to Modular Symbols subspace of dimension 6 of Modular Symbols space of dimension 13 for Gamma_0(40) of weight 2 with sign 0 over Rational Field' """ - return "Integral period mapping associated to %s"%self.modular_symbols_space() + return "Integral period mapping associated to %s" % self.modular_symbols_space() diff --git a/src/sage/modular/modsym/tests.py b/src/sage/modular/modsym/tests.py index 325b9ef632e..c4d0ff1ad7d 100644 --- a/src/sage/modular/modsym/tests.py +++ b/src/sage/modular/modsym/tests.py @@ -25,7 +25,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range import random diff --git a/src/sage/modular/multiple_zeta.py b/src/sage/modular/multiple_zeta.py new file mode 100644 index 00000000000..540e29d0e50 --- /dev/null +++ b/src/sage/modular/multiple_zeta.py @@ -0,0 +1,2560 @@ +# -*- coding: utf-8 -*- +r"""Algebra of motivic multiple zeta values + +This file contains an implementation of the algebra of motivic +multiple zeta values. + +The elements of this algebra are not the usual multiple zeta values as +real numbers defined by concrete iterated integrals, but abstract +symbols that satisfy all the linear relations between formal iterated +integrals that come from algebraic geometry (motivic +relations). Although this set of relations is not explicit, one can +test the equality as explained in the article [Brown2012]_. One can +map these motivic multiple zeta values to the associated real +numbers. Conjecturally, this period map should be injective. + +The implementation follows closely all the conventions from [Brown2012]_. + +As a convenient abbreviation, the elements will be called multizetas. + +EXAMPLES: + +One can input multizetas using compositions as arguments:: + + sage: Multizeta(3) + ζ(3) + sage: Multizeta(2,3,2) + ζ(2,3,2) + +as well as linear combinations of them:: + + sage: Multizeta(5)+6*Multizeta(2,3) + 6*ζ(2,3) + ζ(5) + +This creates elements of the class :class:`Multizetas`. + +One can multiply such elements:: + + sage: Multizeta(2)*Multizeta(3) + 6*ζ(1,4) + 3*ζ(2,3) + ζ(3,2) + +and their linear combinations:: + + sage: (Multizeta(2)+Multizeta(1,2))*Multizeta(3) + 9*ζ(1,1,4) + 5*ζ(1,2,3) + 2*ζ(1,3,2) + 6*ζ(1,4) + 2*ζ(2,1,3) + ζ(2,2,2) + + 3*ζ(2,3) + ζ(3,1,2) + ζ(3,2) + +The algebra is graded by the weight, which is the sum of the arguments. One +can extract homogeneous components:: + + sage: z = Multizeta(6)+6*Multizeta(2,3) + sage: z.homogeneous_component(5) + 6*ζ(2,3) + +One can also use the ring of multiple zeta values as a base ring for other +constructions:: + + sage: Z = Multizeta + sage: M = matrix(2,2,[Z(2),Z(3),Z(4),Z(5)]) + sage: M.det() + -10*ζ(1,6) - 5*ζ(2,5) - ζ(3,4) + ζ(4,3) + ζ(5,2) + +.. rubric:: Auxiliary class for alternative notation + +One can also use sequences of 0 and 1 as arguments:: + + sage: Multizeta(1,1,0)+3*Multizeta(1,0,0) + I(110) + 3*I(100) + +This creates an element of the auxiliary class :class:`Multizetas_iterated`. +This class is used to represent multiple zeta values as iterated integrals. + +One can also multiply such elements:: + + sage: Multizeta(1,0)*Multizeta(1,0) + 4*I(1100) + 2*I(1010) + +Back-and-forth conversion between the two classes can be done using +the methods "composition" and "iterated":: + + sage: (Multizeta(2)*Multizeta(3)).iterated() + 6*I(11000) + 3*I(10100) + I(10010) + + sage: (Multizeta(1,0)*Multizeta(1,0)).composition() + 4*ζ(1,3) + 2*ζ(2,2) + +Beware that the conversion between these two classes, besides +exchanging the indexing by words in 0 and 1 and the indexing by +compositions, also involves the sign `(-1)^w` where `w` is the length +of the composition and the number of `1` in the associated word in 0 +and 1. For example, one has the equality + +.. MATH:: \zeta(2,3,4) = (-1)^3 I(1,0,1,0,0,1,0,0,0). + +.. rubric:: Approximate period map + +The period map, or rather an approximation, is also available under +the generic numerical approximation method:: + + sage: z = Multizeta(5)+6*Multizeta(2,3) + sage: z.n() + 2.40979014076349 + sage: z.n(prec=100) + 2.4097901407634924849438423801 + +Behind the scene, all the numerical work is done by the PARI implementation +of numerical multiple zeta values. + +.. rubric:: Searching for linear relations + +All this can be used to find linear dependencies between any set of +multiple zeta values. Let us illustrate this by an example. + +Let us first build our sample set:: + + sage: Z = Multizeta + sage: L = [Z(*c) for c in [(1, 1, 4), (1, 2, 3), (1, 5), (6,)]] + +Then one can compute the space of relations:: + + sage: M = matrix([Zc.phi_as_vector() for Zc in L]) + sage: K = M.kernel(); K + Vector space of degree 4 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -2 1/16] + [ 0 1 6 -13/48] + +and check that the first relation holds:: + + sage: relation = L[0]-2*L[2]+1/16*L[3]; relation + ζ(1,1,4) - 2*ζ(1,5) + 1/16*ζ(6) + sage: relation.phi() + 0 + sage: relation.is_zero() + True + +.. WARNING:: + + Because this code uses an hardcoded multiplicative basis that is + available up to weight 17 included, some parts will not work + in larger weights, in particular the test of equality. + +REFERENCES: + +.. [Brown2012] Francis C. S. Brown, *On the decomposition of motivic + multiple zeta values*, Advanced Studies in Pure Mathematics 63, + 2012. Galois-Teichmuller Theory and Arithmetic Geometry. + +.. [Brown2019] Francis C. S. Brown, *From the Deligne-Ihara conjecture to + multiple modular values*, :arxiv:`1904.00179` + +.. [Deli2012] Pierre Deligne, *Multizêtas, d’après Francis Brown*, + Séminaire Bourbaki, janvier 2012. http://www.bourbaki.ens.fr/TEXTES/1048.pdf + +.. [Stie2020] \S. Stieberger, *Periods and Superstring Amplitudes*, + Periods in Quantum Field Theory and Arithmetic, Springer Proceedings + in Mathematics and Statistics 314, 2020 +""" +# **************************************************************************** +# Copyright (C) 2020 Frédéric Chapoton +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# https://www.gnu.org/licenses/ +# **************************************************************************** +from sage.structure.unique_representation import UniqueRepresentation +from sage.algebras.free_zinbiel_algebra import FreeZinbielAlgebra +from sage.arith.misc import bernoulli +from sage.categories.cartesian_product import cartesian_product +from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis +from sage.categories.rings import Rings +from sage.categories.domains import Domains +from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.integer_vector import IntegerVectors +from sage.combinat.partition import Partitions +from sage.combinat.words.finite_word import FiniteWord_class +from sage.combinat.words.word import Word +from sage.combinat.words.words import Words +from sage.libs.pari.all import pari +from sage.matrix.constructor import matrix +from sage.misc.cachefunc import cached_function, cached_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.misc_c import prod +from sage.modules.free_module_element import vector +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.rational_field import QQ +from sage.rings.semirings.non_negative_integer_semiring import NN + + +# multiplicative generators for weight <= 17 +# using the following convention +# (3, 5) <---> (sign) * [1,0,0,1,0,0,0,0] +# taken from the Maple implementation by F. Brown +B_data = [[], [], [(2,)], [(3,)], [], [(5,)], [], [(7,)], [(3, 5)], [(9,)], + [(3, 7)], [(11,), (3, 3, 5)], [(5, 7), (5, 3, 2, 2)], + [(13,), (3, 5, 5), (3, 3, 7)], [(5, 9), (3, 11), (3, 3, 3, 5)], + [(15,), (3, 5, 7), (3, 3, 9), (5, 3, 3, 2, 2)], + [(11, 5), (13, 3), (5, 5, 3, 3), (7, 3, 3, 3), (7, 5, 2, 2)], + [(17,), (7, 5, 5), (9, 3, 5), (9, 5, 3), (11, 3, 3), + (5, 3, 3, 3, 3), (5, 5, 3, 2, 2)]] + +Words10 = Words((1, 0), infinite=False) + + +def coproduct_iterator(paire): + """ + Return an iterator for terms in the coproduct. + + This is an auxiliary function. + + INPUT: + + - ``paire`` -- a pair (list of indices, end of word) + + OUTPUT: + + iterator for terms in the motivic coproduct + + Each term is seen as a list of positions. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import coproduct_iterator + sage: list(coproduct_iterator(([0],[0,1,0,1]))) + [[0, 1, 2, 3], [0, 3]] + sage: list(coproduct_iterator(([0],[0,1,0,1,1,0,1]))) + [[0, 1, 2, 3, 4, 5, 6], + [0, 1, 2, 6], + [0, 1, 5, 6], + [0, 3, 4, 5, 6], + [0, 4, 5, 6], + [0, 6]] + """ + head, tail = paire + n = len(tail) + if n == 1: + yield head + return + start_value = tail[0] + last_index = head[-1] + yield from coproduct_iterator((head + [last_index + 1], tail[1:])) + for step in range(3, n): + if tail[step] != start_value: + yield from coproduct_iterator((head + [last_index + step], + tail[step:])) + + +def composition_to_iterated(w, reverse=False): + """ + Convert a composition to a word in 0 and 1. + + By default, the chosen convention maps (2,3) to (1,0,1,0,0), + respecting the reading order from left to right. + + The inverse map is given by :func:`iterated_to_composition`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import composition_to_iterated + sage: composition_to_iterated((1,2)) + (1, 1, 0) + sage: composition_to_iterated((3,1,2)) + (1, 0, 0, 1, 1, 0) + sage: composition_to_iterated((3,1,2,4)) + (1, 0, 0, 1, 1, 0, 1, 0, 0, 0) + + TESTS:: + + sage: composition_to_iterated((1,2), True) + (1, 0, 1) + """ + word = tuple([]) + loop_over = reversed(w) if reverse else w + for letter in loop_over: + word += (1,) + (0,) * (letter - 1) + return word + + +def iterated_to_composition(w, reverse=False): + """ + Convert a word in 0 and 1 to a composition. + + By default, the chosen convention maps (1,0,1,0,0) to (2,3). + + The inverse map is given by :func:`composition_to_iterated`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import iterated_to_composition + sage: iterated_to_composition([1,0,1,0,0]) + (2, 3) + sage: iterated_to_composition(Word([1,1,0])) + (1, 2) + sage: iterated_to_composition(Word([1,1,0,1,1,0,0])) + (1, 2, 1, 3) + + TESTS:: + + sage: iterated_to_composition([1,0,1,0,0], True) + (3, 2) + """ + b = [] + count = 1 + for letter in reversed(w): + if letter == 0: + count += 1 + else: + b.append(count) + count = 1 + return tuple(b) if reverse else tuple(reversed(b)) + + +def dual_composition(c): + """ + Return the dual composition of ``c``. + + This is an involution on compositions such that associated + multizetas are equal. + + INPUT: + + - ``c`` -- a composition + + OUTPUT: + + a composition + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import dual_composition + sage: dual_composition([3]) + (1, 2) + sage: dual_composition(dual_composition([3,4,5])) == (3,4,5) + True + """ + i = composition_to_iterated(c) + ri = [1 - x for x in reversed(i)] + return iterated_to_composition(ri) + + +def minimize_term(w, cf): + """ + Return the largest among ``w`` and the dual word of ``w``. + + INPUT: + + - ``w`` -- a word in the letters 0 and 1 + + - ``cf`` -- a coefficient + + OUTPUT: + + (word, coefficient) + + The chosen order is lexicographic with 1 < 0. + + If the dual word is chosen, the sign of the coefficient is changed, + otherwise the coefficient is returned unchanged. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import minimize_term, Words10 + sage: minimize_term(Words10((1,1,0)), 1) + (word: 100, -1) + sage: minimize_term(Words10((1,0,0)), 1) + (word: 100, 1) + """ + reverse_w = tuple(1 - t for t in reversed(w)) + for x, y in zip(w, reverse_w): + if x < y: + return (w, cf) + if x > y: + return (Words10(reverse_w), (-1)**len(w) * cf) + return (w, cf) + + +# numerical values + +class MultizetaValues(UniqueRepresentation): + """ + Custom cache for numerical values of multiple zetas. + + Computations are performed using the PARI/GP :pari:`zetamultall` (for the + cache) and :pari:`zetamult` (for indices/precision outside of the cache). + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import MultizetaValues + sage: M = MultizetaValues() + + sage: M((1,2)) + 1.202056903159594285399738161511449990764986292340... + sage: parent(M((2,3))) + Real Field with 1024 bits of precision + + sage: M((2,3), prec=53) + 0.228810397603354 + sage: parent(M((2,3), prec=53)) + Real Field with 53 bits of precision + + sage: M((2,3), reverse=False) == M((3,2)) + True + + sage: M((2,3,4,5)) + 2.9182061974731261426525583710934944310404272413...e-6 + sage: M((2,3,4,5), reverse=False) + 0.0011829360522243605614404196778185433287651... + + sage: parent(M((2,3,4,5))) + Real Field with 1024 bits of precision + sage: parent(M((2,3,4,5), prec=128)) + Real Field with 128 bits of precision + """ + def __init__(self): + """ + When first called, pre-compute up to weight 8 at precision 1024. + + TESTS:: + + sage: from sage.modular.multiple_zeta import MultizetaValues + sage: M = MultizetaValues() + """ + self.max_weight = 0 + self.prec = 0 + self.reset() + + def __repr__(self): + r""" + TESTS:: + + sage: from sage.modular.multiple_zeta import MultizetaValues + sage: MultizetaValues() + Cached multiple zeta values at precision 1024 up to weight 8 + """ + return "Cached multiple zeta values at precision %d up to weight %d" % (self.prec, self.max_weight) + + def reset(self, max_weight=8, prec=1024): + r""" + Reset the cache to its default values or to given arguments. + + TESTS:: + + sage: from sage.modular.multiple_zeta import MultizetaValues + sage: M = MultizetaValues() + sage: M + Cached multiple zeta values at precision 1024 up to weight 8 + sage: M.reset(5, 64) + sage: M + Cached multiple zeta values at precision 64 up to weight 5 + sage: M.reset() + sage: M + Cached multiple zeta values at precision 1024 up to weight 8 + """ + self.prec = int(prec) + self.max_weight = int(max_weight) + self._data = pari.zetamultall(self.max_weight, self.prec) + + def update(self, max_weight, prec): + """ + Compute and store more values if needed. + + TESTS:: + + sage: from sage.modular.multiple_zeta import MultizetaValues + sage: M = MultizetaValues() + sage: M + Cached multiple zeta values at precision 1024 up to weight 8 + sage: M.update(5, 64) + sage: M + Cached multiple zeta values at precision 1024 up to weight 8 + sage: M.update(5, 2048) + sage: M + Cached multiple zeta values at precision 2048 up to weight 8 + sage: M.reset() + """ + if self.prec < prec or self.max_weight < max_weight: + self.reset(max(self.max_weight, max_weight), max(self.prec, prec)) + + def pari_eval(self, index): + r""" + TESTS:: + + sage: from sage.modular.multiple_zeta import MultizetaValues + sage: M = MultizetaValues() + sage: [M.pari_eval((n,)) for n in range(2,20)] + [1.64493406684823, 1.20205690315959, 1.08232323371114, 1.03692775514337, ... 1.00000381729326, 1.00000190821272] + """ + weight = sum(index) + index = list(reversed(index)) + if weight <= self.max_weight: + index = pari.zetamultconvert(index, 2) + return self._data[index - 1] + else: + return pari.zetamult(index, precision=self.prec) + + def __call__(self, index, prec=None, reverse=True): + r""" + Numerical multiple zeta value as a Sage real floating point number. + + TESTS:: + + sage: from sage.modular.multiple_zeta import MultizetaValues + sage: M = MultizetaValues() + sage: M((3,2)) + 0.7115661975505724320969738060864026120925612044383392364... + sage: M((3,2), reverse=False) + 0.2288103976033537597687461489416887919325093427198821602... + sage: M((3,2), prec=128) + 0.71156619755057243209697380608640261209 + sage: M((3,2), prec=128, reverse=False) + 0.22881039760335375976874614894168879193 + """ + if reverse: + index = list(reversed(index)) + if prec is None: + prec = self.prec + weight = sum(index) + if weight <= self.max_weight and prec <= self.prec: + index = pari.zetamultconvert(index, 2) + value = self._data[index - 1] + return value.sage().n(prec=prec) + else: + return pari.zetamult(index, precision=prec).sage().n(prec=prec) + + +Values = MultizetaValues() + + +def basis_f_odd_iterator(n): + """ + Return an iterator over compositions of ``n`` with parts in ``(3,5,7,...)`` + + INPUT: + + - ``n`` -- an integer + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import basis_f_odd_iterator + sage: [list(basis_f_odd_iterator(i)) for i in range(2,9)] + [[], [(3,)], [], [(5,)], [(3, 3)], [(7,)], [(5, 3), (3, 5)]] + sage: list(basis_f_odd_iterator(14)) + [(11, 3), + (5, 3, 3, 3), + (3, 5, 3, 3), + (3, 3, 5, 3), + (9, 5), + (3, 3, 3, 5), + (7, 7), + (5, 9), + (3, 11)] + """ + if n == 0: + yield tuple([]) + return + if n == 1: + return + if n % 2: + yield (n,) + for k in range(3, n, 2): + for start in basis_f_odd_iterator(n - k): + yield start + (k, ) + + +def basis_f_iterator(n): + """ + Return an iterator over decompositions of ``n`` using ``2,3,5,7,9,...``. + + The means that each term is made of a power of 2 and a composition + of the remaining integer with parts in ``(3,5,7,...)`` + + INPUT: + + - ``n`` -- an integer + + Each term is returned as a pair (integer, word) where + the integer is the exponent of 2. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import basis_f_iterator + sage: [list(basis_f_iterator(i)) for i in range(2,9)] + [[(1, word: )], + [(0, word: f3)], + [(2, word: )], + [(0, word: f5), (1, word: f3)], + [(0, word: f3,f3), (3, word: )], + [(0, word: f7), (1, word: f5), (2, word: f3)], + [(0, word: f5,f3), (0, word: f3,f5), (1, word: f3,f3), (4, word: )]] + sage: list(basis_f_iterator(11)) + [(0, word: f11), + (0, word: f5,f3,f3), + (0, word: f3,f5,f3), + (0, word: f3,f3,f5), + (1, word: f9), + (1, word: f3,f3,f3), + (2, word: f7), + (3, word: f5), + (4, word: f3)] + """ + if n < 2: + return + for k in range(n // 2 + 1): + for start in basis_f_odd_iterator(n - 2 * k): + yield (k, Word(['f{}'.format(d) for d in start])) + + +def extend_multiplicative_basis(B, n): + """ + Extend a multiplicative basis into a basis. + + This is an iterator. + + INPUT: + + - ``B`` -- function mapping integer to list of tuples of compositions + + - ``n`` -- an integer + + OUTPUT: + + Each term is a tuple of tuples of compositions. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import extend_multiplicative_basis + sage: from sage.modular.multiple_zeta import B_data + sage: list(extend_multiplicative_basis(B_data,5)) + [((5,),), ((3,), (2,))] + sage: list(extend_multiplicative_basis(B_data,6)) + [((3,), (3,)), ((2,), (2,), (2,))] + sage: list(extend_multiplicative_basis(B_data,7)) + [((7,),), ((5,), (2,)), ((3,), (2,), (2,))] + """ + for pi in Partitions(n, min_part=2): + for liste in cartesian_product([B[i] for i in pi]): + yield liste + + +# several classes for the algebra of MZV + + +def Multizeta(*args): + r""" + Common entry point for multiple zeta values. + + If the argument is a sequence of 0 and 1, an element of + :class:`Multizetas_iterated` will be returned. + + Otherwise, an element of :class:`Multizetas` will be returned. + + The base ring is `\QQ`. + + EXAMPLES:: + + sage: Z = Multizeta + sage: Z(1,0,1,0) + I(1010) + sage: Z(3,2,2) + ζ(3,2,2) + + TESTS:: + + sage: Z(3,2,2).iterated().composition() + ζ(3,2,2) + sage: Z(1,0,1,0).composition().iterated() + I(1010) + """ + if 0 in args: + return Multizetas_iterated(QQ)(tuple(args)) + return Multizetas(QQ)(tuple(args)) + + +class Multizetas(CombinatorialFreeModule): + r""" + Main class for the algebra of multiple zeta values. + + The convention is chosen so that `\zeta(1,2)` is convergent. + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: x = M((2,)) + sage: y = M((4,3)) + sage: x+5*y + ζ(2) + 5*ζ(4,3) + sage: x*y + 6*ζ(1,4,4) + 8*ζ(1,5,3) + 3*ζ(2,3,4) + 4*ζ(2,4,3) + 3*ζ(3,2,4) + + 2*ζ(3,3,3) + 6*ζ(4,1,4) + 3*ζ(4,2,3) + ζ(4,3,2) + + TESTS:: + + sage: A = QQ['u'] + sage: u = A.gen() + sage: M = Multizetas(A) + sage: (u*M((2,))+M((3,)))*M((2,)) + 4*u*ζ(1,3) + 6*ζ(1,4) + 2*u*ζ(2,2) + 3*ζ(2,3) + ζ(3,2) + """ + def __init__(self, R): + """ + TESTS:: + + sage: M = Multizetas(QQ) + sage: TestSuite(M).run() # not tested + sage: M.category() + Category of commutative no zero divisors graded algebras + with basis over Rational Field + """ + if R not in Rings(): + raise TypeError("argument R must be a ring") + cat = GradedAlgebrasWithBasis(R).Commutative() + if R in Domains(): + cat = cat & Domains() + CombinatorialFreeModule.__init__(self, R, Words(NN, infinite=False), + prefix="Z", + category=cat) + + def _repr_(self): + r""" + Return a string representation of the algebra. + + EXAMPLES:: + + sage: M = Multizetas(QQ); M + Algebra of motivic multiple zeta values indexed by compositions over Rational Field + """ + txt = "Algebra of motivic multiple zeta values indexed by compositions over {}" + return txt.format(self.base_ring()) + + def _repr_term(self, m): + """ + Return a custom string representation for the monomials. + + EXAMPLES:: + + sage: Multizeta(2,3) # indirect doctest + ζ(2,3) + """ + return "ζ(" + ','.join(str(letter) for letter in m) + ")" + + @cached_method + def one_basis(self): + r""" + Return the index of the unit for the algebra. + + This is the empty word. + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: M.one_basis() + word: + """ + return self.basis().keys()([]) + + def some_elements(self): + r""" + Return some elements of the algebra. + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: M.some_elements() + (ζ(), ζ(2), ζ(3), ζ(4), ζ(1,2)) + """ + return self([]), self([2]), self([3]), self([4]), self((1, 2)) + + def product_on_basis(self, w1, w2): + r""" + Compute the product of two monomials. + + This is done by converting to iterated integrals and + using the shuffle product. + + INPUT: + + - ``w1``, ``w2`` -- compositions + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: M.product_on_basis([2],[2]) + 4*ζ(1,3) + 2*ζ(2,2) + sage: x = M((2,)) + sage: x*x + 4*ζ(1,3) + 2*ζ(2,2) + """ + if not w1: + return self(w2) + if not w2: + return self(w1) + p1 = self.iterated_on_basis(w1) + p2 = self.iterated_on_basis(w2) + p1p2 = p1 * p2 + MZV_it = p1p2.parent() + return MZV_it.composition(p1p2) + + def half_product(self, w1, w2): + r""" + Compute half of the product of two elements. + + This comes from half of the shuffle product. + + .. WARNING:: This is not a motivic operation. + + INPUT: + + - ``w1``, ``w2`` -- elements + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: M.half_product(M([2]),M([2])) + 2*ζ(1,3) + ζ(2,2) + + TESTS: + + sage: M.half_product(M.one(), M([2])) + Traceback (most recent call last): + ... + ValueError: not defined on the unit + """ + empty = self.one_basis() + if w1.coefficient(empty) or w2.coefficient(empty): + raise ValueError('not defined on the unit') + p1 = self.iterated(w1) + p2 = self.iterated(w2) + MZV_it = p1.parent() + p1p2 = MZV_it.half_product(p1, p2) + return MZV_it.composition(p1p2) + + @lazy_attribute + def iterated(self): + """ + Convert to the algebra of iterated integrals. + + This is also available as a method of elements. + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: x = M((3,2)) + sage: M.iterated(3*x) + 3*I(10010) + sage: x = M((2,3,2)) + sage: M.iterated(4*x) + -4*I(1010010) + """ + cod = Multizetas_iterated(self.base_ring()) + return self.module_morphism(self.iterated_on_basis, codomain=cod) + + def iterated_on_basis(self, w): + """ + Convert to the algebra of iterated integrals. + + Beware that this conversion involves signs in our chosen convention. + + INPUT: + + - ``w`` -- a word + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: x = M.basis().keys()((3,2)) + sage: M.iterated_on_basis(x) + I(10010) + sage: x = M.basis().keys()((2,3,2)) + sage: M.iterated_on_basis(x) + -I(1010010) + """ + codomain = Multizetas_iterated(self.base_ring()) + return (-1)**len(w) * codomain(composition_to_iterated(w)) + + def degree_on_basis(self, w): + """ + Return the degree of the monomial ``w``. + + This is the sum of terms in ``w``. + + INPUT: + + - ``w`` -- a composition + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: x = (2,3) + sage: M.degree_on_basis(x) # indirect doctest + 5 + """ + return ZZ(sum(w)) + + @lazy_attribute + def phi(self): + r""" + Return the morphism ``phi``. + + This sends multiple zeta values to the algebra :func:`F_ring`, + which is a shuffle algebra in odd generators `f_3,f_5,f_7,\dots` + over the polynomial ring in one variable `f_2`. + + This is a ring isomorphism, that depends on the choice of a + multiplicative basis for the ring of motivic multiple zeta + values. Here we use one specific hardcoded basis. + + For the precise definition of ``phi`` by induction, see [Brown2012]_. + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: m = Multizeta(2,2) + 2*Multizeta(1,3); m + 2*ζ(1,3) + ζ(2,2) + sage: M.phi(m) + 1/2*f2^2*Z[] + + sage: Z = Multizeta + sage: B5 = [3*Z(1,4) + 2*Z(2,3) + Z(3,2), 3*Z(1,4) + Z(2,3)] + sage: [M.phi(b) for b in B5] + [f2*Z[f3] - 1/2*Z[f5], 1/2*Z[f5]] + """ + M_it = Multizetas_iterated(self.base_ring()) + return M_it.phi * self.iterated + + def _element_constructor_(self, x): + r""" + Convert ``x`` into ``self``. + + INPUT + + - ``x`` -- either a list, tuple, word or a multiple zeta value + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: M(Word((2,3))) + ζ(2,3) + sage: M(Word([2,3])) + ζ(2,3) + sage: x = M((2,3)); x + ζ(2,3) + sage: M(x) == x + True + """ + if isinstance(x, (FiniteWord_class, tuple, list)): + if x: + assert all(letter >= 1 for letter in x), 'bad letter' + assert x[-1] >= 2, 'bad last letter' + W = self.basis().keys() + return self.monomial(W(x)) + + P = x.parent() + if isinstance(P, Multizetas): + if P is self: + return x + if P is not self.base_ring(): + return self.element_class(self, x.monomial_coefficients()) + elif isinstance(P, Multizetas_iterated): + return x.composition() + + R = self.base_ring() + # coercion via base ring + x = R(x) + if x == 0: + return self.element_class(self, {}) + return self.from_base_ring_from_one_basis(x) + + def algebra_generators(self, n): + """ + Return a set of multiplicative generators in weight ``n``. + + This is obtained from hardcoded data, available only up to weight 17. + + INPUT: + + - ``n`` -- an integer + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: M.algebra_generators(5) + [ζ(5)] + sage: M.algebra_generators(8) + [ζ(3,5)] + """ + return [self(b) for b in B_data[n]] + + def basis_data(self, basering, n): + """ + Return an iterator for a basis in weight ``n``. + + This is obtained from hardcoded data, available only up to weight 17. + + INPUT: + + - ``n`` -- an integer + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: list(M.basis_data(QQ, 4)) + [4*ζ(1,3) + 2*ζ(2,2)] + """ + basis_MZV = extend_multiplicative_basis(B_data, n) + return (prod(self(compo) for compo in term) for term in basis_MZV) + + def basis_brown(self, n): + r""" + Return a basis of the algebra of multiple zeta values in weight ``n``. + + It was proved by Francis Brown that this is a basis of motivic + multiple zeta values. + + This is made of all `\zeta(n_1, ..., n_r)` with parts in {2,3}. + + INPUT: + + - ``n`` -- an integer + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: M.basis_brown(3) + [ζ(3)] + sage: M.basis_brown(4) + [ζ(2,2)] + sage: M.basis_brown(5) + [ζ(3,2), ζ(2,3)] + sage: M.basis_brown(6) + [ζ(3,3), ζ(2,2,2)] + """ + return [self(tuple(c)) + for c in IntegerVectors(n, min_part=2, max_part=3)] + + class Element(CombinatorialFreeModule.Element): + def iterated(self): + """ + Convert to the algebra of iterated integrals. + + Beware that this conversion involves signs. + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: x = M((2,3,4)) + sage: x.iterated() + -I(101001000) + """ + return self.parent().iterated(self) + + def simplify(self): + """ + Gather terms using the duality relations. + + This can help to lower the number of monomials. + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: z = 3*M((3,)) + 5*M((1,2)) + sage: z.simplify() + 8*ζ(3) + """ + return self.iterated().simplify().composition() + + def __eq__(self, other): + """ + Test for equality. + + This means equality as motivic multiple zeta value, computed + using the morphism ``phi``. + + EXAMPLES:: + + sage: M = Multizeta + sage: 4*M(1,3) == M(4) + True + sage: our_pi2 = 6*M(2) + sage: Multizeta(2,2,2) == our_pi2**3 / 7.factorial() + True + """ + return self.iterated().phi() == other.iterated().phi() + + def __ne__(self, other): + """ + Test for non-equality. + + This means non-equality as motivic multiple zeta value, computed + using the morphism ``phi``. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizeta + sage: M(2,2,2) != M(6) + True + """ + return not (self == other) + + def phi(self): + """ + Return the image of ``self`` by the morphism ``phi``. + + This sends multiple zeta values to the algebra :func:`F_ring`. + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: M((1,2)).phi() + Z[f3] + + TESTS:: + + sage: A = QQ['u'] + sage: u = A.gen() + sage: M = Multizetas(A) + sage: tst = u*M((1,2))+M((3,)) + sage: tst.phi() + (u+1)*Z[f3] + """ + return self.parent().phi(self) + + def phi_as_vector(self): + """ + Return the image of ``self`` by the morphism ``phi`` as a vector. + + The morphism ``phi`` sends multiple zeta values to the algebra + :func:`F_ring`. Then the image is expressed as a vector in + a fixed basis of one graded component of this algebra. + + This is only defined for homogeneous elements. + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: M((3,2)).phi_as_vector() + (9/2, -2) + + TESTS:: + + sage: (M((4,))+M((1,2))).phi_as_vector() + Traceback (most recent call last): + ... + ValueError: only defined for homogeneous elements + """ + if not self.is_homogeneous(): + raise ValueError('only defined for homogeneous elements') + return f_to_vector(self.parent().phi(self)) + + def _numerical_approx_pari(self): + r""" + The numerical values of individual multiple zeta are obtained via + the class :class:`MultizetaValues` that performs some caching. + + TESTS:: + + sage: M = Multizetas(QQ) + sage: a = M((3,2)) - 2*M((7,)) + sage: a._numerical_approx_pari() + -1.30513235721327 + sage: type(a._numerical_approx_pari()) + + """ + return sum(cf * Values.pari_eval(tuple(w)) for w, cf in self.monomial_coefficients().items()) + + def numerical_approx(self, prec=None, digits=None, algorithm=None): + """ + Return a numerical value for this element. + + EXAMPLES:: + + sage: M = Multizetas(QQ) + sage: M(Word((3,2))).n() # indirect doctest + 0.711566197550572 + sage: parent(M(Word((3,2))).n()) + Real Field with 53 bits of precision + + sage: (M((3,)) * M((2,))).n(prec=80) + 1.9773043502972961181971 + sage: M((1,2)).n(70) + 1.2020569031595942854 + + sage: M((3,)).n(digits=10) + 1.202056903 + + If you need plan to use intensively numerical approximation at high precision, + you might want to add more values and/or accuracy to the cache:: + + sage: from sage.modular.multiple_zeta import MultizetaValues + sage: M = MultizetaValues() + sage: M.update(max_weight=9, prec=2048) + sage: M + Cached multiple zeta values at precision 2048 up to weight 9 + sage: M.reset() # restore precision for the other doctests + """ + if prec is None: + if digits: + from sage.arith.numerical_approx import digits_to_bits + prec = digits_to_bits(digits) + else: + prec = 53 + if algorithm is not None: + raise ValueError("unknown algorithm") + if prec < Values.prec: + s = sum(cf * Values(tuple(w)) for w, cf in self.monomial_coefficients().items()) + return s.n(prec=prec) + else: + return sum(cf * Values(tuple(w), prec=prec) for w, cf in self.monomial_coefficients().items()) + + +class Multizetas_iterated(CombinatorialFreeModule): + r""" + Secondary class for the algebra of multiple zeta values. + + This is used to represent multiple zeta values as iterated integrals + of the differential forms `\omega_0 = \dt/t`and `\omega_1 = \dt/(t-1)`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ); M + Algebra of motivic multiple zeta values as convergent iterated + integrals over Rational Field + sage: M((1,0)) + I(10) + sage: M((1,0))**2 + 4*I(1100) + 2*I(1010) + sage: M((1,0))*M((1,0,0)) + 6*I(11000) + 3*I(10100) + I(10010) + """ + def __init__(self, R): + """ + TESTS:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: TestSuite(M).run() # not tested + sage: M.category() + Category of commutative no zero divisors graded algebras + with basis over Rational Field + """ + if R not in Rings(): + raise TypeError("argument R must be a ring") + cat = GradedAlgebrasWithBasis(R).Commutative() + if R in Domains(): + cat = cat & Domains() + CombinatorialFreeModule.__init__(self, R, Words10, prefix="I", + category=cat) + + def _repr_(self): + """ + Return a string representation for the ring. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ); M + Algebra of motivic multiple zeta values as convergent iterated integrals over Rational Field + """ + return "Algebra of motivic multiple zeta values as convergent iterated integrals over {}".format(self.base_ring()) + + def _repr_term(self, m): + """ + Return a custom string representation for the monomials. + + EXAMPLES:: + + sage: Multizeta(1,0,1,0) # indirect doctest + I(1010) + """ + return "I(" + ''.join(str(letter) for letter in m) + ")" + + @cached_method + def one_basis(self): + r""" + Return the index of the unit for the algebra. + + This is the empty word. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: M.one_basis() + word: + """ + return self.basis().keys()([]) + + def product_on_basis(self, w1, w2): + r""" + Compute the product of two monomials. + + This is the shuffle product. + + INPUT: + + - ``w1``, ``w2`` -- words in 0 and 1 + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: x = Word([1,0]) + sage: M.product_on_basis(x,x) + 2*I(1010) + 4*I(1100) + sage: y = Word([1,1,0]) + sage: M.product_on_basis(y,x) + I(10110) + 3*I(11010) + 6*I(11100) + """ + return sum(self.basis()[u] for u in w1.shuffle(w2)) + + def half_product_on_basis(self, w1, w2): + r""" + Compute half of the product of two monomials. + + This is half of the shuffle product. + + .. WARNING:: This is not a motivic operation. + + INPUT: + + - ``w1``, ``w2`` -- monomials + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: x = Word([1,0]) + sage: M.half_product_on_basis(x,x) + 2*I(1100) + I(1010) + """ + assert w1 + W = self.basis().keys() + u1 = W([w1[0]]) + r1 = w1[1:] + return sum(self.basis()[u1 + u] for u in r1.shuffle(w2)) + + @lazy_attribute + def half_product(self): + r""" + Compute half of the product of two elements. + + This is half of the shuffle product. + + .. WARNING:: This is not a motivic operation. + + INPUT: + + - ``w1``, ``w2`` -- elements + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: x = M(Word([1,0])) + sage: M.half_product(x,x) + 2*I(1100) + I(1010) + """ + half = self.half_product_on_basis + return self._module_morphism(self._module_morphism(half, position=0, + codomain=self), + position=1) + + def coproduct_on_basis(self, w): + """ + Return the motivic coproduct of a monomial. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: M.coproduct_on_basis([1,0]) + I() # I(10) + I(10) # I() + + sage: M.coproduct_on_basis((1,0,1,0)) + I() # I(1010) + 3*I(10) # I(10) + I(1010) # I() + """ + seq = [0] + list(w) + [1] + terms = coproduct_iterator(([0], seq)) + M_all = All_iterated(self.base_ring()) + + def split_word(indices): + L = self.one() + for i in range(len(indices) - 1): + w = Word(seq[indices[i]:indices[i + 1] + 1]) + if len(w) >= 4: + value = M_all(w) + L *= value.regularise() + return L + + resu = self.tensor_square().zero() + for indices in terms: + resu += split_word(indices).tensor( + M_all(Word(seq[i] for i in indices)).regularise()) + return resu + + @lazy_attribute + def coproduct(self): + """ + Return the motivic coproduct of an element. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: a = 3*Multizeta(1,4) + Multizeta(2,3) + sage: M.coproduct(a.iterated()) + 3*I() # I(11000) + I() # I(10100) + 3*I(11000) # I() + - I(10) # I(100) + I(10100) # I() + """ + cop = self.coproduct_on_basis + return self._module_morphism(cop, codomain=self.tensor_square()) + + @lazy_attribute + def composition(self): + """ + Convert to the algebra of multiple zeta values of composition style. + + This means the algebra :class:`Multizetas`. + + This is also available as a method of elements. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: x = M((1,0)) + sage: M.composition(2*x) + -2*ζ(2) + sage: x = M((1,0,1,0,0)) + sage: M.composition(x) + ζ(2,3) + """ + cod = Multizetas(self.base_ring()) + return self.module_morphism(self.composition_on_basis, codomain=cod) + + def composition_on_basis(self, w, basering=None): + """ + Convert to the algebra of multiple zeta values of composition style. + + INPUT: + + - ``basering`` -- optional choice of the coefficient ring + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: x = Word((1,0,1,0,0)) + sage: M.composition_on_basis(x) + ζ(2,3) + sage: x = Word((1,0,1,0,0,1,0)) + sage: M.composition_on_basis(x) + -ζ(2,3,2) + """ + if basering is None: + basering = self.base_ring() + codomain = Multizetas(basering) + return (-1)**w.count(1) * codomain(iterated_to_composition(w)) + + def dual_on_basis(self, w): + """ + Return the order of the word and exchange letters 0 and 1. + + This is an involution. + + INPUT: + + - ``w`` -- a word in 0 and 1 + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: x = Word((1,0,1,0,0)) + sage: M.dual_on_basis(x) + -I(11010) + """ + rev = [1 - x for x in reversed(w)] + return (-1)**len(w) * self(self.basis().keys()(rev)) + + def degree_on_basis(self, w): + """ + Return the degree of the monomial ``w``. + + This is the length of the word. + + INPUT: + + - ``w`` -- a word in 0 and 1 + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: x = Word((1,0,1,0,0)) + sage: M.degree_on_basis(x) + 5 + """ + return ZZ(len(w)) + + def D_on_basis(self, k, w): + """ + Return the action of the operator `D_k` on the monomial ``w``. + + This is one main tool in the procedure that allows + to map the algebra of multiple zeta values to + the F Ring. + + INPUT: + + - ``k`` -- an odd integer, at least 3 + + - ``w`` -- a word in 0 and 1 + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: M.D_on_basis(3,(1,1,1,0,0)) + I(110) # I(10) + 2*I(100) # I(10) + + sage: M.D_on_basis(3,(1,0,1,0,0)) + 3*I(100) # I(10) + sage: M.D_on_basis(5,(1,0,0,0,1,0,0,1,0,0)) + 10*I(10000) # I(10100) + """ + Im = All_iterated(self.base_ring()) + MZV_MZV = self.tensor_square() + N = len(w) + it = [0] + list(w) + [1] + coprod = MZV_MZV.zero() + for p in range(N + 1 - k): + left = Im(it[p: p + k + 2]) + right = Im(it[:p + 1] + it[p + k + 1:]) + if left and right: + coprod += left.regularise().tensor(right.regularise()) + return coprod + + @cached_method + def phi_extended(self, w): + r""" + Return the image of the monomial ``w`` by the morphism ``phi``. + + INPUT: + + - ``w`` -- a word in 0 and 1 + + OUTPUT: + + an element in the algebra :func:`F_ring` + + The coefficients are in the base ring. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: M.phi_extended((1,0)) + -f2*Z[] + sage: M.phi_extended((1,0,0)) + -Z[f3] + sage: M.phi_extended((1,1,0)) + Z[f3] + sage: M.phi_extended((1,0,1,0,0)) + 3*f2*Z[f3] - 11/2*Z[f5] + + More complicated examples:: + + sage: from sage.modular.multiple_zeta import composition_to_iterated + sage: M.phi_extended(composition_to_iterated((4,3))) + 2/5*f2^2*Z[f3] + 10*f2*Z[f5] - 18*Z[f7] + + sage: M.phi_extended(composition_to_iterated((3,4))) + -10*f2*Z[f5] + 17*Z[f7] + + sage: M.phi_extended(composition_to_iterated((4,2))) + 10/21*f2^3*Z[] - 2*Z[f3,f3] + sage: M.phi_extended(composition_to_iterated((3,5))) + -5*Z[f5,f3] + sage: M.phi_extended(composition_to_iterated((3,7))) + -6*Z[f5,f5] - 14*Z[f7,f3] + + sage: M.phi_extended(composition_to_iterated((3,3,2))) + -793/875*f2^4*Z[] - 4*f2*Z[f3,f3] + 9*Z[f3,f5] - 9/2*Z[f5,f3] + + TESTS:: + + sage: M.phi_extended(tuple([])) + Z[] + """ + # this is now hardcoded + # prec = 1024 + f = F_ring_generator + if not w: + F = F_ring(self.base_ring()) + empty = F.indices()([]) + return F.monomial(empty) + N = len(w) + compo = tuple(iterated_to_composition(w)) + BRf2 = PolynomialRing(self.base_ring(), 'f2') + if compo in B_data[N]: + # do not forget the sign + result_QQ = (-1)**len(compo) * phi_on_multiplicative_basis(compo) + return result_QQ.base_extend(BRf2) + u = compute_u_on_basis(w) + rho_inverse_u = rho_inverse(u) + xi = self.composition_on_basis(w, QQ) + c_xi = (xi - rho_inverse_u)._numerical_approx_pari() + c_xi /= Multizeta(N)._numerical_approx_pari() + c_xi = c_xi.bestappr().sage() # in QQ + result_QQ = u + c_xi * f(N) + return result_QQ.base_extend(BRf2) + + @lazy_attribute + def phi(self): + """ + Return the morphism ``phi``. + + This sends multiple zeta values to the algebra :func:`F_ring`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: m = Multizeta(1,0,1,0) + 2*Multizeta(1,1,0,0); m + 2*I(1100) + I(1010) + sage: M.phi(m) + 1/2*f2^2*Z[] + + sage: Z = Multizeta + sage: B5 = [3*Z(1,4) + 2*Z(2,3) + Z(3,2), 3*Z(1,4) + Z(2,3)] + sage: [M.phi(b.iterated()) for b in B5] + [f2*Z[f3] - 1/2*Z[f5], 1/2*Z[f5]] + + sage: B6 = [6*Z(1,5) + 3*Z(2,4) + Z(3,3), + ....: 6*Z(1,1,4) + 4*Z(1,2,3) + 2*Z(1,3,2) + 2*Z(2,1,3) + Z(2,2,2)] + sage: [M.phi(b.iterated()) for b in B6] + [Z[f3,f3], 1/6*f2^3*Z[]] + """ + cod = F_ring(self.base_ring()) + return self.module_morphism(self.phi_extended, codomain=cod) + + def _element_constructor_(self, x): + r""" + Convert ``x`` into ``self``. + + INPUT + + - ``x`` -- either a list, tuple, word or a multiple zeta value + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: x = Word((1,0,1,0,0)) + sage: M(x) + I(10100) + sage: y = M((1,1,0,0)); y + I(1100) + sage: y == M(y) + True + """ + if isinstance(x, (str, (FiniteWord_class, tuple, list))): + if x: + assert all(letter in (0, 1) for letter in x), 'bad letter' + assert x[0] == 1, 'bad first letter, should be 1' + assert x[-1] == 0, 'bad last letter, should be 0' + W = self.basis().keys() + return self.monomial(W(x)) + + P = x.parent() + if isinstance(P, Multizetas_iterated): + if P is self: + return x + if P is not self.base_ring(): + return self.element_class(self, x.monomial_coefficients()) + elif isinstance(P, Multizetas): + return x.iterated() + + R = self.base_ring() + # coercion via base ring + x = R(x) + if x == 0: + return self.element_class(self, {}) + else: + return self.from_base_ring_from_one_basis(x) + + class Element(CombinatorialFreeModule.Element): + def simplify(self): + """ + Gather terms using the duality relations. + + This can help to lower the number of monomials. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: z = 4*M((1,0,0)) + 3*M((1,1,0)) + sage: z.simplify() + I(100) + """ + summing = self.parent().sum_of_terms + return summing(minimize_term(w, cf) + for w, cf in self.monomial_coefficients().items()) + + def composition(self): + """ + Convert to the algebra of multiple zeta values of composition style. + + This means the algebra :class:`Multizetas`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: x = M((1,0,1,0)) + sage: x.composition() + ζ(2,2) + sage: x = M((1,0,1,0,0)) + sage: x.composition() + ζ(2,3) + sage: x = M((1,0,1,0,0,1,0)) + sage: x.composition() + -ζ(2,3,2) + """ + return self.parent().composition(self) + + def numerical_approx(self, prec=None, digits=None, algorithm=None): + """ + Return a numerical approximation as a sage real. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: x = M((1,0,1,0)) + sage: y = M((1, 0, 0)) + sage: (3*x+y).n() # indirect doctest + 1.23317037269047 + """ + return self.composition().numerical_approx(prec=prec, digits=digits, algorithm=algorithm) + + def phi(self): + """ + Return the image of ``self`` by the morphism ``phi``. + + This sends multiple zeta values to the algebra :func:`F_ring`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: M((1,1,0)).phi() + Z[f3] + """ + return self.parent().phi(self) + + def __eq__(self, other): + """ + Test for equality. + + This means equality as motivic multiple zeta value, computed + using the morphism ``phi``. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: M((1,1,0)) == -M((1,0,0)) + True + + sage: M = Multizetas(QQ) + sage: a = 28*M((3,9))+150*M((5,7))+168*M((7,5)) + sage: b = 5197/691*M((12,)) + sage: a.iterated() == b.iterated() # not tested, long time 20s + True + """ + return self.phi() == other.phi() + + def __ne__(self, other): + """ + Test for non-equality. + + This means non-equality as motivic multiple zeta value, computed + using the morphism ``phi``. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import Multizetas_iterated + sage: M = Multizetas_iterated(QQ) + sage: M((1,0)) == M((1,0,0)) + False + """ + return not (self == other) + + +class All_iterated(CombinatorialFreeModule): + r""" + Auxiliary class for multiple zeta value as generalized iterated integrals. + + This is used to represent multiple zeta values as possibly + divergent iterated integrals + of the differential forms `\omega_0 = \dt/t`and `\omega_1 = \dt/(t-1)`. + + This means that the elements are symbols + `I(a_0 ; a_1,a_2,...a_m ; a_{n+1})` + where all arguments, including the starting and ending points + can be 0 or 1. + + This comes with a "regularise" method mapping + to :class:`Multizeta_iterated`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ); M + Space of motivic multiple zeta values as general iterated integrals + over Rational Field + sage: M((0,1,0,1)) + I(0;10;1) + sage: x = M((1,1,0,0)); x + I(1;10;0) + sage: x.regularise() + -I(10) + """ + def __init__(self, R): + """ + TESTS:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ) + sage: TestSuite(M).run() # not tested + """ + if R not in Rings(): + raise TypeError("argument R must be a ring") + CombinatorialFreeModule.__init__(self, R, Words10, prefix="I") + + def _repr_(self): + """ + Return a string representation of the module. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ); M + Space of motivic multiple zeta values as general iterated integrals over Rational Field + """ + txt = "Space of motivic multiple zeta values as general iterated integrals over {}" + return txt.format(self.base_ring()) + + def _repr_term(self, m): + """ + Return a custom string representation for the monomials. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ) + sage: x = Word((1,0,1,0,0)) + sage: M(x) # indirect doctest + I(1;010;0) + """ + start = str(m[0]) + end = str(m[-1]) + mid = ''.join(str(letter) for letter in m[1:-1]) + return "I(" + start + ";" + mid + ";" + end + ")" + + def _element_constructor_(self, x): + r""" + Convert ``x`` into ``self``. + + INPUT + + - ``x`` -- either a list, tuple, word + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ) + sage: y = M((1,1,0,0)); y + I(1;10;0) + sage: y == M(y) + True + """ + if isinstance(x, (str, (FiniteWord_class, tuple, list))): + if x: + assert all(letter in (0, 1) for letter in x), 'bad letter' + # assert len(x) >= 4, 'word too short' + W = self.basis().keys() + mot = W(x) + # conditions R1 de F. Brown + if mot[0] == mot[-1] or (len(x) >= 4 and + all(x == mot[1] for x in mot[2:-1])): + return self.zero() + return self.monomial(mot) + + def dual_on_basis(self, w): + """ + Reverse the word and exchange the letters 0 and 1. + + This is the operation R4 in [Brown2012]_. + + This should be used only when `a_0 = 0` and `a_{n+1} = 1`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ) + sage: x = Word((0,0,1,0,1)) + sage: M.dual_on_basis(x) + I(0;010;1) + sage: x = Word((0,1,0,1,1)) + sage: M.dual_on_basis(x) + -I(0;010;1) + """ + if w[-2] == 0: + return self(w) + rev = [1 - x for x in reversed(w)] + return (-1)**len(w) * self(self.basis().keys()(rev)) + + @lazy_attribute + def dual(self): + """ + Reverse words and exchange the letters 0 and 1. + + This is the operation R4 in [Brown2012]_. + + This should be used only when `a_0 = 0` and `a_{n+1} = 1`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ) + sage: x = Word((0,0,1,1,1)) + sage: y = Word((0,0,1,0,1)) + sage: M.dual(M(x)+5*M(y)) + 5*I(0;010;1) - I(0;001;1) + """ + return self.module_morphism(self.dual_on_basis, codomain=self) + + def reversal_on_basis(self, w): + """ + Reverse the word if necessary. + + This is the operation R3 in [Brown2012]_. + + This reverses the word only if `a_0 = 0` and `a_{n+1} = 1`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ) + sage: x = Word((1,0,1,0,0)) + sage: M.reversal_on_basis(x) + -I(0;010;1) + sage: x = Word((0,0,1,1,1)) + sage: M.reversal_on_basis(x) + I(0;011;1) + """ + if w[0] == 0 and w[-1] == 1: + return self(w) + W = self.basis().keys() + return (-1)**len(w) * self.monomial(W(list(reversed(w)))) + + @lazy_attribute + def reversal(self): + """ + Reverse words if necessary. + + This is the operation R3 in [Brown2012]_. + + This reverses the word only if `a_0 = 0` and `a_{n+1} = 1`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ) + sage: x = Word((1,0,1,0,0)) + sage: y = Word((0,0,1,1,1)) + sage: M.reversal(M(x)+2*M(y)) + 2*I(0;011;1) - I(0;010;1) + """ + return self.module_morphism(self.reversal_on_basis, codomain=self) + + def expand_on_basis(self, w): + """ + Perform an expansion as a linear combination. + + This is the operation R2 in [Brown2012]_. + + This should be used only when `a_0 = 0` and `a_{n+1} = 1`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ) + sage: x = Word((0,0,1,0,1)) + sage: M.expand_on_basis(x) + -2*I(0;100;1) + + sage: x = Word((0,0,0,1,0,1,0,0,1)) + sage: M.expand_on_basis(x) + 6*I(0;1010000;1) + 6*I(0;1001000;1) + 3*I(0;1000100;1) + + sage: x = Word((0,1,1,0,1)) + sage: M.expand_on_basis(x) + I(0;110;1) + """ + if w[1] == 1: + return self(w) + + mot = w[1:-1] + n_zeros = [] + k = 0 + for x in mot: + if x == 0: + k += 1 + else: + n_zeros.append(k) + k = 1 + n_zeros.append(k) + k = n_zeros[0] + n_zeros = n_zeros[1:] + r = len(n_zeros) + + resu = self.zero() + for idx in IntegerVectors(k, r): + coeff = ZZ.prod(ZZ(nj + ij - 1).binomial(ij) + for nj, ij in zip(n_zeros, idx)) + indice = [0] + for nj, ij in zip(n_zeros, idx): + indice += [1] + [0] * (nj + ij - 1) + resu += coeff * self(indice + [1]) + return (-1)**k * resu # attention au signe + + @lazy_attribute + def expand(self): + """ + Perform an expansion as a linear combination. + + This is the operation R2 in [Brown2012]_. + + This should be used only when `a_0 = 0` and `a_{n+1} = 1`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ) + sage: x = Word((0,0,1,0,1)) + sage: y = Word((0,0,1,1,1)) + sage: M.expand(M(x)+2*M(y)) + -2*I(0;110;1) - 2*I(0;101;1) - 2*I(0;100;1) + sage: M.expand(M([0,1,1,0,1])) + I(0;110;1) + sage: M.expand(M([0,1,0,0,1])) + I(0;100;1) + """ + return self.module_morphism(self.expand_on_basis, codomain=self) + + class Element(CombinatorialFreeModule.Element): + def conversion(self): + """ + Conversion to the :class:`Multizeta_iterated`. + + This assumed that the element has been prepared. + + Not to be used directly. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ) + sage: x = Word((0,1,0,0,1)) + sage: y = M(x).conversion(); y + I(100) + sage: y.parent() + Algebra of motivic multiple zeta values as convergent iterated + integrals over Rational Field + """ + M = Multizetas_iterated(self.parent().base_ring()) + return sum(cf * M.monomial(w[1:-1]) for w, cf in self) + + def regularise(self): + """ + Conversion to the :class:`Multizeta_iterated`. + + This is the regularisation procedure, done in several steps. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import All_iterated + sage: M = All_iterated(QQ) + sage: x = Word((0,0,1,0,1)) + sage: M(x).regularise() + -2*I(100) + sage: x = Word((0,1,1,0,1)) + sage: M(x).regularise() + I(110) + + sage: x = Word((1,0,1,0,0)) + sage: M(x).regularise() + 2*I(100) + """ + P = self.parent() + step1 = P.reversal(self) # R3 + step2 = P.expand(step1) # R2 + step3 = P.dual(step2) # R4 + step4 = P.expand(step3) # R2 + return step4.conversion() # dans Multizeta_iterated + + +# **************** procedures after F. Brown ************ + + +def F_ring(basering, N=18): + r""" + Return the free Zinbiel algebra on many generators `f_3,f_5,\dots` + over the polynomial ring with generator `f_2`. + + For the moment, only with a finite number of variables. + + INPUT: + + - ``N`` -- an integer (default 18), upper bound for indices of generators + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import F_ring + sage: F_ring(QQ) + Free Zinbiel algebra on generators (Z[f3], Z[f5], Z[f7], Z[f9], ...) + over Univariate Polynomial Ring in f2 over Rational Field + """ + ring = PolynomialRing(basering, ['f2']) + return FreeZinbielAlgebra(ring, ['f{}'.format(k) + for k in range(3, N, 2)]) + + +def F_prod(a, b): + """ + Return the associative and commutative product of ``a`` and ``b``. + + INPUT: + + - ``a``, ``b`` -- two elements of the F ring + + OUTPUT: + + an element of the F ring + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import F_ring_generator, F_prod + sage: f2 = F_ring_generator(2) + sage: f3 = F_ring_generator(3) + sage: F_prod(f2,f2) + f2^2*Z[] + sage: F_prod(f2,f3) + f2*Z[f3] + sage: F_prod(f3,f3) + 2*Z[f3,f3] + sage: F_prod(3*f2+5*f3,6*f2+f3) + 18*f2^2*Z[] + 33*f2*Z[f3] + 10*Z[f3,f3] + """ + F = a.parent() + empty = F.indices()([]) + one = F.monomial(empty) + ct_a = a.coefficient(empty) + ct_b = b.coefficient(empty) + rem_a = a - ct_a * one + rem_b = b - ct_b * one + resu = ct_a * ct_b * one + ct_a * rem_b + ct_b * rem_a + return resu + rem_a * rem_b + rem_b * rem_a + + +def F_ring_generator(i): + r""" + Return the generator of the F ring over `\QQ`. + + INPUT: + + - ``i`` -- a nonnegative integer + + If ``i`` is odd, this returns a single generator `f_i` of the free + shuffle algebra. + + Otherwise, it returns an appropriate multiple of a power of `f_2`. + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import F_ring_generator + sage: [F_ring_generator(i) for i in range(2,8)] + [f2*Z[], Z[f3], 2/5*f2^2*Z[], Z[f5], 8/35*f2^3*Z[], Z[f7]] + """ + F = F_ring(QQ) + one = F.monomial(Word([])) + f2 = F.base_ring().gen() + if i == 2: + return f2 * one + # now i odd >= 3 + if i % 2: + return F.monomial(Word(['f{}'.format(i)])) + i = i // 2 + B = bernoulli(2 * i) * (-1)**(i - 1) + B *= ZZ(2)**(3 * i - 1) * ZZ(3)**i / ZZ(2 * i).factorial() + return B * f2**i * one + + +def coeff_phi(w): + """ + Return the coefficient of `f_k` in the image by ``phi``. + + INPUT: + + - ``w`` -- a word in 0 and 1 with `k` letters (where `k` is odd) + + OUTPUT: + + a rational number + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import coeff_phi + sage: coeff_phi(Word([1,0,0])) + -1 + sage: coeff_phi(Word([1,1,0])) + 1 + sage: coeff_phi(Word([1,1,0,1,0])) + 11/2 + sage: coeff_phi(Word([1,1,0,0,0,1,0])) + 109/16 + """ + if all(x == 0 for x in w[1:]): + return -1 # beware the sign + k = len(w) + assert k % 2 + M = Multizetas_iterated(QQ) + z = M.phi_extended(w) + W = z.parent().basis().keys() + w = W(['f{}'.format(k)]) + return z.coefficient(w).lc() # in QQ + + +def phi_on_multiplicative_basis(compo): + """ + Compute ``phi`` on one single multiple zeta value. + + INPUT: + + - ``compo`` -- a composition (in the hardcoded multiplicative base) + + OUTPUT: + + an element in :func:`F_ring` with rational coefficients + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import phi_on_multiplicative_basis + sage: phi_on_multiplicative_basis((2,)) + f2*Z[] + sage: phi_on_multiplicative_basis((3,)) + Z[f3] + """ + f = F_ring_generator + F = F_ring(QQ) + one = F.monomial(Word([])) + + if tuple(compo) == (2,): + return f(2) * one + + if len(compo) == 1: + n, = compo + return f(n) + + return compute_u_on_compo(compo) + + +def phi_on_basis(L): + """ + Compute the value of phi on the hardcoded basis. + + INPUT: + + a list of compositions, each composition in the hardcoded basis + + This encodes a product of multiple zeta values. + + OUTPUT: + + an element in :func:`F_ring` + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import phi_on_basis + sage: phi_on_basis([(3,),(3,)]) + 2*Z[f3,f3] + sage: phi_on_basis([(2,),(2,)]) + f2^2*Z[] + sage: phi_on_basis([(2,),(3,),(3,)]) + 2*f2*Z[f3,f3] + """ + # beware that the default * is the half-shuffle ! + F = F_ring(QQ) + resu = F.monomial(Word([])) + for compo in L: + resu = F_prod(resu, phi_on_multiplicative_basis(compo)) + return resu + + +def D_on_compo(k, compo): + """ + Return the value of the operator `D_k` on a multiple zeta value. + + This is now only used as a place to keep many doctests. + + INPUT: + + - ``k`` -- an odd integer + + - ``compo`` -- a composition + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import D_on_compo + sage: D_on_compo(3,(2,3)) + 3*I(100) # I(10) + + sage: D_on_compo(3,(4,3)) + I(100) # I(1000) + sage: D_on_compo(5,(4,3)) + 10*I(10000) # I(10) + + sage: [D_on_compo(k, [3,5]) for k in (3,5,7)] + [0, -5*I(10000) # I(100), 0] + + sage: [D_on_compo(k, [3,7]) for k in (3,5,7,9)] + [0, -6*I(10000) # I(10000), -14*I(1000000) # I(100), 0] + + sage: D_on_compo(3,(4,3,3)) + -I(100) # I(1000100) + sage: D_on_compo(5,(4,3,3)) + -10*I(10000) # I(10100) + sage: D_on_compo(7,(4,3,3)) + 4*I(1001000) # I(100) + 2*I(1000100) # I(100) + + sage: [D_on_compo(k,(1,3,1,3,1,3)) for k in range(3,10,2)] + [0, 0, 0, 0] + """ + it = composition_to_iterated(compo) + M = Multizetas_iterated(QQ) + return (-1)**len(compo) * M.D_on_basis(k, it) + + +def compute_u_on_compo(compo): + r""" + Compute the value of the map ``u`` on a multiple zeta value. + + INPUT: + + - ``compo`` -- a composition + + OUTPUT: + + an element of :func:`F_ring` over `\QQ` + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import compute_u_on_compo + sage: compute_u_on_compo((2,4)) + 2*Z[f3,f3] + sage: compute_u_on_compo((2,3,2)) + -11/2*f2*Z[f5] + sage: compute_u_on_compo((3,2,3,2)) + 11*f2*Z[f3,f5] - 75/4*Z[f3,f7] - 9*f2*Z[f5,f3] + 81/4*Z[f5,f5] + 75/8*Z[f7,f3] + """ + it = composition_to_iterated(compo) + return (-1)**len(compo) * compute_u_on_basis(it) + + +def compute_u_on_basis(w): + r""" + Compute the value of ``u`` on a multiple zeta value. + + INPUT: + + - ``w`` -- a word in 0,1 + + OUTPUT: + + an element of :func:`F_ring` over `\QQ` + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import compute_u_on_basis + sage: compute_u_on_basis((1,0,0,0,1,0)) + -2*Z[f3,f3] + + sage: compute_u_on_basis((1,1,1,0,0)) + f2*Z[f3] + + sage: compute_u_on_basis((1,0,0,1,0,0,0,0)) + -5*Z[f5,f3] + + sage: compute_u_on_basis((1,0,1,0,0,1,0)) + 11/2*f2*Z[f5] + + sage: compute_u_on_basis((1,0,0,1,0,1,0,0,1,0)) + 11*f2*Z[f3,f5] - 75/4*Z[f3,f7] - 9*f2*Z[f5,f3] + 81/4*Z[f5,f5] + + 75/8*Z[f7,f3] + """ + M = Multizetas_iterated(QQ) + F = F_ring(QQ) + f = F_ring_generator + N = len(w) + xi_dict = {} + for k in range(3, N, 2): + xi_dict[k] = F.sum(cf * coeff_phi(ww[0]) * M.phi_extended(tuple(ww[1])) + for ww, cf in M.D_on_basis(k, w)) + return F.sum(f(k) * xi_dict[k] for k in range(3, N, 2)) + + +def f_to_vector(elt): + """ + Convert an element of F ring to a vector. + + INPUT: + + an homogeneous element of :func:`F_ring` over some base ring + + OUTPUT: + + a vector with coefficients in the base ring + + .. SEEALSO:: :func:`vector_to_f` + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import F_ring, vector_to_f, f_to_vector + sage: F = F_ring(QQ) + sage: f2 = F.base_ring().gen() + sage: x = f2**4*F.monomial(Word([]))+f2*F.monomial(Word(['f3','f3'])) + sage: f_to_vector(x) + (0, 0, 1, 1) + sage: vector_to_f(_,8) + f2^4*Z[] + f2*Z[f3,f3] + + sage: x = F.monomial(Word(['f11'])); x + Z[f11] + sage: f_to_vector(x) + (1, 0, 0, 0, 0, 0, 0, 0, 0) + """ + F = elt.parent() + BR = F.base_ring().base_ring() + a, b = next(iter(elt)) + N = sum(int(x[1:]) for x in a) + 2 * b.degree() + W = F.basis().keys() + return vector(BR, [elt.coefficient(W(b)).lc() + for _, b in basis_f_iterator(N)]) + + +def vector_to_f(vec, N): + """ + Convert back a vector to an element of the F ring. + + INPUT: + + a vector with coefficients in some base ring + + OUTPUT: + + an homogeneous element of :func:`F_ring` over this base ring + + .. SEEALSO:: :func:`f_to_vector` + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import vector_to_f, f_to_vector + sage: vector_to_f((4,5),6) + 5*f2^3*Z[] + 4*Z[f3,f3] + sage: f_to_vector(_) + (4, 5) + """ + if isinstance(vec, (list, tuple)): + vec = vector(vec) + BR = vec.base_ring() + F = F_ring(BR) + f2 = F.base_ring().gen() + basis_F = (f2**k * F.monomial(b) + for k, b in basis_f_iterator(N)) + return sum(cf * bi for cf, bi in zip(vec, basis_F)) + + +@cached_function +def rho_matrix_inverse(n): + """ + Return the matrix of the inverse of ``rho``. + + This is the matrix in the chosen bases, namely the hardcoded basis + of multiple zeta values and the natural basis of the F ring. + + INPUT: + + - ``n`` -- an integer + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import rho_matrix_inverse + sage: rho_matrix_inverse(3) + [1] + sage: rho_matrix_inverse(8) + [-1/5 0 0 0] + [ 1/5 1 0 0] + [ 0 0 1/2 0] + [ 0 0 0 1] + """ + base = extend_multiplicative_basis(B_data, n) + resu = [] + for b in base: + phi_b = phi_on_basis(b) + resu.append(f_to_vector(phi_b)) + dN = len(resu) + return ~matrix(QQ, dN, dN, resu) + + +def rho_inverse(elt): + """ + Return the image by the inverse of ``rho``. + + INPUT: + + - ``elt`` -- an homogeneous element of the F ring + + OUTPUT: + + a linear combination of multiple zeta values + + EXAMPLES:: + + sage: from sage.modular.multiple_zeta import F_ring_generator, rho_inverse + sage: f = F_ring_generator + sage: rho_inverse(f(3)) + ζ(3) + sage: rho_inverse(f(9)) + ζ(9) + """ + pa = elt.parent() + BR = pa.base_ring().base_ring() + M_BR = Multizetas(BR) + if elt == pa.zero(): + return M_BR.zero() + + a, b = next(iter(elt)) + N = sum(int(x[1:]) for x in a) + 2 * b.degree() + + v = f_to_vector(elt) + w = v * rho_matrix_inverse(N) + return sum(cf * b for cf, b in zip(w, M_BR.basis_data(BR, N))) diff --git a/src/sage/modular/overconvergent/genus0.py b/src/sage/modular/overconvergent/genus0.py index 755289a75ba..e8fd60393ac 100644 --- a/src/sage/modular/overconvergent/genus0.py +++ b/src/sage/modular/overconvergent/genus0.py @@ -172,11 +172,9 @@ # https://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range -from six import integer_types from sage.matrix.all import matrix, MatrixSpace, diagonal_matrix -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.misc.cachefunc import cached_method from sage.modular.all import (trivial_character, EtaProduct, j_invariant_qexp, hecke_operator_on_qexp) @@ -732,12 +730,10 @@ def _element_constructor_(self, input): sage: M10(M(f)) 3-adic overconvergent modular form of weight-character 0 with q-expansion 27*q + 324*q^2 + 2430*q^3 + 13716*q^4 + O(q^5) """ - if isinstance(input, integer_types): + if isinstance(input, int): input = ZZ(input) if isinstance(input, OverconvergentModularFormElement): - if input.parent() is self: - return input return self._coerce_from_ocmf(input) elif isinstance(input, ModularFormElement): @@ -1090,17 +1086,16 @@ def eigenfunctions(self, n, F = None, exact_arith=True): eigenfunctions = [] verbose("Expected %s eigenvalues, got %s" % (n, len(eigenvalues))) for (r, d) in eigenvalues: - v = r.valuation() if d != 1: continue - mr = (m.__pari__() - r.__pari__()) + mr = m.__pari__() - r.__pari__() # Annoying thing: r isn't quite as precise as it claims to be # (bug reported to sage-support list) while F(mr.matdet()) != 0: verbose("p-adic solver returned wrong result in slope %s; refining" % r.valuation(), level=2) r = r - cp(r)/cp.derivative()(r) - mr2 = (m.__pari__() - r.__pari__()) + mr2 = m.__pari__() - r.__pari__() if mr2.matdet().valuation(self.prime()) > mr.matdet().valuation(self.prime()): mr = mr2 else: diff --git a/src/sage/modular/overconvergent/hecke_series.py b/src/sage/modular/overconvergent/hecke_series.py index 164fd07a1e2..3320b6464f6 100644 --- a/src/sage/modular/overconvergent/hecke_series.py +++ b/src/sage/modular/overconvergent/hecke_series.py @@ -66,9 +66,9 @@ # 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 six.moves import range +# http://www.gnu.org/licenses/ +#***************************************************************************** + from sage.functions.all import floor, ceil from sage.arith.all import valuation from sage.rings.all import ZZ, Zmod, Infinity, Integer @@ -78,7 +78,8 @@ from sage.misc.functional import dimension,transpose,charpoly from sage.matrix.constructor import matrix, random_matrix from sage.matrix.matrix_space import MatrixSpace -from sage.misc.misc import cputime, verbose +from sage.misc.misc import cputime +from sage.misc.verbose import verbose # AUXILIARY CODE: SPACES OF MODULAR FORMS AND LINEAR ALGEBRA diff --git a/src/sage/modular/overconvergent/weightspace.py b/src/sage/modular/overconvergent/weightspace.py index 9ff96b6b9f2..f660ba97e6f 100644 --- a/src/sage/modular/overconvergent/weightspace.py +++ b/src/sage/modular/overconvergent/weightspace.py @@ -61,9 +61,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# https://www.gnu.org/licenses/ -# **************************************************************************** -from six.moves import range +# http://www.gnu.org/licenses/ +#***************************************************************************** from sage.structure.parent_base import ParentWithBase from sage.structure.element import Element @@ -72,7 +71,6 @@ from sage.rings.all import ZZ, QQ, IntegerModRing, Qp, Infinity from sage.arith.all import divisors from sage.rings.padics.padic_generic_element import pAdicGenericElement -from sage.misc.misc import verbose from sage.misc.cachefunc import cached_method from sage.rings.padics.precision_error import PrecisionError import weakref @@ -725,6 +723,7 @@ def __call__(self, x): sage: kappa(2 + 2*23 + 11*23^2 + O(23^3)) 16 + 7*23 + O(23^3) """ + from sage.misc.verbose import verbose if not isinstance(x, pAdicGenericElement): x = Qp(self._p)(x) diff --git a/src/sage/modular/pollack_stevens/dist.pyx b/src/sage/modular/pollack_stevens/dist.pyx index f7025f2dec3..eec63763660 100644 --- a/src/sage/modular/pollack_stevens/dist.pyx +++ b/src/sage/modular/pollack_stevens/dist.pyx @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +# distutils: libraries = gmp zn_poly +# distutils: extra_compile_args = -D_XPG6 """ `p`-adic distributions spaces @@ -46,7 +48,8 @@ from sage.rings.padics.padic_capped_relative_element cimport pAdicCappedRelative from sage.rings.padics.padic_fixed_mod_element cimport pAdicFixedModElement from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational -from sage.misc.misc import verbose, cputime +from sage.misc.misc import cputime +from sage.misc.verbose import verbose from sage.rings.infinity import Infinity from sage.libs.flint.nmod_poly cimport (nmod_poly_init2_preinv, diff --git a/src/sage/modular/pollack_stevens/distributions.py b/src/sage/modular/pollack_stevens/distributions.py index 97a015d10bf..a4aab63a1c3 100644 --- a/src/sage/modular/pollack_stevens/distributions.py +++ b/src/sage/modular/pollack_stevens/distributions.py @@ -41,7 +41,6 @@ #***************************************************************************** from __future__ import print_function from __future__ import absolute_import -from six.moves import range from sage.modules.module import Module from sage.structure.parent import Parent diff --git a/src/sage/modular/pollack_stevens/fund_domain.py b/src/sage/modular/pollack_stevens/fund_domain.py index eb75ded7468..545dc60b174 100644 --- a/src/sage/modular/pollack_stevens/fund_domain.py +++ b/src/sage/modular/pollack_stevens/fund_domain.py @@ -23,7 +23,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import -from six import iteritems from sage.matrix.matrix_space import MatrixSpace from sage.modular.modsym.all import P1List @@ -1540,7 +1539,7 @@ def prep_hecke_on_gen_list(self, l, gen, modulus=None): 4 """ ans = [] - for h, vh in iteritems(self.prep_hecke_on_gen(l, gen, modulus=modulus)): + for h, vh in self.prep_hecke_on_gen(l, gen, modulus=modulus).items(): ans.extend([(h, v) for v in vh]) return ans diff --git a/src/sage/modular/pollack_stevens/manin_map.py b/src/sage/modular/pollack_stevens/manin_map.py index e5118e89d7a..668d115d100 100644 --- a/src/sage/modular/pollack_stevens/manin_map.py +++ b/src/sage/modular/pollack_stevens/manin_map.py @@ -43,8 +43,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, absolute_import -from six import iteritems -from six.moves import range from sage.rings.continued_fraction import convergents from .sigma0 import Sigma0 @@ -405,7 +403,7 @@ def __add__(self, right): D = {} sd = self._dict rd = right._dict - for ky, val in iteritems(sd): + for ky, val in sd.items(): if ky in rd: D[ky] = val + rd[ky] return self.__class__(self._codomain, self._manin, D, check=False) @@ -442,7 +440,7 @@ def __sub__(self, right): D = {} sd = self._dict rd = right._dict - for ky, val in iteritems(sd): + for ky, val in sd.items(): if ky in rd: D[ky] = val - rd[ky] return self.__class__(self._codomain, self._manin, D, check=False) @@ -480,7 +478,7 @@ def __mul__(self, right): return self._right_action(right) D = {} - for ky, val in iteritems(self._dict): + for ky, val in self._dict.items(): D[ky] = val * right return self.__class__(self._codomain, self._manin, D, check=False) @@ -614,7 +612,7 @@ def apply(self, f, codomain=None, to_moments=False): sd = self._dict if codomain is None: codomain = self._codomain - for ky, val in iteritems(sd): + for ky, val in sd.items(): if to_moments: D[ky] = codomain([f(val.moment(a)) for a in range(val.precision_absolute())]) @@ -738,7 +736,7 @@ def reduce_precision(self, M): 1 + O(11^2) """ D = {} - for ky, val in iteritems(self._dict): + for ky, val in self._dict.items(): D[ky] = val.reduce_precision(M) return self.__class__(self._codomain, self._manin, D, check=False) @@ -760,7 +758,7 @@ def specialize(self, *args): Sym^0 Z_11^2 """ D = {} - for ky, val in iteritems(self._dict): + for ky, val in self._dict.items(): D[ky] = val.specialize(*args) return self.__class__(self._codomain.specialize(*args), self._manin, D, check=False) diff --git a/src/sage/modular/pollack_stevens/modsym.py b/src/sage/modular/pollack_stevens/modsym.py index e4d92f7f33a..fba726d0468 100644 --- a/src/sage/modular/pollack_stevens/modsym.py +++ b/src/sage/modular/pollack_stevens/modsym.py @@ -47,7 +47,7 @@ from sage.rings.polynomial.all import PolynomialRing from sage.rings.padics.padic_generic import pAdicGeneric from sage.arith.all import next_prime, gcd, kronecker -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.rings.padics.precision_error import PrecisionError from sage.categories.action import Action diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py index 689c5debddc..571cc1f3803 100644 --- a/src/sage/modular/quatalg/brandt.py +++ b/src/sage/modular/quatalg/brandt.py @@ -196,7 +196,8 @@ class if `I=aJ` for some `a \in A^*`. (Left `\mathcal{O}`-ideals are from __future__ import print_function # imports -from sage.misc.all import prod, verbose +from sage.misc.all import prod +from sage.misc.verbose import verbose from sage.rings.all import Integer, ZZ, QQ, PolynomialRing, GF, CommutativeRing from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra, basis_for_quaternion_lattice diff --git a/src/sage/modular/ssmod/ssmod.py b/src/sage/modular/ssmod/ssmod.py index 6882c1c51c3..c0198b3fdfd 100644 --- a/src/sage/modular/ssmod/ssmod.py +++ b/src/sage/modular/ssmod/ssmod.py @@ -75,7 +75,6 @@ from sage.matrix.matrix_space import MatrixSpace from sage.modular.arithgroup.all import Gamma0 from sage.libs.pari.all import pari -from sage.misc.misc import verbose from sage.structure.richcmp import richcmp_method, richcmp ZZy = PolynomialRing(ZZ, 'y') @@ -736,6 +735,8 @@ def upper_bound_on_elliptic_factors(self, p=None, ellmax=2): (There are 4 elliptic curves of conductor 37, but only 2 isogeny classes.) """ + from sage.misc.verbose import verbose + # NOTE: The heuristic runtime is *very* roughly `p^2/(2\cdot 10^6)`. # ellmax -- (default: 2) use Hecke operators T_ell with ell <= ellmax if p is None: diff --git a/src/sage/modules/diamond_cutting.py b/src/sage/modules/diamond_cutting.py index f76e024d4cd..bb1f4e9f8c0 100644 --- a/src/sage/modules/diamond_cutting.py +++ b/src/sage/modules/diamond_cutting.py @@ -18,7 +18,6 @@ from sage.geometry.polyhedron.constructor import Polyhedron from sage.matrix.constructor import matrix, identity_matrix from sage.modules.free_module_element import vector -from sage.rings.rational_field import QQ from math import sqrt, floor, ceil @@ -153,9 +152,6 @@ def diamond_cut(V, GM, C, verbose=False): sage: V.vertices() (A vertex at (2), A vertex at (0)) """ - # coerce to floats - GM = GM.n() - C = float(C) if verbose: print("Cut\n{}\nwith radius {}".format(GM, C)) @@ -223,7 +219,6 @@ def diamond_cut(V, GM, C, verbose=False): cut_count += 1 if verbose: print("\n%d) Cut using normal vector %s" % (cut_count, hv)) - hv = [QQ(elmt.n(digits=6)) for elmt in hv] inequalities.append(plane_inequality(hv)) if verbose: diff --git a/src/sage/modules/filtered_vector_space.py b/src/sage/modules/filtered_vector_space.py index b9ca38ddb6f..221694c3fd7 100644 --- a/src/sage/modules/filtered_vector_space.py +++ b/src/sage/modules/filtered_vector_space.py @@ -107,8 +107,6 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six import iteritems -from six.moves import range from sage.rings.all import QQ, ZZ, RDF, RR, Integer from sage.rings.infinity import InfinityRing, infinity, minus_infinity @@ -317,7 +315,7 @@ def normalize_gen(v): # normalize filtration data normalized = dict() - for deg, gens_deg in iteritems(filtration): + for deg, gens_deg in filtration.items(): indices = [generators.index(normalize_gen(v)) for v in gens_deg] normalized[deg] = tuple(indices) return construct_from_generators_indices(generators, normalized, base_ring, check) @@ -376,7 +374,7 @@ def construct_from_generators_indices(generators, filtration, base_ring, check): # normalize filtration data normalized = dict() - for deg, gens in iteritems(filtration): + for deg, gens in filtration.items(): deg = normalize_degree(deg) gens = [ZZ(i) for i in gens] if any(i < 0 or i >= len(generators) for i in gens): @@ -448,7 +446,7 @@ def __init__(self, base_ring, dim, generators, filtration, check=True): if check: assert matrix(generators).rank() == self.dimension() assert isinstance(filtration, dict) - for degree, indices in iteritems(filtration): + for degree, indices in filtration.items(): assert isinstance(degree, Integer) or degree == infinity assert isinstance(indices, tuple) assert all(isinstance(r, Integer) for r in indices) @@ -1228,7 +1226,7 @@ def shift(self, deg): """ generators, filtration = self.presentation() shifted = dict() - for d, indices in iteritems(filtration): + for d, indices in filtration.items(): shifted[d + deg] = indices return FilteredVectorSpace(generators, shifted, base_ring=self.base_ring()) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 10ba91f559a..8f89c06c9b5 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -162,7 +162,6 @@ # http://www.gnu.org/licenses/ ########################################################################### from __future__ import print_function, absolute_import -from six import integer_types from itertools import islice from . import free_module_element @@ -197,28 +196,154 @@ class FreeModuleFactory(UniqueFactory): r""" - Create the free module over the given commutative ring of the given - rank. + Factory class for the finite-dimensional free modules with standard basis + """ + def create_key(self, base_ring, rank, sparse=False, inner_product_matrix=None): + """ + TESTS:: - INPUT: + sage: loads(dumps(ZZ^6)) is ZZ^6 + True + sage: loads(dumps(RDF^3)) is RDF^3 + True + + .. TODO:: + + Replace the above by ``TestSuite(...).run()``, once + :meth:`_test_pickling` will test unique representation + and not only equality. + """ + rank = int(sage.rings.integer.Integer(rank)) + + if not (inner_product_matrix is None): + inner_product_matrix = sage.matrix.matrix_space.MatrixSpace(base_ring, rank)(inner_product_matrix) + inner_product_matrix.set_immutable() + + return (base_ring, rank, sparse, inner_product_matrix) + def create_object(self, version, key): + + base_ring, rank, sparse, inner_product_matrix = key + + if inner_product_matrix is not None: + from .free_quadratic_module import FreeQuadraticModule + return FreeQuadraticModule(base_ring, rank, inner_product_matrix=inner_product_matrix, sparse=sparse) + + if not isinstance(sparse,bool): + raise TypeError("Argument sparse (= %s) must be True or False" % sparse) + + if not (hasattr(base_ring,'is_commutative') and base_ring.is_commutative()): + warn("""You are constructing a free module +over a noncommutative ring. Sage does not have a concept +of left/right and both sided modules, so be careful. +It's also not guaranteed that all multiplications are +done from the right side.""") + + # raise TypeError, "The base_ring must be a commutative ring." + + try: + if not sparse and isinstance(base_ring,sage.rings.real_double.RealDoubleField_class): + return RealDoubleVectorSpace_class(rank) + + elif not sparse and isinstance(base_ring,sage.rings.complex_double.ComplexDoubleField_class): + return ComplexDoubleVectorSpace_class(rank) + + elif base_ring.is_field(): + return FreeModule_ambient_field(base_ring, rank, sparse=sparse) + + elif base_ring in PrincipalIdealDomains(): + return FreeModule_ambient_pid(base_ring, rank, sparse=sparse) + + elif isinstance(base_ring, sage.rings.number_field.order.Order) \ + and base_ring.is_maximal() and base_ring.class_number() == 1: + return FreeModule_ambient_pid(base_ring, rank, sparse=sparse) + + elif isinstance(base_ring, ring.IntegralDomain) or base_ring.is_integral_domain(): + return FreeModule_ambient_domain(base_ring, rank, sparse=sparse) + + else: + return FreeModule_ambient(base_ring, rank, sparse=sparse) + except NotImplementedError: + return FreeModule_ambient(base_ring, rank, sparse=sparse) + +FreeModuleFactory_with_standard_basis = FreeModuleFactory("FreeModule") + +def FreeModule(base_ring, rank_or_basis_keys=None, sparse=False, inner_product_matrix=None, *, + with_basis='standard', rank=None, basis_keys=None, **args): + r""" + Create a free module over the given commutative ``base_ring`` + + ``FreeModule`` can be called with the following positional arguments: + + - ``FreeModule(base_ring, rank, ...)`` + + - ``FreeModule(base_ring, basis_keys, ...)`` + + INPUT: - ``base_ring`` - a commutative ring - ``rank`` - a nonnegative integer + - ``basis_keys`` - a finite or enumerated family of arbitrary objects + - ``sparse`` - bool; (default False) - - ``inner_product_matrix`` - the inner product - matrix (default None) + - ``inner_product_matrix`` - the inner product matrix (default ``None``) + - ``with_basis`` - either ``"standard"`` (the default), in which case + a free module with the standard basis as the distinguished basis is created; + or ``None``, in which case a free module without distinguished basis is + created. + + - further options may be accepted by various implementation classes OUTPUT: a free module - .. note:: + This factory function creates instances of various specialized classes + depending on the input. Not all combinations of options are + implemented. + + - If the parameter ``basis_keys`` is provided, it must be a finite + or enumerated family of objects, and an instance of + :class:`CombinatorialFreeModule` is created. + + EXAMPLES:: + + sage: CombinatorialFreeModule(QQ, ['a','b','c']) + Free module generated by {'a', 'b', 'c'} over Rational Field + + It has a distinguished standard basis that is indexed by the provided + ``basis_keys``. See the documentation of :class:`CombinatorialFreeModule` + for more examples and details, including its :class:`UniqueRepresentation` + semantics. + + - If the parameter ``with_basis`` is set to ``None``, then a free module + of the given ``rank`` without distinguished basis is created. It is + represented by an instance of :class:`FiniteRankFreeModule`. + + EXAMPLES:: + + sage: FiniteRankFreeModule(ZZ, 3, name='M') + Rank-3 free module M over the Integer Ring + + See the documentation of :class:`FiniteRankFreeModule` for more + options, examples, and details. + + - If ``rank`` is provided and the option ``with_basis`` is left at its + default value, ``"standard"``, then a free ambient module with + distinguished standard basis indexed by ``range(rank)`` is created. + There is only one dense and one sparse free ambient module of + given ``rank`` over ``base_ring``. + + EXAMPLES:: + + sage: FreeModule(Integers(8), 10) + Ambient free module of rank 10 over Ring of integers modulo 8 + + The remainder of this documentation discusses this case of + free ambient modules. - In Sage it is the case that there is only one dense and one - sparse free ambient module of rank `n` over `R`. EXAMPLES: @@ -231,8 +356,6 @@ class FreeModuleFactory(UniqueFactory): :: - sage: FreeModule(Integers(8),10) - Ambient free module of rank 10 over Ring of integers modulo 8 sage: FreeModule(QQ,10) Vector space of dimension 10 over Rational Field sage: FreeModule(ZZ,10) @@ -323,78 +446,44 @@ class FreeModuleFactory(UniqueFactory): Refactor modules such that it only counts what category the base ring belongs to, but not what is its Python class. - """ - def create_key(self, base_ring, rank, sparse=False, inner_product_matrix=None): - """ - TESTS:: - - sage: loads(dumps(ZZ^6)) is ZZ^6 - True - sage: loads(dumps(RDF^3)) is RDF^3 - True - TODO: replace the above by ``TestSuite(...).run()``, once - :meth:`_test_pickling` will test unique representation and not - only equality. - """ - rank = int(sage.rings.integer.Integer(rank)) - - if not (inner_product_matrix is None): - inner_product_matrix = sage.matrix.matrix_space.MatrixSpace(base_ring, rank)(inner_product_matrix) - inner_product_matrix.set_immutable() - - return (base_ring, rank, sparse, inner_product_matrix) - - def create_object(self, version, key): - - base_ring, rank, sparse, inner_product_matrix = key + EXAMPLES:: - if inner_product_matrix is not None: - from .free_quadratic_module import FreeQuadraticModule - return FreeQuadraticModule(base_ring, rank, inner_product_matrix=inner_product_matrix, sparse=sparse) + sage: FreeModule(QQ, ['a', 'b', 'c']) + Free module generated by {'a', 'b', 'c'} over Rational Field + sage: _.category() + Category of finite dimensional vector spaces with basis over Rational Field - if not isinstance(sparse,bool): - raise TypeError("Argument sparse (= %s) must be True or False" % sparse) - - if not (hasattr(base_ring,'is_commutative') and base_ring.is_commutative()): - warn("""You are constructing a free module -over a noncommutative ring. Sage does not have a concept -of left/right and both sided modules, so be careful. -It's also not guaranteed that all multiplications are -done from the right side.""") - - # raise TypeError, "The base_ring must be a commutative ring." + sage: FreeModule(QQ, 3, with_basis=None) + 3-dimensional vector space over the Rational Field + sage: _.category() + Category of finite dimensional vector spaces over Rational Field + """ + if rank_or_basis_keys is not None: try: - if not sparse and isinstance(base_ring,sage.rings.real_double.RealDoubleField_class): - return RealDoubleVectorSpace_class(rank) - - elif not sparse and isinstance(base_ring,sage.rings.complex_double.ComplexDoubleField_class): - return ComplexDoubleVectorSpace_class(rank) - - elif base_ring.is_field(): - return FreeModule_ambient_field(base_ring, rank, sparse=sparse) - - elif base_ring in PrincipalIdealDomains(): - return FreeModule_ambient_pid(base_ring, rank, sparse=sparse) - - elif isinstance(base_ring, sage.rings.number_field.order.Order) \ - and base_ring.is_maximal() and base_ring.class_number() == 1: - return FreeModule_ambient_pid(base_ring, rank, sparse=sparse) - - elif isinstance(base_ring, ring.IntegralDomain) or base_ring.is_integral_domain(): - return FreeModule_ambient_domain(base_ring, rank, sparse=sparse) - - else: - return FreeModule_ambient(base_ring, rank, sparse=sparse) - except NotImplementedError: - return FreeModule_ambient(base_ring, rank, sparse=sparse) - - -FreeModule = FreeModuleFactory("FreeModule") - + rank = sage.rings.integer_ring.ZZ(rank_or_basis_keys) + except: + basis_keys = rank_or_basis_keys + if not with_basis: + if inner_product_matrix is not None: + raise NotImplementedError + from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule + return FiniteRankFreeModule(base_ring, rank, **args) + elif with_basis == 'standard': + if rank is not None: + return FreeModuleFactory_with_standard_basis(base_ring, rank, sparse, + inner_product_matrix, **args) + else: + if inner_product_matrix is not None: + raise NotImplementedError + from sage.combinat.free_module import CombinatorialFreeModule + return CombinatorialFreeModule(base_ring, basis_keys, **args) + else: + raise NotImplementedError -def VectorSpace(K, dimension, sparse=False, inner_product_matrix=None): +def VectorSpace(K, dimension_or_basis_keys=None, sparse=False, inner_product_matrix=None, *, + with_basis='standard', dimension=None, basis_keys=None, **args): """ EXAMPLES: @@ -425,7 +514,9 @@ def VectorSpace(K, dimension, sparse=False, inner_product_matrix=None): raise TypeError("Argument K (= %s) must be a field." % K) if not sparse in (True,False): raise TypeError("Argument sparse (= %s) must be a boolean."%sparse) - return FreeModule(K, rank=dimension, sparse=sparse, inner_product_matrix=inner_product_matrix) + return FreeModule(K, dimension_or_basis_keys, sparse, inner_product_matrix, + with_basis=with_basis, rank=dimension, basis_keys=basis_keys, + **args) ############################################################################### # @@ -1012,7 +1103,7 @@ def _element_constructor_(self, x, coerce=True, copy=True, check=True): sage: N((0,0,0,1), check=False) in N True """ - if (isinstance(x, integer_types + (sage.rings.integer.Integer,)) and + if (isinstance(x, (int, sage.rings.integer.Integer)) and x == 0): return self.zero_vector() elif isinstance(x, free_module_element.FreeModuleElement): @@ -1653,7 +1744,7 @@ def basis_matrix(self, ring=None): sage: M.basis_matrix(ZZ) Traceback (most recent call last): ... - TypeError: matrix has denominators so can't change to ZZ. + TypeError: matrix has denominators so can...t change to ZZ. """ try: A = self.__basis_matrix diff --git a/src/sage/modules/free_module_element.pxd b/src/sage/modules/free_module_element.pxd index 546d6d08113..64a1760a259 100644 --- a/src/sage/modules/free_module_element.pxd +++ b/src/sage/modules/free_module_element.pxd @@ -1,7 +1,6 @@ from sage.structure.element cimport Vector cdef class FreeModuleElement(Vector): - cdef bint _is_mutable cdef int set_unsafe(self, Py_ssize_t i, value) except -1 cdef get_unsafe(self, Py_ssize_t i) cpdef int hamming_weight(self) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index c0d39da6968..21189e6d6b3 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -385,7 +385,7 @@ def vector(arg0, arg1=None, arg2=None, sparse=None, immutable=False): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) sage: v[3].parent() Integer Ring - sage: v = vector([float(23.4), int(2), complex(2+7*I), long(1)]); v + sage: v = vector([float(23.4), int(2), complex(2+7*I), 1]); v (23.4, 2.0, 2.0 + 7.0*I, 1.0) sage: v[1].parent() Complex Double Field @@ -736,6 +736,7 @@ def zero_vector(arg0, arg1=None): TypeError: first argument must be a ring """ if arg1 is None: + arg0 = ZZ(arg0) # default to a zero vector over the integers (ZZ) if no ring given return (ZZ**arg0).zero_vector() if is_Ring(arg0): @@ -1485,53 +1486,6 @@ cdef class FreeModuleElement(Vector): # abstract base class """ return self.parent()([ a.subs(in_dict, **kwds) for a in self.list() ]) - def set_immutable(self): - """ - Make this vector immutable. This operation can't be undone. - - EXAMPLES:: - - sage: v = vector([1..5]); v - (1, 2, 3, 4, 5) - sage: v[1] = 10 - sage: v.set_immutable() - sage: v[1] = 10 - Traceback (most recent call last): - ... - ValueError: vector is immutable; please change a copy instead (use copy()) - """ - self._is_mutable = 0 - - def is_mutable(self): - """ - Return True if this vector is mutable, i.e., the entries can be - changed. - - EXAMPLES:: - - sage: v = vector(QQ['x,y'], [1..5]); v.is_mutable() - True - sage: v.set_immutable() - sage: v.is_mutable() - False - """ - return self._is_mutable - - def is_immutable(self): - """ - Return True if this vector is immutable, i.e., the entries cannot - be changed. - - EXAMPLES:: - - sage: v = vector(QQ['x,y'], [1..5]); v.is_immutable() - False - sage: v.set_immutable() - sage: v.is_immutable() - True - """ - return not self._is_mutable - def change_ring(self, R): """ Change the base ring of this vector. @@ -2698,15 +2652,14 @@ cdef class FreeModuleElement(Vector): # abstract base class Return the matrix which describes a cross product between ``self`` and some other vector. - This operation is sometimes written using the `hat operator`_. + This operation is sometimes written using the hat operator: + see :wikipedia:`Hat_operator#Cross_product`. It is only defined for vectors of length 3 or 7. For a vector `v` the cross product matrix `\hat{v}` is a matrix which satisfies `\hat{v} \cdot w = v \times w` and also `w \cdot \hat{v} = w \times v` for all vectors `w`. The basis vectors are assumed to be orthonormal. - .. _hat operator: :wikipedia:`Hat_operator#Cross_product` - OUTPUT: The cross product matrix of this vector. diff --git a/src/sage/modules/free_module_integer.py b/src/sage/modules/free_module_integer.py index 0ab13d2a1ce..038bdb092bd 100644 --- a/src/sage/modules/free_module_integer.py +++ b/src/sage/modules/free_module_integer.py @@ -783,6 +783,17 @@ def closest_vector(self, t): ALGORITHM: Uses the algorithm from [MV2010]_. + + TESTS: + + Check that the example from :trac:`29866` works:: + + sage: from sage.modules.free_module_integer import IntegerLattice + sage: M = matrix(ZZ, [[20957228, -4966110], [9411844, 19625639]]) + sage: L = IntegerLattice(M) + sage: u = vector([-423434678248195, -18882583298608161305227077482]) + sage: L.closest_vector(u) in L + True """ voronoi_cell = self.voronoi_cell() diff --git a/src/sage/modules/free_quadratic_module_integer_symmetric.py b/src/sage/modules/free_quadratic_module_integer_symmetric.py index 94a55520b3f..24766d3fdda 100644 --- a/src/sage/modules/free_quadratic_module_integer_symmetric.py +++ b/src/sage/modules/free_quadratic_module_integer_symmetric.py @@ -1419,6 +1419,7 @@ def twist(self, s, discard_basis=False): ambient = FreeQuadraticModule(self.base_ring(), n, inner_product_matrix) return FreeQuadraticModule_integer_symmetric(ambient=ambient, basis=self.basis(), inner_product_matrix=inner_product_matrix) + def local_modification(M, G, p, check=True): r""" Return a local modification of `M` that matches `G` at `p`. @@ -1466,7 +1467,6 @@ def local_modification(M, G, p, check=True): # notation d = G.inverse().denominator() - n = M.rank() scale = d.valuation(p) d = p**scale diff --git a/src/sage/modules/multi_filtered_vector_space.py b/src/sage/modules/multi_filtered_vector_space.py index e7777d1b92a..05973ccf7ed 100644 --- a/src/sage/modules/multi_filtered_vector_space.py +++ b/src/sage/modules/multi_filtered_vector_space.py @@ -38,7 +38,6 @@ # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from six import iteritems from sage.rings.all import QQ, ZZ, Integer from sage.rings.infinity import infinity, minus_infinity @@ -190,7 +189,7 @@ def change_ring(self, base_ring): return MultiFilteredVectorSpace(self.dimension(), base_ring=base_ring) filtrations = {} - for key, F in iteritems(self._filt): + for key, F in self._filt.items(): filtrations[key] = F.change_ring(base_ring) return MultiFilteredVectorSpace(filtrations, base_ring=base_ring) diff --git a/src/sage/modules/tensor_operations.py b/src/sage/modules/tensor_operations.py index 2840567f4b2..16eb8717e36 100644 --- a/src/sage/modules/tensor_operations.py +++ b/src/sage/modules/tensor_operations.py @@ -60,7 +60,6 @@ # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from six.moves import range from collections import defaultdict diff --git a/src/sage/modules/torsion_quadratic_module.py b/src/sage/modules/torsion_quadratic_module.py index eb377239191..2a2b77952a6 100644 --- a/src/sage/modules/torsion_quadratic_module.py +++ b/src/sage/modules/torsion_quadratic_module.py @@ -27,7 +27,7 @@ from sage.misc.cachefunc import cached_method from sage.rings.finite_rings.integer_mod import mod from sage.arith.misc import legendre_symbol - +from sage.structure.unique_representation import CachedRepresentation def TorsionQuadraticForm(q): r""" @@ -183,7 +183,7 @@ def quadratic_product(self): q = quadratic_product -class TorsionQuadraticModule(FGP_Module_class): +class TorsionQuadraticModule(FGP_Module_class, CachedRepresentation): r""" Finite quotients with a bilinear and a quadratic form. @@ -225,15 +225,20 @@ class TorsionQuadraticModule(FGP_Module_class): """ Element = TorsionQuadraticModuleElement - def __init__(self, V, W, gens=None, modulus=None, modulus_qf=None, check=True): + @staticmethod + def __classcall__(cls, V, W, gens=None, modulus=None, modulus_qf=None, check=True): r""" - Initialize ``self``. + Return a :class:``TorsionQuadraticModule``. + + This method does the preprocessing for :meth:``sage.structure.CachedRepresentation``. TESTS:: - sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule - sage: T = TorsionQuadraticModule(ZZ^3, 6*ZZ^3) - sage: TestSuite(T).run() + sage: q = matrix([1/2]) + sage: D1 = TorsionQuadraticForm(q) + sage: D2 = TorsionQuadraticForm(q) + sage: D1 is D2 + True """ if check: if V.rank() != W.rank(): @@ -246,36 +251,51 @@ def __init__(self, V, W, gens=None, modulus=None, modulus_qf=None, check=True): if gens is not None and V.span(gens) + W != V: raise ValueError("provided gens do not generate the quotient") - FGP_Module_class.__init__(self, V, W, check=check) - if gens is not None: - self._gens_user = tuple(self(v) for v in gens) - else: - # this is taken care of in the .gens method - # we do not want this at initialization - self._gens_user = None - # compute the modulus - this may be expensive if modulus is None or check: # The inner product of two elements `b(v1+W,v2+W)` # is defined `mod (V,W)` num = V.basis_matrix() * V.inner_product_matrix() * W.basis_matrix().T - self._modulus = gcd(num.list()) + max_modulus = gcd(num.list()) - if modulus is not None: - if check and self._modulus / modulus not in self.base_ring(): - raise ValueError("the modulus must divide (V, W)") - self._modulus = modulus + if modulus is None: + modulus = max_modulus + elif check and max_modulus / modulus not in V.base_ring(): + raise ValueError("the modulus must divide (V, W)") if modulus_qf is None or check: # The quadratic_product of an element `q(v+W)` is defined # `\mod 2(V,W) + ZZ\{ (w,w) | w in w\}` - norm = gcd(self.W().gram_matrix().diagonal()) - self._modulus_qf = gcd(norm, 2 * self._modulus) + norm = gcd(W.gram_matrix().diagonal()) + max_modulus_qf = gcd(norm, 2 * modulus) + + if modulus_qf is None: + modulus_qf = max_modulus_qf + elif check and max_modulus_qf / modulus_qf not in V.base_ring(): + raise ValueError("the modulus_qf must divide (V, W)") + return super(TorsionQuadraticModule, cls).__classcall__(cls, V, W, gens, modulus, modulus_qf) + + def __init__(self, V, W, gens, modulus, modulus_qf): + r""" + Initialize ``self``. + + TESTS:: + + sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule + sage: T = TorsionQuadraticModule(ZZ^3, 6*ZZ^3) + sage: TestSuite(T).run() + """ + + FGP_Module_class.__init__(self, V, W, check=True) + if gens is not None: + self._gens_user = tuple(self(g) for g in gens) + else: + # this is taken care of in the .gens method + # we do not want this at initialization + self._gens_user = None + self._modulus = modulus + self._modulus_qf = modulus_qf - if modulus_qf is not None: - if check and self._modulus_qf / modulus_qf not in self.base_ring(): - raise ValueError("the modulus_qf must divide (V, W)") - self._modulus_qf = modulus_qf def _repr_(self): r""" @@ -346,12 +366,12 @@ def all_submodules(self): r""" Return a list of all submodules of ``self``. - WARNING: + .. WARNING:: - This method creates all submodules in memory. The number of submodules - grows rapidly with the number of generators. For example consider a - vector space of dimension `n` over a finite field of prime order `p`. - The number of subspaces is (very) roughly `p^{(n^2-n)/2}`. + This method creates all submodules in memory. The number of submodules + grows rapidly with the number of generators. For example consider a + vector space of dimension `n` over a finite field of prime order `p`. + The number of subspaces is (very) roughly `p^{(n^2-n)/2}`. EXAMPLES:: @@ -794,6 +814,81 @@ def is_genus(self, signature_pair, even=True): return False return True + def orthogonal_group(self, gens=None, check=False): + r""" + Orthogonal group of the associated torsion quadratic form. + + .. WARNING:: + + This is can be smaller than the orthogonal group of the bilinear form. + + INPUT: + + - ``gens`` -- a list of generators, for instance square matrices, + something that acts on ``self``, or an automorphism + of the underlying abelian group + - ``check`` -- perform additional checks on the generators + + EXAMPLES: + + You can provide generators to obtain a subgroup of the full orthogonal group:: + + sage: D = TorsionQuadraticForm(matrix.identity(2)/2) + sage: f = matrix(2,[0,1,1,0]) + sage: D.orthogonal_group(gens=[f]).order() + 2 + + If no generators are given a slow brute force approach is used to calculate the full orthogonal group:: + + sage: D = TorsionQuadraticForm(matrix.identity(3)/2) + sage: OD = D.orthogonal_group() + sage: OD.order() + 6 + sage: fd = D.hom([D.1,D.0,D.2]) + sage: OD(fd) + [0 1 0] + [1 0 0] + [0 0 1] + + We compute the kernel of the action of the orthogonal group of `L` on the discriminant group. + + sage: L = IntegralLattice('A4') + sage: O = L.orthogonal_group() + sage: D = L.discriminant_group() + sage: Obar = D.orthogonal_group(O.gens()) + sage: O.order() + 240 + sage: Obar.order() + 2 + sage: phi = O.hom(Obar.gens()) + sage: phi.kernel().order() + 120 + """ + from sage.groups.fqf_orthogonal import FqfOrthogonalGroup,_isom_fqf + from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap + + # slow brute force implementation + flag = False + if gens is None: + try: + return self._orthogonal_group + except AttributeError: + flag = True + gens = _isom_fqf(self) + else: + # see if there is an action + try: + gens = [matrix(x*g for x in self.smith_form_gens()) for g in gens] + except TypeError: + pass + ambient = AbelianGroupGap(self.invariants()).aut() + # the ambient knows what to do with the generators + gens = tuple(ambient(g) for g in gens) + Oq = FqfOrthogonalGroup(ambient, gens, self, check=check) + if flag: + self._orthogonal_group = Oq + return Oq + def orthogonal_submodule_to(self, S): r""" Return the submodule orthogonal to ``S``. @@ -1112,7 +1207,7 @@ def submodule_with_gens(self, gens): [1/5 0] [ 0 1/5] """ - gens = [self(v) for v in gens] + gens = tuple(self(v) for v in gens) V = self.V().submodule([v.lift() for v in gens]) + self._W W = self.W() return TorsionQuadraticModule(V, W, gens=gens, modulus=self._modulus, diff --git a/src/sage/modules/vector_integer_dense.pyx b/src/sage/modules/vector_integer_dense.pyx index 5d418090d93..e1fea021020 100644 --- a/src/sage/modules/vector_integer_dense.pyx +++ b/src/sage/modules/vector_integer_dense.pyx @@ -58,7 +58,7 @@ from cysignals.signals cimport sig_on, sig_off from sage.structure.element cimport Element, ModuleElement, RingElement, Vector from sage.structure.richcmp cimport rich_to_bool -from sage.rings.integer cimport Integer +from sage.rings.integer cimport Integer, _Integer_from_mpz cimport sage.modules.free_module_element as free_module_element @@ -66,12 +66,6 @@ from .free_module_element import vector from sage.libs.gmp.mpz cimport * - -cdef inline _Integer_from_mpz(mpz_t e): - cdef Integer z = Integer.__new__(Integer) - mpz_set(z.value, e) - return z - cdef class Vector_integer_dense(free_module_element.FreeModuleElement): cdef bint is_dense_c(self): return 1 diff --git a/src/sage/modules/vector_integer_sparse.pxd b/src/sage/modules/vector_integer_sparse.pxd index 1cd823ee756..463b3b2c0e1 100644 --- a/src/sage/modules/vector_integer_sparse.pxd +++ b/src/sage/modules/vector_integer_sparse.pxd @@ -18,10 +18,11 @@ cdef void mpz_vector_clear(mpz_vector* v) cdef Py_ssize_t mpz_binary_search0(mpz_t* v, Py_ssize_t n, mpz_t x) cdef Py_ssize_t mpz_binary_search(mpz_t* v, Py_ssize_t n, mpz_t x, Py_ssize_t* ins) cdef int mpz_vector_get_entry(mpz_t ans, mpz_vector* v, Py_ssize_t n) except -1 +cdef bint mpz_vector_is_entry_zero_unsafe(mpz_vector* v, Py_ssize_t n) cdef object mpz_vector_to_list(mpz_vector* v) cdef int mpz_vector_set_entry(mpz_vector* v, Py_ssize_t n, mpz_t x) except -1 cdef int mpz_vector_set_entry_str(mpz_vector* v, Py_ssize_t n, char *x_str) except -1 cdef int add_mpz_vector_init(mpz_vector* sum, mpz_vector* v, mpz_vector* w, mpz_t multiple) except -1 cdef int mpz_vector_scale(mpz_vector* v, mpz_t scalar) except -1 cdef int mpz_vector_scalar_multiply(mpz_vector* v, mpz_vector* w, mpz_t scalar) except -1 -cdef int mpz_vector_cmp(mpz_vector* v, mpz_vector* w) \ No newline at end of file +cdef int mpz_vector_cmp(mpz_vector* v, mpz_vector* w) diff --git a/src/sage/modules/vector_integer_sparse.pyx b/src/sage/modules/vector_integer_sparse.pyx index 80a5b6efdee..7f94092a286 100644 --- a/src/sage/modules/vector_integer_sparse.pyx +++ b/src/sage/modules/vector_integer_sparse.pyx @@ -142,6 +142,15 @@ cdef int mpz_vector_get_entry(mpz_t ans, mpz_vector* v, Py_ssize_t n) except -1: mpz_set(ans, v.entries[m]) return 0 +cdef bint mpz_vector_is_entry_zero_unsafe(mpz_vector* v, Py_ssize_t n): + """ + Return if the ``n``-th entry of the sparse vector ``v`` is zero. + + This is meant for internal use only. If ``n`` is not valid, then + this might lead to a segfault. + """ + return binary_search0(v.positions, v.num_nonzero, n) == -1 + cdef object mpz_vector_to_list(mpz_vector* v): """ Returns a Python list of 2-tuples (i,x), where x=v[i] runs diff --git a/src/sage/modules/vector_mod2_dense.pyx b/src/sage/modules/vector_mod2_dense.pyx index 3c12d9e7c1a..549e33c43d1 100644 --- a/src/sage/modules/vector_mod2_dense.pyx +++ b/src/sage/modules/vector_mod2_dense.pyx @@ -1,3 +1,8 @@ +# distutils: libraries = M4RI_LIBRARIES GDLIB_LIBRARIES LIBPNG_LIBRARIES +# distutils: library_dirs = M4RI_LIBDIR GDLIB_LIBDIR LIBPNG_LIBDIR +# distutils: include_dirs = M4RI_INCDIR GDLIB_INCDIR LIBPNG_INCDIR +# distutils: extra_compile_args = M4RI_CFLAGS + """ Vectors with elements in GF(2) @@ -183,7 +188,7 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): sage: (GF(2)**5)(1) Traceback (most recent call last): ... - TypeError: can't initialize vector from nonzero non-list + TypeError: can...t initialize vector from nonzero non-list sage: (GF(2)**0).zero_vector() () """ @@ -343,6 +348,7 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): cpdef _dot_product_(self, Vector right): """ EXAMPLES:: + sage: VS = VectorSpace(GF(2),3) sage: v = VS([1,1,1]); w = VS([0,0,0]) sage: v * w, w * v #indirect doctest diff --git a/src/sage/modules/vector_modn_sparse.pxd b/src/sage/modules/vector_modn_sparse.pxd index 2e4e9f6ab41..41d2dd1dd43 100644 --- a/src/sage/modules/vector_modn_sparse.pxd +++ b/src/sage/modules/vector_modn_sparse.pxd @@ -13,7 +13,8 @@ cdef void clear_c_vector_modint(c_vector_modint* v) cdef Py_ssize_t binary_search0_modn(Py_ssize_t* v, Py_ssize_t n, int_fast64_t x) cdef Py_ssize_t binary_search_modn(Py_ssize_t* v, Py_ssize_t n, int_fast64_t x, Py_ssize_t* ins) cdef int_fast64_t get_entry(c_vector_modint* v, Py_ssize_t n) except -1 +cdef bint is_entry_zero_unsafe(c_vector_modint* v, Py_ssize_t n) cdef object to_list(c_vector_modint* v) cdef int set_entry(c_vector_modint* v, Py_ssize_t n, int_fast64_t x) except -1 cdef int add_c_vector_modint_init(c_vector_modint* sum, c_vector_modint* v, c_vector_modint* w, int multiple) except -1 -cdef int scale_c_vector_modint(c_vector_modint* v, int_fast64_t scalar) except -1 \ No newline at end of file +cdef int scale_c_vector_modint(c_vector_modint* v, int_fast64_t scalar) except -1 diff --git a/src/sage/modules/vector_modn_sparse.pyx b/src/sage/modules/vector_modn_sparse.pyx index 3a166786bfe..5258c9a141a 100644 --- a/src/sage/modules/vector_modn_sparse.pyx +++ b/src/sage/modules/vector_modn_sparse.pyx @@ -123,6 +123,14 @@ cdef int_fast64_t get_entry(c_vector_modint* v, Py_ssize_t n) except -1: return 0 return v.entries[m] +cdef bint is_entry_zero_unsafe(c_vector_modint* v, Py_ssize_t n): + """ + Return if the ``n``-th entry of the sparse vector ``v`` is zero. + + This is meant for internal use only. If ``n`` is not valid, then + this might lead to a segfault. + """ + return binary_search0_modn(v.positions, v.num_nonzero, n) == -1 cdef object to_list(c_vector_modint* v): """ diff --git a/src/sage/modules/vector_rational_sparse.pxd b/src/sage/modules/vector_rational_sparse.pxd index fcd8115c29f..0888a8700fe 100644 --- a/src/sage/modules/vector_rational_sparse.pxd +++ b/src/sage/modules/vector_rational_sparse.pxd @@ -19,6 +19,7 @@ cdef void mpq_vector_clear(mpq_vector* v) cdef Py_ssize_t mpq_binary_search0(mpq_t* v, Py_ssize_t n, mpq_t x) cdef Py_ssize_t mpq_binary_search(mpq_t* v, Py_ssize_t n, mpq_t x, Py_ssize_t* ins) cdef int mpq_vector_get_entry(mpq_t ans, mpq_vector* v, Py_ssize_t n) except -1 +cdef bint mpq_vector_is_entry_zero_unsafe(mpq_vector* v, Py_ssize_t n) cdef object mpq_vector_to_list(mpq_vector* v) cdef int mpq_vector_set_entry(mpq_vector* v, Py_ssize_t n, mpq_t x) except -1 cdef int mpq_vector_set_entry_str(mpq_vector* v, Py_ssize_t n, char *x_str) except -1 diff --git a/src/sage/modules/vector_rational_sparse.pyx b/src/sage/modules/vector_rational_sparse.pyx index fec595aec78..8b204b40e4f 100644 --- a/src/sage/modules/vector_rational_sparse.pyx +++ b/src/sage/modules/vector_rational_sparse.pyx @@ -149,6 +149,15 @@ cdef int mpq_vector_get_entry(mpq_t ans, mpq_vector* v, Py_ssize_t n) except -1: mpq_set(ans, v.entries[m]) return 0 +cdef bint mpq_vector_is_entry_zero_unsafe(mpq_vector* v, Py_ssize_t n): + """ + Return if the ``n``-th entry of the sparse vector ``v`` is zero. + + This is meant for internal use only. If ``n`` is not valid, then + this might lead to a segfault. + """ + return binary_search0(v.positions, v.num_nonzero, n) == -1 + cdef object mpq_vector_to_list(mpq_vector* v): """ Returns a Python list of 2-tuples (i,x), where x=v[i] runs diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index 95bfa10e3d9..956a6dca050 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -21,11 +21,11 @@ from sage.structure.element cimport parent from sage.structure.richcmp cimport richcmp, rich_to_bool from cpython.object cimport Py_NE, Py_EQ -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute -from sage.typeset.ascii_art import AsciiArt, empty_ascii_art -from sage.typeset.unicode_art import UnicodeArt, empty_unicode_art +from sage.typeset.ascii_art import AsciiArt, empty_ascii_art, ascii_art +from sage.typeset.unicode_art import UnicodeArt, empty_unicode_art, unicode_art from sage.categories.all import Category, Sets, ModulesWithBasis from sage.data_structures.blas_dict cimport add, negate, scal, axpy @@ -307,7 +307,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): sage: ascii_art(M.zero()) 0 """ - from sage.misc.misc import coeff_repr + from sage.misc.repr import coeff_repr terms = self._sorted_items_for_printing() scalar_mult = self._parent._print_options['scalar_mult'] repr_monomial = self._parent._ascii_art_term @@ -316,7 +316,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): if repr_monomial is None: repr_monomial = str - s = empty_ascii_art # "" + chunks = [] first = True if scalar_mult is None: @@ -345,8 +345,12 @@ cdef class IndexedFreeModuleElement(ModuleElement): break_points = [2] else: coeff = "%s"%coeff - s += AsciiArt([coeff], break_points) + b + if coeff: + chunks.append(AsciiArt([coeff], break_points)) + if b._l: + chunks.append(b) first = False + s = ascii_art(*chunks) if first: return AsciiArt(["0"]) elif s == empty_ascii_art: @@ -367,12 +371,12 @@ cdef class IndexedFreeModuleElement(ModuleElement): ├┤ └┘ └┘ └┴┘ └┘ - The following test failed before :trac:`26850` :: + The following test failed before :trac:`26850`:: sage: unicode_art([M.zero()]) # indirect doctest [ 0 ] """ - from sage.misc.misc import coeff_repr + from sage.misc.repr import coeff_repr terms = self._sorted_items_for_printing() scalar_mult = self._parent._print_options['scalar_mult'] repr_monomial = self._parent._unicode_art_term @@ -381,7 +385,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): if repr_monomial is None: repr_monomial = str - s = empty_unicode_art # "" + chunks = [] first = True if scalar_mult is None: @@ -410,8 +414,12 @@ cdef class IndexedFreeModuleElement(ModuleElement): break_points = [2] else: coeff = "%s" % coeff - s += UnicodeArt([coeff], break_points) + b + if coeff: + chunks.append(UnicodeArt([coeff], break_points)) + if b._l: + chunks.append(b) first = False + s = unicode_art(*chunks) if first: return UnicodeArt(["0"]) elif s == empty_unicode_art: @@ -656,13 +664,14 @@ cdef class IndexedFreeModuleElement(ModuleElement): return self.base_ring().zero() return res - def _vector_(self, new_base_ring=None): + def _vector_(self, new_base_ring=None, order=None): """ Returns ``self`` as a dense vector INPUT: - ``new_base_ring`` -- a ring (default: ``None``) + - ``order`` -- (optional) an ordering of the support of ``self`` OUTPUT: a dense :func:`FreeModule` vector @@ -727,8 +736,10 @@ cdef class IndexedFreeModuleElement(ModuleElement): dense_free_module = self._parent._dense_free_module(new_base_ring) d = self._monomial_coefficients zero = dense_free_module.base_ring().zero() + if order is None: + order = self._parent.get_order() return dense_free_module.element_class(dense_free_module, - [d.get(m, zero) for m in self._parent.get_order()], + [d.get(m, zero) for m in order], coerce=True, copy=False) to_vector = _vector_ @@ -875,20 +886,6 @@ cdef class IndexedFreeModuleElement(ModuleElement): x_inv = B(x) ** -1 return type(self)(F, scal(x_inv, D)) - def __div__(left, right): - """ - Forward old-style division to true division. - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, [1,2,3]) - sage: x = F._from_dict({1:2, 2:3}) - sage: x/2 - B[1] + 3/2*B[2] - """ - return left / right - - def _unpickle_element(C, d): """ Unpickle an element in ``C`` given by ``d``. diff --git a/src/sage/modules/with_basis/morphism.py b/src/sage/modules/with_basis/morphism.py index 27f6e732a23..435fd68ee3c 100644 --- a/src/sage/modules/with_basis/morphism.py +++ b/src/sage/modules/with_basis/morphism.py @@ -104,11 +104,10 @@ # http://www.gnu.org/licenses/ #****************************************************************************** from __future__ import print_function -from six import iteritems from sage.categories.fields import Fields from sage.categories.modules import Modules -from sage.misc.misc import attrcall +from sage.misc.call import attrcall # The identity function would deserve a more canonical location from sage.misc.c3_controlled import identity from sage.categories.commutative_additive_semigroups import CommutativeAdditiveSemigroups @@ -178,17 +177,19 @@ def __init__(self, domain, codomain=None, category=None, affine=False): raise ValueError("codomain(=%s) needs to have a base_ring attribute"%(codomain)) # codomain should be a module over base_ring # The natural test would be ``codomains in Modules(base_ring)`` - # But this is not properly implemented yet: + # But this is not properly implemented yet:: + # # sage: CC in Modules(QQ) # False # sage: QQ in Modules(QQ) # False # sage: CC[x] in Modules(QQ) # False + # The test below is a bit more restrictive if (not codomain.base_ring().has_coerce_map_from(base_ring)) \ and (not codomain.has_coerce_map_from(base_ring)): - raise ValueError("codomain(=%s) should be a module over the base ring of the domain(=%s)"%(codomain, domain)) + raise ValueError("codomain(=%s) should be a module over the base ring of the domain(=%s)" % (codomain, domain)) if affine: # We don't yet have a category whose morphisms are affine morphisms @@ -393,9 +394,12 @@ def __call__(self, *args): mc = x.monomial_coefficients(copy=False) if self._is_module_with_basis_over_same_base_ring: - return self.codomain().linear_combination( (self._on_basis(*(before+(index,)+after)), coeff ) for (index, coeff) in iteritems(mc) ) + return self.codomain().linear_combination( + (self._on_basis(*(before+(index,)+after)), coeff ) + for (index, coeff) in mc.items()) else: - return sum(( coeff * self._on_basis(*(before+(index,)+after)) for (index, coeff) in iteritems(mc) ), self._zero) + return sum((coeff * self._on_basis(*(before+(index,)+after)) + for (index, coeff) in mc.items()), self._zero) # As per the specs of Map, we should in fact implement _call_. # However we currently need to abuse Map.__call__ (which strict @@ -1533,7 +1537,6 @@ def pointwise_inverse_function(f): sage: from sage.modules.with_basis.morphism import pointwise_inverse_function sage: def f(x): return x - ....: sage: g = pointwise_inverse_function(f) sage: g(1), g(2), g(3) (1, 1/2, 1/3) 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/modules/with_basis/subquotient.py b/src/sage/modules/with_basis/subquotient.py index 24032916ddc..5b5c52b2752 100644 --- a/src/sage/modules/with_basis/subquotient.py +++ b/src/sage/modules/with_basis/subquotient.py @@ -8,6 +8,7 @@ # http://www.gnu.org/licenses/ #****************************************************************************** +from sage.misc.cachefunc import cached_method from sage.sets.family import Family from sage.combinat.free_module import CombinatorialFreeModule from sage.misc.lazy_attribute import lazy_attribute @@ -173,6 +174,9 @@ class SubmoduleWithBasis(CombinatorialFreeModule): :class:`module with basis ` `V`, or data that can be converted into such a family + - ``support_order`` -- an ordering of the support of ``basis`` + expressed in ``ambient`` given as a list + - ``unitriangular`` -- if the lift morphism is unitriangular - ``ambient`` -- the ambient space `V` @@ -192,8 +196,8 @@ class SubmoduleWithBasis(CombinatorialFreeModule): """ @staticmethod - def __classcall_private__(cls, basis, ambient=None, unitriangular=False, - category=None, *args, **opts): + def __classcall_private__(cls, basis, support_order, ambient=None, + unitriangular=False, category=None, *args, **opts): r""" Normalize the input. @@ -201,8 +205,8 @@ def __classcall_private__(cls, basis, ambient=None, unitriangular=False, sage: from sage.modules.with_basis.subquotient import SubmoduleWithBasis sage: X = CombinatorialFreeModule(QQ, range(3)); x = X.basis() - sage: Y1 = SubmoduleWithBasis((x[0]-x[1], x[1]-x[2]), X) - sage: Y2 = SubmoduleWithBasis([x[0]-x[1], x[1]-x[2]], X) + sage: Y1 = SubmoduleWithBasis((x[0]-x[1], x[1]-x[2]), [0,1,2], X) + sage: Y2 = SubmoduleWithBasis([x[0]-x[1], x[1]-x[2]], (0,1,2), X) sage: Y1 is Y2 True """ @@ -211,10 +215,12 @@ def __classcall_private__(cls, basis, ambient=None, unitriangular=False, ambient = basis.an_element().parent() default_category = ModulesWithBasis(ambient.category().base_ring()).Subobjects() category = default_category.or_subcategory(category, join=True) - return super(SubmoduleWithBasis, cls).__classcall__( - cls, basis, ambient, unitriangular, category, *args, **opts) + return super(SubmoduleWithBasis, cls).__classcall__(cls, + basis, tuple(support_order), ambient, unitriangular, category, + *args, **opts) - def __init__(self, basis, ambient, unitriangular, category): + def __init__(self, basis, support_order, ambient, unitriangular, category, + *args, **opts): r""" Initialization. @@ -223,7 +229,7 @@ def __init__(self, basis, ambient, unitriangular, category): sage: from sage.modules.with_basis.subquotient import SubmoduleWithBasis sage: X = CombinatorialFreeModule(QQ, range(3), prefix="x"); x = X.basis() sage: ybas = (x[0]-x[1], x[1]-x[2]) - sage: Y = SubmoduleWithBasis(ybas, X) + sage: Y = SubmoduleWithBasis(ybas, [0, 1, 2], X) sage: Y.print_options(prefix='y') sage: Y.basis().list() [y[0], y[1]] @@ -233,11 +239,14 @@ def __init__(self, basis, ambient, unitriangular, category): """ ring = ambient.base_ring() CombinatorialFreeModule.__init__(self, ring, basis.keys(), - category=category.Subobjects()) + category=category.Subobjects(), + *args, **opts) self._ambient = ambient self._basis = basis self._unitriangular = unitriangular + self._support_order = support_order self.lift_on_basis = self._basis.__getitem__ + self.lift.register_as_coercion() def ambient(self): """ @@ -252,6 +261,21 @@ def ambient(self): """ return self._ambient + @cached_method + def _support_key(self, x): + """ + Return a key corresponding to the index ``x`` for ordering the + basis of ``self``. + + EXAMPLES:: + + sage: A = GradedModulesWithBasis(ZZ).example() + sage: M = A.submodule(list(A.basis(3)), already_echelonized=True) + sage: [M._support_key(x) for x in M._support_order] + [0, 1, 2] + """ + return self._support_order.index(x) + @lazy_attribute def lift(self): r""" @@ -274,7 +298,7 @@ def lift(self): codomain=self.ambient(), triangular="lower", unitriangular=self._unitriangular, - key=self.ambient().get_order_key(), + key=self._support_key, inverse_on_support="compute") @lazy_attribute @@ -360,3 +384,4 @@ def is_submodule(self, other): except ValueError: return False return True + diff --git a/src/sage/monoids/automatic_semigroup.py b/src/sage/monoids/automatic_semigroup.py index a85b0a8cc43..73286e2d71a 100644 --- a/src/sage/monoids/automatic_semigroup.py +++ b/src/sage/monoids/automatic_semigroup.py @@ -171,7 +171,6 @@ class AutomaticSemigroup(UniqueRepresentation, Parent): ....: z=x*y ....: z.set_immutable() ....: return z - ....: sage: Mon = AutomaticSemigroup([M1,M2], mul=prod_m, category=Monoids().Finite().Subobjects()) sage: Mon.cardinality() 24 @@ -477,7 +476,6 @@ def ambient(self): ....: z=x*y ....: z.set_immutable() ....: return z - ....: sage: Mon = AutomaticSemigroup([M1,M2], mul=prod_m) sage: Mon.ambient() Full MatrixSpace of 3 by 3 dense matrices over Integer Ring diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py index 6674037fb15..8cae763d82e 100644 --- a/src/sage/monoids/free_abelian_monoid.py +++ b/src/sage/monoids/free_abelian_monoid.py @@ -54,7 +54,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import -from six import integer_types from sage.misc.cachefunc import cached_method from sage.structure.category_object import normalize_names @@ -193,7 +192,7 @@ def __init__(self, n, names): sage: F = FreeAbelianMonoid(6,'b') sage: TestSuite(F).run() """ - if not isinstance(n, integer_types + (Integer,)): + if not isinstance(n, (int, Integer)): raise TypeError("n (=%s) must be an integer"%n) if n < 0: raise ValueError("n (=%s) must be nonnegative"%n) diff --git a/src/sage/monoids/free_abelian_monoid_element.pxd b/src/sage/monoids/free_abelian_monoid_element.pxd new file mode 100644 index 00000000000..74dd0f7b5ed --- /dev/null +++ b/src/sage/monoids/free_abelian_monoid_element.pxd @@ -0,0 +1,16 @@ +from sage.structure.element cimport MonoidElement +from sage.libs.gmp.types cimport mpz_t +from sage.structure.parent cimport Parent + +cdef class FreeAbelianMonoidElement(MonoidElement): + cdef mpz_t *_element_vector + cdef Py_ssize_t _n + + cdef int _init(self, Py_ssize_t n, Parent parent) except -1 + + cdef inline FreeAbelianMonoidElement _new_c(self): + cdef type t = type(self) + cdef FreeAbelianMonoidElement x = (t.__new__(t)) + x._init(self._n, self._parent) + return x + diff --git a/src/sage/monoids/free_abelian_monoid_element.py b/src/sage/monoids/free_abelian_monoid_element.py deleted file mode 100644 index 64168e86254..00000000000 --- a/src/sage/monoids/free_abelian_monoid_element.py +++ /dev/null @@ -1,221 +0,0 @@ -""" -Abelian Monoid Elements - -AUTHORS: - -- David Kohel (2005-09) - -EXAMPLES: - -Recall the example from abelian monoids:: - - sage: F = FreeAbelianMonoid(5,names = list("abcde")) - sage: (a,b,c,d,e) = F.gens() - sage: a*b^2*e*d - a*b^2*d*e - sage: x = b^2*e*d*a^7 - sage: x - a^7*b^2*d*e - sage: x.list() - [7, 2, 0, 1, 1] - -The list is a copy, so changing the list does not change the element:: - - sage: x.list()[0] = 0 - sage: x - a^7*b^2*d*e -""" - -#***************************************************************************** -# Copyright (C) 2006 William Stein -# Copyright (C) 2005 David Kohel -# -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** -from six import integer_types - -from sage.structure.richcmp import richcmp -from sage.rings.integer import Integer -from sage.structure.element import MonoidElement - -def is_FreeAbelianMonoidElement(x): - r""" - Queries whether ``x`` is an object of type ``FreeAbelianMonoidElement``. - - INPUT: - - - ``x`` -- an object. - - OUTPUT: - - - ``True`` if ``x`` is an object of type ``FreeAbelianMonoidElement``; - ``False`` otherwise. - """ - return isinstance(x, FreeAbelianMonoidElement) - -class FreeAbelianMonoidElement(MonoidElement): - def __init__(self, F, x): - """ - Create the element x of the FreeAbelianMonoid F. - - EXAMPLES:: - - sage: F = FreeAbelianMonoid(5, 'abcde') - sage: F - Free abelian monoid on 5 generators (a, b, c, d, e) - sage: F(1) - 1 - sage: a, b, c, d, e = F.gens() - sage: a^2 * b^3 * a^2 * b^4 - a^4*b^7 - sage: F = FreeAbelianMonoid(5, 'abcde') - sage: a, b, c, d, e = F.gens() - sage: a in F - True - sage: a*b in F - True - """ - MonoidElement.__init__(self, F) - n = F.ngens() - if isinstance(x, integer_types + (Integer,)) and x == 1: - self._element_vector = tuple([0]*n) - elif isinstance(x, (list, tuple)): - if len(x) != n: - raise IndexError("argument length (= %s) must be %s"%(len(x), n)) - self._element_vector = tuple(x) - else: - raise TypeError("argument x (= %s) is of wrong type"%x) - - def _repr_(self): - """ - Return a string representation of ``self``. - - EXAMPLES:: - - sage: F = FreeAbelianMonoid(5, 'abcde') - sage: F(1) - 1 - sage: a, b, c, d, e = F.gens() - sage: a^2 * b^3 * a^2 * b^4 - a^4*b^7 - """ - s = "" - A = self.parent() - n = A.ngens() - x = A.variable_names() - v = self._element_vector - for i in range(n): - if v[i] == 0: - continue - elif v[i] == 1: - if len(s) > 0: s += "*" - s += "%s"%x[i] - else: - if len(s) > 0: s += "*" - s += "%s^%s"%(x[i],v[i]) - if not s: - s = "1" - return s - - def _richcmp_(self, other, op): - """ - Rich comparison. - - EXAMPLES:: - - sage: F = FreeAbelianMonoid(5, 'abcde') - sage: F(1) - 1 - sage: a, b, c, d, e = F.gens() - sage: x = a^2 * b^3 - sage: F(1) < x - True - sage: x > b - True - sage: x <= a^4 - True - sage: x != a*b - True - sage: a*b == b*a - True - sage: x > a^3*b^2 - False - """ - return richcmp(self._element_vector, other._element_vector, op) - - def __mul__(self, y): - if not isinstance(y, FreeAbelianMonoidElement): - raise TypeError("argument y (= %s) is of wrong type"%y) - M = self.parent() - z = M.element_class(M, 1) - xelt = self._element_vector - yelt = y._element_vector - z._element_vector = tuple([xelt[i]+yelt[i] for i in range(len(xelt))]) - return z - - def __pow__(self, n): - """ - Raises self to the power of `n`. - - AUTHORS: - - - Tom Boothby (2007-08): Replaced O(log n) time, O(n) space - algorithm with O(1) time and space"algorithm". - - EXAMPLES:: - - sage: F = FreeAbelianMonoid(5,names = list("abcde")) - sage: (a,b,c,d,e) = F.gens() - sage: x = a*b^2*e*d; x - a*b^2*d*e - sage: x^3 - a^3*b^6*d^3*e^3 - sage: x^0 - 1 - """ - if not isinstance(n, integer_types + (Integer,)): - raise TypeError("argument n (= %s) must be an integer"%(n,)) - if n < 0: - raise IndexError("argument n (= %s) must be positive"%n) - elif n == 1: - return self - M = self.parent() - z = M.element_class(M, 1) - if n == 0: - return z - else: - z._element_vector = tuple([i*n for i in self._element_vector]) - return z - - def __hash__(self): - """ - Return the hash of ``self``. - - EXAMPLES:: - - sage: F = FreeAbelianMonoid(5,names = list("abcde")) - sage: (a,b,c,d,e) = F.gens() - sage: x = a*b^2*e*d - sage: hash(x) == hash(x) - True - """ - return hash(self._element_vector) - - def list(self): - """ - Return (a reference to) the underlying list used to represent this - element. If this is a monoid in an abelian monoid on `n` - generators, then this is a list of nonnegative integers of length - `n`. - - EXAMPLES:: - - sage: F = FreeAbelianMonoid(5, 'abcde') - sage: (a, b, c, d, e) = F.gens() - sage: a.list() - [1, 0, 0, 0, 0] - """ - return list(self._element_vector) - - diff --git a/src/sage/monoids/free_abelian_monoid_element.pyx b/src/sage/monoids/free_abelian_monoid_element.pyx new file mode 100644 index 00000000000..232deeadac6 --- /dev/null +++ b/src/sage/monoids/free_abelian_monoid_element.pyx @@ -0,0 +1,391 @@ +""" +Abelian Monoid Elements + +AUTHORS: + +- David Kohel (2005-09) + +EXAMPLES: + +Recall the example from abelian monoids:: + + sage: F = FreeAbelianMonoid(5,names = list("abcde")) + sage: (a,b,c,d,e) = F.gens() + sage: a*b^2*e*d + a*b^2*d*e + sage: x = b^2*e*d*a^7 + sage: x + a^7*b^2*d*e + sage: x.list() + [7, 2, 0, 1, 1] + +The list is a copy, so changing the list does not change the element:: + + sage: x.list()[0] = 0 + sage: x + a^7*b^2*d*e +""" + +#***************************************************************************** +# Copyright (C) 2006 William Stein +# Copyright (C) 2005 David Kohel +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from cysignals.memory cimport check_allocarray, sig_free +from cysignals.signals cimport sig_on, sig_off +from sage.structure.richcmp cimport rich_to_bool +from sage.rings.integer cimport Integer, _Integer_from_mpz +from sage.libs.gmp.mpz cimport * + +def is_FreeAbelianMonoidElement(x): + r""" + Queries whether ``x`` is an object of type ``FreeAbelianMonoidElement``. + + INPUT: + + - ``x`` -- an object. + + OUTPUT: + + - ``True`` if ``x`` is an object of type ``FreeAbelianMonoidElement``; + ``False`` otherwise. + """ + return isinstance(x, FreeAbelianMonoidElement) + +cdef class FreeAbelianMonoidElement(MonoidElement): + cdef int _init(self, Py_ssize_t n, Parent parent) except -1: + """ + Initialize the C data structures in this vector. After calling + this, ``self`` is the identity element represented as a zero vector + of degree ``n`` with parent ``parent``. + + Only if you call ``__new__`` without a ``parent`` argument, it + is needed to call this function manually. The only reason to do + that is for efficiency: calling ``__new__`` without any + additional arguments besides the type and then calling ``_init`` + is (slightly) more efficient than calling ``__new__`` with a + ``parent`` argument. + """ + # Assign variables only when the array is fully initialized + cdef mpz_t* entries = check_allocarray(n, sizeof(mpz_t)) + cdef Py_ssize_t i + sig_on() + for i in range(n): + mpz_init(entries[i]) + sig_off() + self._element_vector = entries + self._n = n + self._parent = parent + + def __cinit__(self, parent=None, x=None): + """ + C level initialization of ``self``. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(5, 'abcde') + sage: F(1) + 1 + """ + self._element_vector = NULL + if parent is None: + self._n = 0 + return + self._init(parent.ngens(), parent) + + def __init__(self, parent, x): + r""" + Create the element ``x`` of the FreeAbelianMonoid ``parent``. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(5, 'abcde') + sage: F + Free abelian monoid on 5 generators (a, b, c, d, e) + sage: F(1) + 1 + sage: F(2) + Traceback (most recent call last): + ... + TypeError: argument x (= 2) is of the wrong type + sage: F(int(1)) + 1 + sage: a, b, c, d, e = F.gens() + sage: a^2 * b^3 * a^2 * b^4 + a^4*b^7 + sage: F = FreeAbelianMonoid(5, 'abcde') + sage: a, b, c, d, e = F.gens() + sage: a in F + True + sage: a*b in F + True + """ + cdef Py_ssize_t i + cdef Integer z + + if isinstance(x, int): + if ( x) == 1: + return # the _element_vector is already set to the 0 vector + elif isinstance(x, Integer): + if mpz_cmp_si(( x).value, 1) == 0: + return # the _element_vector is already set to the 0 vector + elif isinstance(x, (list, tuple)): + if len(x) != self._n: + raise IndexError("argument length (= %s) must be %s" % (len(x), self._n)) + for i in range(self._n): + z = Integer(x[i]) + mpz_set(self._element_vector[i], z.value) + return + raise TypeError("argument x (= %s) is of the wrong type" % x) + + def __dealloc__(self): + """ + Deallocate ``self``. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(5, 'abcde') + sage: a, b, c, d, e = F.gens() + sage: x = a^2 * b^3 + sage: del x + """ + cdef Py_ssize_t i + if self._element_vector: + for i in range(self._n): + mpz_clear(self._element_vector[i]) + sig_free(self._element_vector) + + def __copy__(self): + """ + Return a copy of ``self``. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(5, 'abcde') + sage: a, b, c, d, e = F.gens() + sage: x = a^2 * b^3 + sage: copy(x) == x + True + sage: copy(x) is x + False + """ + cdef FreeAbelianMonoidElement y + y = self._new_c() + cdef Py_ssize_t i + for i in range(self._n): + mpz_set(y._element_vector[i], self._element_vector[i]) + return y + + def __reduce__(self): + """ + Used in pickling. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(5, 'abcde') + sage: a, b, c, d, e = F.gens() + sage: x = a^2 * b^3 + sage: loads(dumps(x)) == x + True + """ + return (self._parent, (self.list(),)) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(5, 'abcde') + sage: F(1) + 1 + sage: a, b, c, d, e = F.gens() + sage: a^2 * b^3 * a^2 * b^4 + a^4*b^7 + """ + cdef str s = "" + A = self._parent + cdef tuple x = A.variable_names() + cdef mpz_t *v = self._element_vector + cdef Integer val + for i in range(self._n): + val = _Integer_from_mpz(v[i]) + if val == 0: + continue + elif val == 1: + if s: + s += "*" + s += "%s" % x[i] + else: + if s: + s += "*" + s += "%s^%s" % (x[i], val) + if not s: + s = "1" + return s + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(2, 'alpha,beta') + sage: latex(F(1)) + 1 + sage: a, b = F.gens() + sage: latex(a^2 * b^3 * a^2 * b^4) + \alpha^{4} \beta^{7} + """ + cdef str s = "" + A = self._parent + cdef list x = A.latex_variable_names() + cdef mpz_t *v = self._element_vector + cdef Integer val + for i in range(self._n): + val = _Integer_from_mpz(v[i]) + if val == 0: + continue + elif val == 1: + if s: + s += " " + s += "%s" % x[i] + else: + if s: + s += " " + s += "%s^{%s}" % (x[i], val) + if not s: + s = "1" + return s + + cpdef _richcmp_(left, right, int op): + """ + Rich comparison. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(5, 'abcde') + sage: F(1) + 1 + sage: a, b, c, d, e = F.gens() + sage: x = a^2 * b^3 + sage: F(1) < x + True + sage: x > b + True + sage: x <= a^4 + True + sage: x != a*b + True + sage: a*b == b*a + True + sage: x > a^3*b^2 + False + """ + cdef Py_ssize_t i + cdef int c + for i in range(left._n): + c = mpz_cmp(left._element_vector[i], (right)._element_vector[i]) + if c < 0: + return rich_to_bool(op, -1) + elif c > 0: + return rich_to_bool(op, 1) + return rich_to_bool(op, 0) + + def __mul__(self, y): + """ + Multiply ``self`` with ``y``. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(5, 'abcde') + sage: a, b, c, d, e = F.gens() + sage: b * a^2 * b^3 + a^2*b^4 + """ + if not isinstance(y, FreeAbelianMonoidElement): + raise TypeError("argument y (= %s) is of wrong type"%y) + cdef FreeAbelianMonoidElement s, z, r + s = self + r = y + z = s._new_c() + cdef Py_ssize_t i + for i in range(s._n): + mpz_add(z._element_vector[i], s._element_vector[i], r._element_vector[i]) + return z + + def __pow__(self, n, modulus): + """ + Raises self to the power of ``n``. + + AUTHORS: + + - Tom Boothby (2007-08): Replaced O(log n) time, O(n) space + algorithm with O(1) time and space "algorithm". + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(5,names = list("abcde")) + sage: (a,b,c,d,e) = F.gens() + sage: x = a*b^2*e*d; x + a*b^2*d*e + sage: x^3 + a^3*b^6*d^3*e^3 + sage: x^0 + 1 + """ + if modulus is not None: + raise NotImplementedError("modulus for exponents not implemented") + cdef Integer val + if isinstance(n, int): + val = Integer(n) + else: + if not isinstance(n, Integer): + raise TypeError("argument n (= %s) must be an integer" % (n,)) + val = n + if val < 0: + raise IndexError("argument n (= %s) must be positive" % val) + elif val == 1: + return self + cdef FreeAbelianMonoidElement s, z + s = self + z = s._new_c() + cdef Py_ssize_t i + for i in range(s._n): + mpz_mul(z._element_vector[i], s._element_vector[i], val.value) + return z + + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(5,names = list("abcde")) + sage: (a,b,c,d,e) = F.gens() + sage: x = a*b^2*e*d + sage: hash(x) == hash(x) + True + """ + return hash(tuple(self.list())) + + def list(self): + r""" + Return the underlying list used to represent ``self``. + + If this is a monoid in an abelian monoid on `n` generators, + then this is a list of nonnegative integers of length `n`. + + EXAMPLES:: + + sage: F = FreeAbelianMonoid(5, 'abcde') + sage: (a, b, c, d, e) = F.gens() + sage: a.list() + [1, 0, 0, 0, 0] + """ + cdef Py_ssize_t i + return [_Integer_from_mpz(self._element_vector[i]) for i in range(self._n)] + diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index db6bcdb5d34..609c1631a71 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -24,7 +24,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import -from six import integer_types from sage.rings.integer import Integer from sage.structure.category_object import normalize_names @@ -190,7 +189,7 @@ def __init__(self, n, names=None): sage: F. = FreeMonoid() sage: TestSuite(F).run() """ - if not isinstance(n, integer_types + (Integer,)): + if not isinstance(n, (int, Integer)): raise TypeError("n (=%s) must be an integer" % n) if n < 0: raise ValueError("n (=%s) must be nonnegative" % n) @@ -234,13 +233,18 @@ def _element_constructor_(self, x, check=True): a^2*b^2*c*a*b*a*c sage: F(Word([])) 1 + + TESTS:: + + sage: F(F(w), check=False) + a^2*b^2*c*a*b*a*c """ # There should really be some careful type checking here... if isinstance(x, FreeMonoidElement) and x.parent() is self: return x if isinstance(x, FreeMonoidElement) and x.parent() == self: return self.element_class(self, x._element_list, check) - if isinstance(x, integer_types + (Integer,)) and x == 1: + if isinstance(x, (int, Integer)) and x == 1: return self.element_class(self, x, check) if isinstance(x, FiniteWord_class): d = self.gens_dict() diff --git a/src/sage/monoids/free_monoid_element.py b/src/sage/monoids/free_monoid_element.py index add4a903555..549f9381e94 100644 --- a/src/sage/monoids/free_monoid_element.py +++ b/src/sage/monoids/free_monoid_element.py @@ -23,7 +23,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six import iteritems, integer_types from sage.rings.integer import Integer from sage.structure.element import MonoidElement @@ -58,7 +57,7 @@ def __init__(self, F, x, check=True): This should typically be called by a FreeMonoid. """ MonoidElement.__init__(self, F) - if isinstance(x, integer_types + (Integer,)): + if isinstance(x, (int, Integer)): if x == 1: self._element_list = [] else: @@ -67,10 +66,10 @@ def __init__(self, F, x, check=True): if check: x2 = [] for v in x: - if not isinstance(v, tuple) and len(v) == 2: + if not (isinstance(v, tuple) and len(v) == 2): raise TypeError("x (= %s) must be a list of 2-tuples or 1."%x) - if not (isinstance(v[0], integer_types + (Integer,)) and - isinstance(v[1], integer_types + (Integer,))): + if not (isinstance(v[0], (int, Integer)) and + isinstance(v[1], (int, Integer))): raise TypeError("x (= %s) must be a list of 2-tuples of integers or 1."%x) if len(x2) > 0 and v[0] == x2[len(x2)-1][0]: x2[len(x2)-1] = (v[0], v[1]+x2[len(x2)-1][1]) @@ -194,7 +193,7 @@ def __call__(self, *x, **kwds): if kwds: x = self.gens() gens_dict = {name: i for i, name in enumerate(P.variable_names())} - for key, value in iteritems(kwds): + for key, value in kwds.items(): if key in gens_dict: x[gens_dict[key]] = value diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index 51494741aaf..8ee49b1fd01 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -12,7 +12,6 @@ # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # **************************************************************************** -from six import integer_types, iteritems from copy import copy from sage.misc.abstract_method import abstract_method @@ -545,7 +544,7 @@ def __pow__(self, n): sage: x^0 1 """ - if not isinstance(n, integer_types + (Integer,)): + if not isinstance(n, (int, Integer)): raise TypeError("Argument n (= {}) must be an integer".format(n)) if n < 0: raise ValueError("Argument n (= {}) must be positive".format(n)) @@ -553,7 +552,7 @@ def __pow__(self, n): return self if n == 0: return self.parent().one() - return self.__class__(self.parent(), {k:v*n for k,v in iteritems(self._monomial)}) + return self.__class__(self.parent(), {k:v*n for k,v in self._monomial.items()}) def __floordiv__(self, elt): """ @@ -581,7 +580,7 @@ def __floordiv__(self, elt): ValueError: invalid cancellation """ d = copy(self._monomial) - for k, v in iteritems(elt._monomial): + for k, v in elt._monomial.items(): if k not in d: raise ValueError("invalid cancellation") diff = d[k] - v @@ -961,7 +960,7 @@ def _element_constructor_(self, x=None): d[k] = v x = d if isinstance(x, dict): - x = {k: v for k, v in iteritems(x) if v != 0} + x = {k: v for k, v in x.items() if v != 0} return IndexedMonoid._element_constructor_(self, x) Element = IndexedFreeAbelianMonoidElement diff --git a/src/sage/monoids/string_monoid_element.py b/src/sage/monoids/string_monoid_element.py index 2e335e76769..f99d68c22fb 100644 --- a/src/sage/monoids/string_monoid_element.py +++ b/src/sage/monoids/string_monoid_element.py @@ -20,7 +20,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import -from six import integer_types # import operator from sage.rings.integer import Integer @@ -88,7 +87,7 @@ def __init__(self, S, x, check=True): if isinstance(x, list): if check: for b in x: - if not isinstance(b, integer_types + (Integer,)): + if not isinstance(b, (int, Integer)): raise TypeError( "x (= %s) must be a list of integers." % x) self._element_list = list(x) # make copy @@ -191,7 +190,7 @@ def __pow__(self, n): ... IndexError: Argument n (= -1) must be non-negative. """ - if not isinstance(n, integer_types + (Integer,)): + if not isinstance(n, (int, Integer)): raise TypeError("Argument n (= %s) must be an integer." % n) if n < 0: raise IndexError("Argument n (= %s) must be non-negative." % n) diff --git a/src/sage/monoids/trace_monoid.py b/src/sage/monoids/trace_monoid.py new file mode 100644 index 00000000000..6e52c3bdb35 --- /dev/null +++ b/src/sage/monoids/trace_monoid.py @@ -0,0 +1,985 @@ +r""" +Module of trace monoids (free partially commutative monoids). + +EXAMPLES: + +We first create a trace monoid:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: M. = TraceMonoid(I=(('a','c'), ('c','a'))); M + Trace monoid on 3 generators ([a], [b], [c]) with independence relation {{a, c}} + +Different elements can be equal because of the partially +commutative multiplication:: + + sage: c * a * b == a * c * b + True + +We check that it is a monoid:: + + sage: M in Monoids() + True + +REFERENCES: + +- :wikipedia:`Trace_monoid` + +- https://ncatlab.org/nlab/show/trace+monoid + +AUTHORS: + +- Pavlo Tokariev (2019-05-31): initial version +""" +# **************************************************************************** +# Copyright (C) 2019 Pavlo Tokariev +# +# 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 collections import OrderedDict +from itertools import repeat, chain, product + +from sage.misc.cachefunc import cached_method +from sage.misc.misc_c import prod + +from sage.graphs.digraph import DiGraph +from sage.graphs.graph import Graph +from sage.monoids.free_monoid import FreeMonoid +from sage.monoids.monoid import Monoid_class +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.power_series_ring import PowerSeriesRing +from sage.rings.infinity import infinity +from sage.combinat.words.alphabet import Alphabet +from sage.structure.element import MonoidElement +from sage.structure.element_wrapper import ElementWrapper +from sage.structure.unique_representation import UniqueRepresentation + + +class TraceMonoidElement(ElementWrapper, MonoidElement): + r""" + Element of a trace monoid, also known as a trace. + + Elements of trace monoid is actually a equivalence classes + of related free monoid over some equivalence relation + that in the case is presented as independence relation. + + .. RUBRIC:: Representative + + We transform each trace to its lexicographic form for the + representative in the ambient free monoid. This is also used + for comparisons. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: x = b * a * d * a * c * b + sage: x^3 + [b*a^2*d*b^2*c*a^2*d*b^2*c*a^2*d*b*c] + sage: x^0 + 1 + sage: x.lex_normal_form() + b*a^2*d*b*c + sage: x.foata_normal_form() + (b, a*d, a, b*c) + """ + def _repr_(self): + """ + Textual representation of ``self``. + + TESTS:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: a * b + [a*b] + sage: b * a + [b*a] + sage: d * a + [a*d] + """ + if self == self.parent().one(): + return "1" + return "[{}]".format(self.value) + + def _richcmp_(self, other, op): + r""" + Compare two traces by their lexicographic normal forms. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: a^2 > a + True + sage: a*b < b*a + True + sage: a * c * b == a * b * c + True + """ + return self.value._richcmp_(other.value, op) + + def lex_normal_form(self): + r""" + Return the lexicographic normal form of ``self``. + + OUTPUT: + + A free monoid element. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: (a*b).lex_normal_form() + a*b + sage: (b*a).lex_normal_form() + b*a + sage: (d*a).lex_normal_form() + a*d + """ + return self.value + + def foata_normal_form(self): + r""" + Return the Foata normal form of ``self``. + + OUTPUT: + + Tuple of free monoid elements. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: x = b * a * d * a * c * b + sage: x.foata_normal_form() + (b, a*d, a, b*c) + """ + return self.parent()._compute_foata_normal_form(self.value) + + def _mul_(self, other): + r""" + Concatenate one equivalence class with another. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: a * b * c == a * c * b + True + """ + return self.parent(self.value * other.value) + + def _flat_elements(self): + r""" + Return flatten list of generator numbers representing the trace. + + OUTPUT: + + A list of generator indexes. + + TESTS:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: x = b * a^3 * d * a * c * b^2 + sage: x._flat_elements() + [b, a, a, a, a, d, b, b, c] + """ + return [g for g, times in self.value for _ in range(times)] + + @cached_method + def dependence_graph(self): + r""" + Return dependence graph of the trace. + + It is a directed graph where all dependent (non-commutative) + generators are connected by edges which + direction depend on the generator position in the trace. + + OUTPUT: + + Directed graph of generator indexes. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: x = b * a * d * a * c * b + sage: x.dependence_graph() + Digraph on 6 vertices + """ + elements = self._flat_elements() + independence = self.parent()._independence + graph = {} + + for i, e in enumerate(elements): + edges = [] + for v in graph: + if (e, elements[v]) not in independence: + edges.append((v, i)) + graph[i] = [] + for v1, v2 in edges: + graph[v1].append(v2) + + return DiGraph(graph) + + @cached_method + def hasse_diagram(self, algorithm="naive"): + r""" + Return Hasse diagram of the trace. + + Hasse diagram is a dependence graph without transitive edges. + + INPUT: + + - ``algorithm`` -- string (default: ``'naive'``); defines algorithm + that will be used to compute Hasse diagram; there are two + variants: ``'naive'`` and ``'min'``. + + OUTPUT: + + Directed graph of generator indexes. + + .. SEEALSO:: + + :meth:`~sage.monoids.trace_monoid.TraceMonoidElement.naive_hasse_digram`, + :meth:`~sage.monoids.trace_monoid.TraceMonoidElement.min_hasse_diagram`. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: x = b * a * d * a * c * b + sage: x.hasse_diagram() + Digraph on 6 vertices + + TESTS:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: x = b * a * d * a * c * b + sage: x.hasse_diagram(algorithm='naive') == x.hasse_diagram(algorithm='min') + True + sage: y = b * a^3 * d * a * c * b^2 + sage: y.hasse_diagram(algorithm='naive') == y.hasse_diagram(algorithm='min') + True + """ + if algorithm == "naive": + return self.naive_hasse_diagram() + elif algorithm == "min": + return self.min_hasse_diagram() + else: + raise ValueError("`alg` option must be `naive` " + "or `min`, got `{}`.".format(algorithm)) + + def min_hasse_diagram(self): + r""" + Return Hasse diagram of the trace. + + OUTPUT: + + Directed graph of generator indexes. + + .. SEEALSO:: + + :meth:`~sage.monoids.trace_monoid.TraceMonoidElement.hasse_digram`, + :meth:`~sage.monoids.trace_monoid.TraceMonoidElement.naive_hasse_diagram`. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: x = b * a * d * a * c * b + sage: x.min_hasse_diagram() + Digraph on 6 vertices + """ + elements = self._flat_elements() + elements.reverse() + independence = self.parent()._independence + reachable = dict() + min = set() + graph = DiGraph({}) + + for i, x in enumerate(elements): + reachable[i] = set() + front = min.copy() + while front: + used = set() + for j in list(front): + y = elements[j] + if (x, y) not in independence: + graph.add_edge(i, j) + reachable[i].add(j) + reachable[i].update(reachable[j]) + if j in min: + min.remove(j) + used.add(j) + forbidden = set(chain.from_iterable(reachable[v] for v in used)) + front = set(dest for _, dest in graph.outgoing_edges(front, labels=False)) + front = front - forbidden + + min.add(i) + + length = len(elements) + graph.relabel(length - 1 - i for i in range(length)) + return graph + + def naive_hasse_diagram(self): + r""" + Return Hasse diagram of ``self``. + + ALGORITHM: + + In loop check for every two pair of edges if they + have common vertex, remove their transitive edge. + + OUTPUT: + + Directed graph of generator indexes. + + .. SEEALSO:: + + :meth:`~sage.monoids.trace_monoid.TraceMonoidElement.hasse_digram`, + :meth:`~sage.monoids.trace_monoid.TraceMonoidElement.min_hasse_diagram`. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: x = b * a * d * a * c * b + sage: x.naive_hasse_diagram() + Digraph on 6 vertices + """ + d = self.dependence_graph() + h = d.copy() + + for e1 in d.edges(): + for e2 in d.edges(): + if e1[1] == e2[0]: + h.delete_edge((e1[0], e2[1])) + + return h + + def alphabet(self): + r""" + Return alphabet of ``self``. + + OUTPUT: + + A set of free monoid generators. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: x = b*a*d*a*c*b + sage: x.alphabet() + {b, a, d, c} + """ + return Alphabet([g for g, _ in self.value]) + + def projection(self, letters): + r""" + Return a trace that formed from ``self`` by erasing ``letters``. + + INPUT: + + - ``letters`` -- set of generators; defines set of letters that will be + used to filter the trace + + OUTPUT: + + A trace + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: F. = FreeMonoid() + sage: I = ((a,d), (d,a), (b,c), (c,b)) + sage: M. = TraceMonoid(F, I=I) + sage: x = M(b*a*d*a*c*b) + sage: x.projection({a,b}) + [b*a^2*b] + sage: x.projection({b,d,c}) + [b*d*b*c] + """ + P = self.parent() + base = P._free_monoid + return P(base.prod(x for x in self._flat_elements() if x in letters)) + + def multiplicative_order(self): + r""" + Return the multiplicative order of ``self``, which is `\infty` + for any element not the identity. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: a.multiplicative_order() + +Infinity + sage: M.one().multiplicative_order() + 1 + """ + if self.value.is_one(): + return ZZ.one() + return infinity + + +class TraceMonoid(UniqueRepresentation, Monoid_class): + r""" + Return a free partially commuting monoid (trace monoid) on `n` generators + over independence relation `I`. + + We construct a trace monoid by specifing: + + - a free monoid and independence relation + - or generator names and independence relation, + FreeMonoid is constructed automatically then. + + INPUT: + + - ``M`` -- a free monoid + + - ``I`` -- commutation relation between generators + (or their names if the ``names`` are given) + + - ``names`` -- names of generators + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: F = TraceMonoid(names=('a', 'b', 'c'), I={('a','c'), ('c','a')}); F + Trace monoid on 3 generators ([a], [b], [c]) with independence relation {{a, c}} + sage: x = F.gens() + sage: x[0]*x[1]**5 * (x[0]*x[2]) + [a*b^5*a*c] + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: M. = TraceMonoid(I=(('a','c'), ('c','a'))) + sage: latex(M) + \langle a, b, c \mid ac=ca \rangle + + TESTS:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: M. = TraceMonoid(I=(('a','c'), ('c','a'))) + sage: M.number_of_words(3) == len(M.words(3)) + True + """ + Element = TraceMonoidElement + + @staticmethod + def __classcall_private__(cls, M=None, I=frozenset(), names=None): + """ + Normalize input to ensure a unique representation. + + TESTS:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: M1. = TraceMonoid(I=(('a','c'), ('c','a'))) + sage: M2. = TraceMonoid(I=[('a','c')]) + sage: M3 = TraceMonoid(I=[{'a','c'}], names=('a', 'b', 'c')) + sage: M1 is M2 and M2 is M3 + True + """ + if not M: + if names: + M = FreeMonoid(names=names) + else: + raise ValueError("names must be provided") + elif not names: + names = [str(g) for g in M.gens()] + names = tuple(names) + + rels = set() + gen_from_str = {names[i]: gen for i, gen in enumerate(M.gens())} + for (x, y) in I: + try: + if isinstance(x, str): + x = gen_from_str[x] + x = M(x) + if isinstance(y, str): + y = gen_from_str[y] + y = M(y) + if x == y: + raise ValueError + except (TypeError, ValueError): + raise ValueError("invalid relation defined") + rels.add((x, y)) + rels.add((y, x)) + I = frozenset(rels) + + return super(TraceMonoid, cls).__classcall__(cls, M, I, names) + + def __init__(self, M, I, names): + r""" + Initialize ``self``. + + TESTS:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: M. = TraceMonoid(I=(('a','c'), ('c','a'))) + sage: TestSuite(M).run() + """ + self._free_monoid = M + self._independence = I + Monoid_class.__init__(self, names=names) + + def ngens(self): + """ + Return the number of generators of ``self``. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: M. = TraceMonoid(I=(('a','c'), ('c','a'))) + sage: M.ngens() + 3 + """ + return self._free_monoid.ngens() + + def one(self): + """ + Return the neutral element of ``self``. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: M. = TraceMonoid(I=(('a','c'), ('c','a'))) + sage: M.one() + 1 + """ + return self.element_class(self, self._free_monoid.one()) + + def gen(self, i=0): + """ + Return the `i`-th generator of the monoid. + + INPUT: + + - ``i`` -- integer (default: 0) + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: M. = TraceMonoid(I=(('a','c'), ('c','a'))) + sage: M.gen(1) + [b] + sage: M.gen(4) + Traceback (most recent call last): + ... + IndexError: argument i (= 4) must be between 0 and 2 + """ + return self.element_class(self, self._free_monoid.gen(i)) + + def cardinality(self): + """ + Return the cardinality of ``self``, which is infinite except for + the trivial monoid. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: M. = TraceMonoid(I=(('a','c'), ('c','a'))) + sage: M.cardinality() + +Infinity + """ + return self._free_monoid.cardinality() + + def _compute_dependence_stack(self, x): + r""" + Return generator stacks formed from trace + subelements with respect to non-commutativity. + + OUTPUT: + + Used generators and list of stacks as tuple. + + ALGORITHM: + + Let `x` be a word of monoid; we scan `x` from right to left; + when processing a letter `a` it is pushed on its stack and a + marker is pushed on the stack of all the letters `b` ( `b \neq a` ) + which do not commute with `a`. + + TESTS:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: F. = FreeMonoid() + sage: I = (('ac','dc'), ('dc','ac'), ('bc','cc'), ('cc','bc')) + sage: M. = TraceMonoid(F, I=I) + sage: x = b*a*d*a*c*b + sage: M._compute_dependence_stack(x) + ({a, b, c, d}, + OrderedDict([(a, [False, False, True, True, False]), + (b, [True, False, False, False, True]), + (c, [True, False, False, False]), + (d, [False, False, True, False])])) + """ + independence = self._independence + generators_set = set(e for e, _ in x) + stacks = OrderedDict(sorted((g, []) for g in generators_set)) + for generator, times in reversed(list(x)): + stacks[generator].extend(repeat(True, times)) + for other_gen in generators_set: + if other_gen == generator: + continue + if (generator, other_gen) not in independence: + stacks[other_gen].extend(repeat(False, times)) + return generators_set, stacks + + @cached_method + def _compute_lex_normal_form(self, x): + r""" + Return lexicographic normal form of the free monoid + element in free monoid terms. + + OUTPUT: + + Trace monoid element. + + ALGORITHM: + + Take among the letters being on the top of some stack that + letter `a` being minimal with respect to the given lexicographic + ordering. We pop a marker from each stack corresponding to a + letter `b` ( `b \neq a` ) which does not commute with `a`. We repeat + this loop until all stacks are empty. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: F. = FreeMonoid() + sage: I = ((a,d), (d,a), (b,c), (c,b)) + sage: M. = TraceMonoid(F, I=I) + sage: M._compute_lex_normal_form(c*a*c*b*a^2) + c*a*b*c*a^2 + """ + if not x._element_list: + return x + generators_set, stacks = self._compute_dependence_stack(x) + independence = self._independence + + elements = [] + while any(stacks.values()): + for generator, g_stack in stacks.items(): + if g_stack and g_stack[-1]: + g_stack.pop() + elements.append(generator) + for other_gen in generators_set: + if (other_gen != generator + and (generator, other_gen) not in independence): + stacks[other_gen].pop() + break + + return prod(elements) + + @cached_method + def _compute_foata_normal_form(self, x): + r""" + Return Foata normal form of the monoid element. + + OUTPUT: tuple of steps + + ALGORITHM: + + Within a loop we form the set using letters being + on the top of stacks; arranging the letters in the lexicographic + order yields a step of the Foata normal form; + This loop is repeated until all stacks are empty. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: F. = FreeMonoid() + sage: I = ((a,d), (d,a), (b,c), (c,b)) + sage: M. = TraceMonoid(F, I=I) + sage: x = b*a*d*a*c*b + sage: M._compute_foata_normal_form(x) + (b, a*d, a, b*c) + sage: y = b*a*a*d*b*a*b*c^2*a + sage: M._compute_foata_normal_form(y) + (b, a*d, a, b, a, b*c, c, a) + """ + if not x._element_list: + return tuple() + + generators_set, stacks = self._compute_dependence_stack(x) + independence = self._independence + + steps = [] + while any(stacks.values()): + step = [] + for generator, g_stack in stacks.items(): + if g_stack and g_stack[-1]: + g_stack.pop() + step.append(generator) + + for g in step: + for other_gen in generators_set: + if other_gen != g and (g, other_gen) not in independence: + stacks[other_gen].pop() + + steps.append(step) + + return tuple(prod(step) for step in steps) + + def _element_constructor_(self, x): + """ + Return ``x`` coerced into this trace monoid. + + One can create a free monoid element from the integer 1, + free monoid elements of the same generators as internal one, + and coerce everything that can coerce free monoid. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: F. = FreeMonoid() + sage: I = ((a,d), (d,a), (b,c), (c,b)) + sage: M. = TraceMonoid(F, I=I) + sage: x = b*a*d*a*c*b + sage: M(x) + [b*a^2*d*b*c] + """ + x = self._compute_lex_normal_form(self._free_monoid(x)) + return self.element_class(self, x) + + @cached_method + def independence(self): + r""" + Return independence relation over the monoid. + + OUTPUT: set of commuting generator pairs. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: F. = FreeMonoid() + sage: I = frozenset(((a,c), (c,a))) + sage: M. = TraceMonoid(F, I=I) + sage: M.independence() == frozenset([frozenset([a,c])]) + True + """ + return frozenset(map(frozenset, self._independence)) + + @cached_method + def dependence(self): + r""" + Return dependence relation over the monoid. + + OUTPUT: + + Set of non-commuting generator pairs. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: M. = TraceMonoid(I=(('a','c'), ('c','a'))) + sage: sorted(M.dependence()) + [(a, a), (a, b), (b, a), (b, b), (b, c), (c, b), (c, c)] + """ + return frozenset(pair for pair in product(self._free_monoid.gens(), repeat=2) + if pair not in self._independence) + + @cached_method + def dependence_graph(self): + r""" + Return graph of dependence relation. + + OUTPUT: dependence graph with generators as vertices + + TESTS:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: F. = FreeMonoid() + sage: M. = TraceMonoid(F, I=((a,c), (c,a))) + sage: M.dependence_graph() == Graph({a:[a,b], b:[b], c:[c,b]}) + True + """ + return Graph(set(frozenset((e1, e2)) if e1 != e2 else (e1, e2) + for e1, e2 in self.dependence()), loops=True, + format="list_of_edges", + immutable=True) + + @cached_method + def independence_graph(self): + r""" + Return the digraph of independence relations. + + OUTPUT: + + Independence graph with generators as vertices. + + TESTS:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: F. = FreeMonoid() + sage: M. = TraceMonoid(F, I=((a,c), (c,a))) + sage: M.independence_graph() == Graph({a:[c], b:[], c:[]}) + True + """ + verts = list(self._free_monoid.gens()) + edges = list(map(list, self.independence())) + return Graph([verts, edges], immutable=True) + + @cached_method + def dependence_polynomial(self, t=None): + r""" + Return dependence polynomial. + + The polynomial is defined as follows: `\sum{i}{(-1)^i c_i t^i}`, + where `c_i` equals to number of full subgraphs + of size `i` in the independence graph. + + OUTPUT: + + A rational function in ``t`` with coefficients in the integer ring. + + EXAMPLES:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: M.dependence_polynomial() + 1/(2*t^2 - 4*t + 1) + """ + if t is None: + R = PolynomialRing(ZZ, 't') + t = R.gen() + clique_seq = self.independence_graph().clique_polynomial().coefficients() + return ~sum((-1)**i * coeff * (t**i) + for i, coeff in enumerate(clique_seq)) + + @cached_method + def number_of_words(self, length): + r""" + Return number of unique words of defined length. + + INPUT: + + - ``length`` -- integer; defines size of words what number should be computed + + OUTPUT: words number as integer + + EXAMPLES: + + Get number of words of size 3 :: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: M.number_of_words(3) + 48 + """ + psr = PowerSeriesRing(ZZ, default_prec=length + 1) + return psr(self.dependence_polynomial()).coefficients()[length] + + @cached_method + def words(self, length): + r""" + Return all lexicographic forms of defined length. + + INPUT: + + - ``length`` -- integer; defines size of words + + OUTPUT: set of traces of size ``length`` + + EXAMPLES: + + All words of size 2:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: sorted(M.words(2)) + [[a^2], [a*b], [a*c], [a*d], [b*a], [b^2], [b*c], + [b*d], [c*a], [c^2], [c*d], [d*b], [d*c], [d^2]] + + Get number of words of size 3:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I) + sage: len(M.words(3)) + 48 + + TESTS:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: M. = TraceMonoid(I=(('a','b'), ('b','a'), ('b', 'c'), ('c', 'b'))) + sage: for i in range(10): + ....: assert len(M.words(i)) == M.number_of_words(i) + sage: True + True + """ + if length < 0: + raise ValueError("Bad length of words. Expected zero or positive number.") + if length == 0: + return frozenset([self.one()]) + if length == 1: + return frozenset(self.gens()) + + return frozenset([word * suffix for word in self.words(length - 1) + for suffix in self.gens() + if not ((list(word.value)[-1][0], suffix.value) in self._independence + and list(word.value)[-1][0] > suffix.value)]) + + def _repr_(self): + r""" + Textual representation of trace monoids. + + TESTS:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I); M + Trace monoid on 4 generators ([a], [b], [c], [d]) + with independence relation {{a, d}, {b, c}} + """ + return ("Trace monoid on {!s} generators {!s} " + "with independence relation {{{}}}").format(self.ngens(), self.gens(), + ", ".join("{{{}, {}}}".format(x, y) + for (x, y) in sorted(self.independence()))) + + def _latex_(self): + r""" + LaTeX representation of trace monoids. + + TESTS:: + + sage: from sage.monoids.trace_monoid import TraceMonoid + sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) + sage: M. = TraceMonoid(I=I); latex(M) + \langle a, b, c, d \mid ad=da,bc=cb \rangle + """ + return "\\langle {} \\mid {} \\rangle".format( + repr(self._free_monoid.gens())[1:-1], + ",".join( + "{0!r}{1!r}={1!r}{0!r}".format(v1, v2) + for v1, v2 in sorted(self.independence()) + ) + ) diff --git a/src/sage/numerical/backends/glpk_backend.pxd b/src/sage/numerical/backends/glpk_backend.pxd index 80f8f9faeaf..03dbe2c8688 100644 --- a/src/sage/numerical/backends/glpk_backend.pxd +++ b/src/sage/numerical/backends/glpk_backend.pxd @@ -29,7 +29,7 @@ cdef class GLPKBackend(GenericBackend): cpdef __copy__(self) cpdef int print_ranges(self, filename = *) except -1 cpdef double get_row_dual(self, int variable) - cpdef double get_col_dual(self, int variable) + cpdef double get_col_dual(self, int variable) except? -1 cpdef int get_row_stat(self, int variable) except? -1 cpdef int get_col_stat(self, int variable) except? -1 cpdef eval_tab_row(self, int k) diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index 68904b89674..8f44144d8ef 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -26,7 +26,6 @@ from sage.cpython.string cimport char_to_str, str_to_bytes from sage.cpython.string import FS_ENCODING from sage.ext.memory_allocator cimport MemoryAllocator from sage.numerical.mip import MIPSolverException -from sage.libs.glpk.error import GLPKError from sage.libs.glpk.constants cimport * from sage.libs.glpk.lp cimport * from libc.float cimport DBL_MAX @@ -55,7 +54,7 @@ cdef class GLPKBackend(GenericBackend): sage: p = MixedIntegerLinearProgram(solver="GLPK") """ self.lp = glp_create_prob() - self.simplex_or_intopt = glp_intopt_only + self.simplex_or_intopt = glp_simplex_then_intopt self.smcp = sig_malloc(sizeof(glp_smcp)) glp_init_smcp(self.smcp) self.iocp = sig_malloc(sizeof(glp_iocp)) @@ -261,7 +260,21 @@ cdef class GLPKBackend(GenericBackend): sage: p.set_variable_type(0,1) sage: p.is_variable_integer(0) True + + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.set_variable_type(2,0) + Traceback (most recent call last): + ... + ValueError: invalid variable index 2 + """ + if variable < 0 or variable > (self.ncols() - 1): + raise ValueError("invalid variable index %d" % variable) if vtype==1: glp_set_col_kind(self.lp, variable+1, GLP_IV) @@ -320,7 +333,22 @@ cdef class GLPKBackend(GenericBackend): sage: p.objective_coefficient(0,2) sage: p.objective_coefficient(0) 2.0 + + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.objective_coefficient(2) + Traceback (most recent call last): + ... + ValueError: invalid variable index 2 + """ + if variable < 0 or variable > (self.ncols() - 1): + raise ValueError("invalid variable index %d" % variable) + if coeff is None: return glp_get_obj_coef(self.lp, variable + 1) else: @@ -405,6 +433,7 @@ cdef class GLPKBackend(GenericBackend): sage: p.solve() 0.30000000000000004 sage: p.get_backend().set_verbosity(3) + sage: p.solver_parameter("simplex_or_intopt", "intopt_only") sage: p.solve() GLPK Integer Optimizer... 2 rows, 2 columns, 2 non-zeros @@ -420,17 +449,17 @@ cdef class GLPKBackend(GenericBackend): sage: p.add_constraint(10 * x[0] <= 1) sage: p.add_constraint(5 * x[1] <= 1) sage: p.set_objective(x[0] + x[1]) - sage: p.solve() + sage: p.solve() # tol 1e-14 0.3 sage: p.get_backend().set_verbosity(2) - sage: p.solve() + sage: p.solve() # tol 1e-14 * 2: objval = 0.3 (0) * 2: objval = 0.3 (0) 0.3 sage: p.get_backend().set_verbosity(3) - sage: p.solve() + sage: p.solve() # tol 1e-14 glp_exact: 2 rows, 2 columns, 2 non-zeros - GNU MP bignum library is being used + ... * 2: objval = 0.3 (0) * 2: objval = 0.3 (0) OPTIMAL SOLUTION FOUND @@ -580,12 +609,19 @@ cdef class GLPKBackend(GenericBackend): sage: q.add_constraint(p.new_variable()[0] <= 1) Traceback (most recent call last): ... - GLPKError: glp_set_mat_row: i = 1; len = 1; invalid row length - Error detected in file api/prob1.c at line ... + ValueError: invalid variable index 0 + """ if lower_bound is None and upper_bound is None: raise ValueError("At least one of 'upper_bound' or 'lower_bound' must be set.") + # We're going to iterate through this more than once. + coefficients = list(coefficients) + + for (index,_) in coefficients: + if index < 0 or index > (self.ncols() - 1): + raise ValueError("invalid variable index %d" % index) + glp_add_rows(self.lp, 1) cdef int n = glp_get_num_rows(self.lp) @@ -593,7 +629,6 @@ cdef class GLPKBackend(GenericBackend): cdef int * row_i cdef double * row_values - coefficients = list(coefficients) cdef int n_coeff = len(coefficients) row_i = mem.allocarray(n_coeff + 1, sizeof(int)) row_values = mem.allocarray(n_coeff + 1, sizeof(double)) @@ -695,7 +730,22 @@ cdef class GLPKBackend(GenericBackend): ([4, 3, 2, 1], [4.0, 3.0, 2.0, 1.0]) sage: p.row_bounds(0) (2.0, 2.0) + + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.row(2) + Traceback (most recent call last): + ... + ValueError: invalid row index 2 + """ + if index < 0 or index > (self.nrows() - 1): + raise ValueError("invalid row index %d" % index) + cdef int n = glp_get_num_cols(self.lp) cdef MemoryAllocator mem = MemoryAllocator() cdef int * c_indices = mem.allocarray(n+1, sizeof(int)) @@ -736,10 +786,25 @@ cdef class GLPKBackend(GenericBackend): ([4, 3, 2, 1], [4.0, 3.0, 2.0, 1.0]) sage: p.row_bounds(0) (2.0, 2.0) + + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.row_bounds(2) + Traceback (most recent call last): + ... + ValueError: invalid row index 2 + """ cdef double ub cdef double lb + if index < 0 or index > (self.nrows() - 1): + raise ValueError("invalid row index %d" % index) + ub = glp_get_row_ub(self.lp, index + 1) lb = glp_get_row_lb(self.lp, index +1) @@ -773,11 +838,26 @@ cdef class GLPKBackend(GenericBackend): sage: p.variable_upper_bound(0, 5) sage: p.col_bounds(0) (0.0, 5.0) + + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.col_bounds(2) + Traceback (most recent call last): + ... + ValueError: invalid column index 2 + """ cdef double ub cdef double lb + if index < 0 or index > (self.ncols() - 1): + raise ValueError("invalid column index %d" % index) + ub = glp_get_col_ub(self.lp, index +1) lb = glp_get_col_lb(self.lp, index +1) @@ -845,12 +925,15 @@ cdef class GLPKBackend(GenericBackend): """ Solve the problem. - Sage uses GLPK's implementation of the branch-and-cut algorithm - (``glp_intopt``) to solve the mixed-integer linear program. - This algorithm can be requested explicitly by setting the solver - parameter "simplex_or_intopt" to "intopt_only". - (If all variables are continuous, the algorithm reduces to solving the - linear program by the simplex method.) + Sage uses GLPK's implementation of the branch-and-cut + algorithm (``glp_intopt``) to solve the mixed-integer linear + program. This algorithm can be requested explicitly by + setting the solver parameter "simplex_or_intopt" to + "intopt_only". By default, the simplex method will be used + first to detect pathological problems that the integer solver + cannot handle. If all variables are continuous, the integer + algorithm reduces to solving the linear program by the simplex + method. EXAMPLES:: @@ -889,15 +972,14 @@ cdef class GLPKBackend(GenericBackend): .. WARNING:: - Sage uses GLPK's ``glp_intopt`` to find solutions. - This routine sometimes FAILS CATASTROPHICALLY - when given a system it cannot solve. (:trac:`12309`.) - Here, "catastrophic" can mean either "infinite loop" or - segmentation fault. Upstream considers this behavior - "essentially innate" to their design, and suggests - preprocessing it with ``glp_simplex`` first. - Thus, if you suspect that your system is infeasible, - set the ``preprocessing`` option first. + GLPK's ``glp_intopt`` sometimes fails catastrophically + when given a system it cannot solve (:trac:`12309`). It + can loop indefinitely, or just plain segfault. Upstream + considers this behavior "essentially innate" to the + current design, and suggests preprocessing with + ``glp_simplex``, which is what SageMath does by default. + Set the ``simplex_or_intopt`` solver parameter to + ``glp_intopt_only`` at your own risk. EXAMPLES:: @@ -912,11 +994,6 @@ cdef class GLPKBackend(GenericBackend): sage: lp.solve() Traceback (most recent call last): ... - GLPKError: Assertion failed: ... - sage: lp.solver_parameter("simplex_or_intopt", "simplex_then_intopt") - sage: lp.solve() - Traceback (most recent call last): - ... MIPSolverException: GLPK: Problem has no feasible solution If we switch to "simplex_only", the integrality constraints are ignored, @@ -1205,7 +1282,22 @@ cdef class GLPKBackend(GenericBackend): 0.0 sage: p.get_variable_value(1) 1.5 + + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.get_variable_value(2) + Traceback (most recent call last): + ... + ValueError: invalid variable index 2 + """ + if variable < 0 or variable > (self.ncols() - 1): + raise ValueError("invalid variable index %d" % variable) + if (self.simplex_or_intopt != glp_simplex_only and self.simplex_or_intopt != glp_exact_simplex_only): return glp_mip_col_val(self.lp, variable+1) @@ -1242,7 +1334,22 @@ cdef class GLPKBackend(GenericBackend): 20.0 sage: lp.get_row_prim(2) 8.0 + + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.get_row_prim(2) + Traceback (most recent call last): + ... + ValueError: invalid row index 2 + """ + if i < 0 or i > (self.nrows() - 1): + raise ValueError("invalid row index %d" % i) + return glp_get_row_prim(self.lp, i+1) cpdef int ncols(self): @@ -1295,9 +1402,24 @@ cdef class GLPKBackend(GenericBackend): 0 sage: p.col_name(0) 'I am a variable' + + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.col_name(2) + Traceback (most recent call last): + ... + ValueError: invalid column index 2 + """ cdef char * s + if index < 0 or index > (self.ncols() - 1): + raise ValueError("invalid column index %d" % index) + glp_create_index(self.lp) s = glp_get_col_name(self.lp, index + 1) @@ -1321,9 +1443,24 @@ cdef class GLPKBackend(GenericBackend): sage: p.add_linear_constraints(1, 2, None, names=['Empty constraint 1']) sage: p.row_name(0) 'Empty constraint 1' + + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.row_name(2) + Traceback (most recent call last): + ... + ValueError: invalid row index 2 + """ cdef char * s + if index < 0 or index > (self.nrows() - 1): + raise ValueError("invalid row index %d" % index) + glp_create_index(self.lp) s = glp_get_row_name(self.lp, index + 1) @@ -1352,7 +1489,21 @@ cdef class GLPKBackend(GenericBackend): sage: p.is_variable_binary(0) True + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.is_variable_binary(2) + False + """ + if index < 0 or index > (self.ncols() - 1): + # This is how the other backends behave, and this method is + # unable to raise a python exception as currently defined. + return False + return glp_get_col_kind(self.lp, index + 1) == GLP_BV cpdef bint is_variable_integer(self, int index): @@ -1374,7 +1525,22 @@ cdef class GLPKBackend(GenericBackend): sage: p.set_variable_type(0,1) sage: p.is_variable_integer(0) True + + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.is_variable_integer(2) + False + """ + if index < 0 or index > (self.ncols() - 1): + # This is how the other backends behave, and this method is + # unable to raise a python exception as currently defined. + return False + return glp_get_col_kind(self.lp, index + 1) == GLP_IV cpdef bint is_variable_continuous(self, int index): @@ -1399,7 +1565,21 @@ cdef class GLPKBackend(GenericBackend): sage: p.is_variable_continuous(0) False + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.is_variable_continuous(2) + False + """ + if index < 0 or index > (self.ncols() - 1): + # This is how the other backends behave, and this method is + # unable to raise a python exception as currently defined. + return False + return glp_get_col_kind(self.lp, index + 1) == GLP_CV cpdef bint is_maximization(self): @@ -1459,11 +1639,17 @@ cdef class GLPKBackend(GenericBackend): sage: p.variable_upper_bound(2) Traceback (most recent call last): ... - GLPKError: ... + ValueError: invalid variable index 2 + + sage: p.variable_upper_bound(-1) + Traceback (most recent call last): + ... + ValueError: invalid variable index -1 + sage: p.variable_upper_bound(3, 5) Traceback (most recent call last): ... - GLPKError: ... + ValueError: invalid variable index 3 sage: p.add_variable() 0 @@ -1480,6 +1666,9 @@ cdef class GLPKBackend(GenericBackend): cdef double min cdef double dvalue + if index < 0 or index > (self.ncols() - 1): + raise ValueError("invalid variable index %d" % index) + if value is False: sig_on() x = glp_get_col_ub(self.lp, index +1) @@ -1554,11 +1743,17 @@ cdef class GLPKBackend(GenericBackend): sage: p.variable_lower_bound(2) Traceback (most recent call last): ... - GLPKError: ... + ValueError: invalid variable index 2 + + sage: p.variable_lower_bound(-1) + Traceback (most recent call last): + ... + ValueError: invalid variable index -1 + sage: p.variable_lower_bound(3, 5) Traceback (most recent call last): ... - GLPKError: ... + ValueError: invalid variable index 3 sage: p.add_variable() 0 @@ -1575,6 +1770,9 @@ cdef class GLPKBackend(GenericBackend): cdef double max cdef double dvalue + if index < 0 or index > (self.ncols() - 1): + raise ValueError("invalid variable index %d" % index) + if value is False: sig_on() x = glp_get_col_lb(self.lp, index +1) @@ -1734,16 +1932,15 @@ cdef class GLPKBackend(GenericBackend): * - ``simplex_or_intopt`` - - specify which of ``simplex``, ``exact`` and ``intopt`` routines - in GLPK to use. - This is controlled by setting ``simplex_or_intopt`` to - ``glp_simplex_only``, ``glp_exact_simplex_only``, - ``glp_intopt_only`` and ``glp_simplex_then_intopt``, respectively. - The latter is useful to deal with a problem in GLPK where - problems with no solution hang when using integer optimization; - if you specify ``glp_simplex_then_intopt``, - sage will try simplex first, then perform integer optimization - only if a solution of the LP relaxation exists. + - specify which solution routines in GLPK to use. Set this + to either ``simplex_only``, ``exact_simplex_only``, + ``intopt_only``, or ``simplex_then_intopt`` (the + default). The ``simplex_then_intopt`` option does some + extra work, but avoids hangs/crashes in GLPK on problems + with no solution; SageMath will try simplex first, then + perform integer optimization only if a solution of the LP + relaxation exists. If you know that your system is not + pathological, one of the other options will be faster. * - ``verbosity_intopt`` and ``verbosity_simplex`` @@ -2384,7 +2581,7 @@ cdef class GLPKBackend(GenericBackend): else: return 0.0 - cpdef double get_col_dual(self, int variable): + cpdef double get_col_dual(self, int variable) except? -1: """ Returns the dual value (reduced cost) of a variable @@ -2420,7 +2617,21 @@ cdef class GLPKBackend(GenericBackend): sage: p.get_col_dual(1) -5.0 + TESTS: + + We sanity check the input that will be passed to GLPK:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver="GLPK") + sage: p.get_col_dual(2) + Traceback (most recent call last): + ... + ValueError: invalid column index 2 + """ + if variable < 0 or variable > (self.ncols() - 1): + raise ValueError("invalid column index %d" % variable) + if self.simplex_or_intopt == simplex_only: return glp_get_col_dual(self.lp, variable+1) else: @@ -2641,8 +2852,9 @@ cdef class GLPKBackend(GenericBackend): .. NOTE:: - The basis factorization must exist. - Otherwise, a ``MIPSolverException`` will be raised. + The basis factorization must exist and the variable with + index ``k`` must be basic. Otherwise, a ``ValueError`` is + be raised. INPUT: @@ -2675,7 +2887,7 @@ cdef class GLPKBackend(GenericBackend): sage: lp.eval_tab_row(0) Traceback (most recent call last): ... - MIPSolverException: ... + ValueError: basis factorization does not exist sage: lp.solve() 0 sage: lp.eval_tab_row(0) @@ -2687,29 +2899,35 @@ cdef class GLPKBackend(GenericBackend): sage: lp.eval_tab_row(1) Traceback (most recent call last): ... - MIPSolverException: ... + ValueError: slack variable 1 is not basic sage: lp.eval_tab_row(-1) Traceback (most recent call last): ... ValueError: ... """ - cdef int n = glp_get_num_cols(self.lp) + cdef int m = self.nrows() + cdef int n = self.ncols() cdef int i,j - if k < 0 or k >= n + glp_get_num_rows(self.lp): + if k < 0 or k >= n + m: raise ValueError("k = %s; Variable number out of range" % k) + if glp_bf_exists(self.lp) == 0: + raise ValueError("basis factorization does not exist") + + if k < m: + if not self.is_slack_variable_basic(k): + raise ValueError("slack variable %d is not basic" % k) + else: + if not self.is_variable_basic(k-m): + raise ValueError("variable %d is not basic" % (k-m) ) + cdef MemoryAllocator mem = MemoryAllocator() cdef int * c_indices = mem.allocarray(n+1, sizeof(int)) cdef double * c_values = mem.allocarray(n+1, sizeof(double)) - try: - sig_on() # to catch GLPKError - i = glp_eval_tab_row(self.lp, k + 1, c_indices, c_values) - sig_off() - except GLPKError: - raise MIPSolverException('GLPK: basis factorization does not exist; or variable must be basic') + i = glp_eval_tab_row(self.lp, k + 1, c_indices, c_values) indices = [c_indices[j+1] - 1 for j in range(i)] values = [c_values[j+1] for j in range(i)] @@ -2733,8 +2951,9 @@ cdef class GLPKBackend(GenericBackend): .. NOTE:: - The basis factorization must exist. - Otherwise a ``MIPSolverException`` will be raised. + The basis factorization must exist and the variable with + index ``k`` must not be basic. Otherwise, a ``ValueError`` is + be raised. INPUT: @@ -2767,7 +2986,7 @@ cdef class GLPKBackend(GenericBackend): sage: lp.eval_tab_col(1) Traceback (most recent call last): ... - MIPSolverException: ... + ValueError: basis factorization does not exist sage: lp.solve() 0 sage: lp.eval_tab_col(1) @@ -2779,29 +2998,35 @@ cdef class GLPKBackend(GenericBackend): sage: lp.eval_tab_col(0) Traceback (most recent call last): ... - MIPSolverException: ... + ValueError: slack variable 0 is basic sage: lp.eval_tab_col(-1) Traceback (most recent call last): ... ValueError: ... """ - cdef int m = glp_get_num_rows(self.lp) + cdef int m = self.nrows() + cdef int n = self.ncols() cdef int i,j - if k < 0 or k >= m + glp_get_num_cols(self.lp): + if k < 0 or k >= m + n: raise ValueError("k = %s; Variable number out of range" % k) + if glp_bf_exists(self.lp) == 0: + raise ValueError("basis factorization does not exist") + + if k < m: + if self.is_slack_variable_basic(k): + raise ValueError("slack variable %d is basic" % k) + else: + if self.is_variable_basic(k-m): + raise ValueError("variable %d is basic" % (k-m) ) + cdef MemoryAllocator mem = MemoryAllocator() cdef int * c_indices = mem.allocarray(m+1, sizeof(int)) cdef double * c_values = mem.allocarray(m+1, sizeof(double)) - try: - sig_on() # To catch GLPKError - i = glp_eval_tab_col(self.lp, k + 1, c_indices, c_values) - sig_off() - except GLPKError: - raise MIPSolverException('GLPK: basis factorization does not exist; or variable must be non-basic') + i = glp_eval_tab_col(self.lp, k + 1, c_indices, c_values) indices = [c_indices[j+1] - 1 for j in range(i)] values = [c_values[j+1] for j in range(i)] diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index 129edf63dcf..dda596ce0eb 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -53,7 +53,7 @@ def nodes(degree,prec): A list of (node,weight) pairs. - EXAMPLE: + EXAMPLES: The nodes for the Gauss-Legendre scheme are roots of Legendre polynomials. The weights can be computed by a straightforward formula (note that evaluating diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index 1481066669e..8324bf72430 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -167,23 +167,19 @@ Classes and functions --------------------- """ - - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2013 Andrey Novoseltsev # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from __future__ import print_function -from six.moves import range, zip import operator import re - from copy import copy from sage.misc.abstract_method import abstract_method @@ -200,7 +196,6 @@ random) from sage.misc.html import HtmlFragment from sage.misc.misc import get_main_globals -from sage.misc.superseded import deprecation from sage.modules.all import random_vector, vector from sage.plot.all import Graphics, arrow, line, point, rainbow, text from sage.rings.all import Infinity, PolynomialRing, QQ, RDF, ZZ @@ -3984,86 +3979,6 @@ def _latex_(self): lines[l] = lin return "\n".join(lines) - def ELLUL(self, entering, leaving): - r""" - Perform the Enter-Leave-LaTeX-Update-LaTeX step sequence on ``self``. - - INPUT: - - - ``entering`` -- the entering variable - - - ``leaving`` -- the leaving variable - - OUTPUT: - - - a string with LaTeX code for ``self`` before and after update - - EXAMPLES:: - - sage: A = ([1, 1], [3, 1]) - sage: b = (1000, 1500) - sage: c = (10, 5) - sage: P = InteractiveLPProblemStandardForm(A, b, c) - sage: D = P.initial_dictionary() - sage: D.ELLUL("x1", "x4") - doctest:...: DeprecationWarning: ELLUL is deprecated, please use separate enter-leave-update and output commands - See http://trac.sagemath.org/19097 for details. - \renewcommand{\arraystretch}{1.5} %notruncate - \begin{array}{|rcrcrcr|} - \hline - x_{3} \mspace{-6mu}&\mspace{-6mu} = \mspace{-6mu}&\mspace{-6mu} 1000 \mspace{-6mu}&\mspace{-6mu} - \mspace{-6mu}&\color{green}\mspace{-6mu} x_{1} \mspace{-6mu}&\mspace{-6mu} - \mspace{-6mu}&\mspace{-6mu} x_{2}\\ - \color{red}x_{4} \mspace{-6mu}&\color{red}\mspace{-6mu} = \mspace{-6mu}&\color{red}\mspace{-6mu} 1500 \mspace{-6mu}&\color{red}\mspace{-6mu} - \mspace{-6mu}&\color{blue}\mspace{-6mu} 3 x_{1} \mspace{-6mu}&\color{red}\mspace{-6mu} - \mspace{-6mu}&\color{red}\mspace{-6mu} x_{2}\\ - \hline - z \mspace{-6mu}&\mspace{-6mu} = \mspace{-6mu}&\mspace{-6mu} 0 \mspace{-6mu}&\mspace{-6mu} + \mspace{-6mu}&\color{green}\mspace{-6mu} 10 x_{1} \mspace{-6mu}&\mspace{-6mu} + \mspace{-6mu}&\mspace{-6mu} 5 x_{2}\\ - \hline - \\ - \hline - x_{3} \mspace{-6mu}&\mspace{-6mu} = \mspace{-6mu}&\mspace{-6mu} 500 \mspace{-6mu}&\mspace{-6mu} + \mspace{-6mu}&\mspace{-6mu} \frac{1}{3} x_{4} \mspace{-6mu}&\mspace{-6mu} - \mspace{-6mu}&\mspace{-6mu} \frac{2}{3} x_{2}\\ - x_{1} \mspace{-6mu}&\mspace{-6mu} = \mspace{-6mu}&\mspace{-6mu} 500 \mspace{-6mu}&\mspace{-6mu} - \mspace{-6mu}&\mspace{-6mu} \frac{1}{3} x_{4} \mspace{-6mu}&\mspace{-6mu} - \mspace{-6mu}&\mspace{-6mu} \frac{1}{3} x_{2}\\ - \hline - z \mspace{-6mu}&\mspace{-6mu} = \mspace{-6mu}&\mspace{-6mu} 5000 \mspace{-6mu}&\mspace{-6mu} - \mspace{-6mu}&\mspace{-6mu} \frac{10}{3} x_{4} \mspace{-6mu}&\mspace{-6mu} + \mspace{-6mu}&\mspace{-6mu} \frac{5}{3} x_{2}\\ - \hline - \end{array} - - This is how the above output looks when rendered: - - .. MATH:: - - \renewcommand{\arraystretch}{1.5} - \begin{array}{|rcrcrcr|} - \hline - x_{3} \mspace{-6mu}&\mspace{-6mu} = \mspace{-6mu}&\mspace{-6mu} 1000 \mspace{-6mu}&\mspace{-6mu} - \mspace{-6mu}&\color{green}\mspace{-6mu} x_{1} \mspace{-6mu}&\mspace{-6mu} - \mspace{-6mu}&\mspace{-6mu} x_{2}\\ - \color{red}x_{4} \mspace{-6mu}&\color{red}\mspace{-6mu} = \mspace{-6mu}&\color{red}\mspace{-6mu} 1500 \mspace{-6mu}&\color{red}\mspace{-6mu} - \mspace{-6mu}&\color{blue}\mspace{-6mu} 3 x_{1} \mspace{-6mu}&\color{red}\mspace{-6mu} - \mspace{-6mu}&\color{red}\mspace{-6mu} x_{2}\\ - \hline - z \mspace{-6mu}&\mspace{-6mu} = \mspace{-6mu}&\mspace{-6mu} 0 \mspace{-6mu}&\mspace{-6mu} + \mspace{-6mu}&\color{green}\mspace{-6mu} 10 x_{1} \mspace{-6mu}&\mspace{-6mu} + \mspace{-6mu}&\mspace{-6mu} 5 x_{2}\\ - \hline - \\ - \hline - x_{3} \mspace{-6mu}&\mspace{-6mu} = \mspace{-6mu}&\mspace{-6mu} 500 \mspace{-6mu}&\mspace{-6mu} + \mspace{-6mu}&\mspace{-6mu} \frac{1}{3} x_{4} \mspace{-6mu}&\mspace{-6mu} - \mspace{-6mu}&\mspace{-6mu} \frac{2}{3} x_{2}\\ - x_{1} \mspace{-6mu}&\mspace{-6mu} = \mspace{-6mu}&\mspace{-6mu} 500 \mspace{-6mu}&\mspace{-6mu} - \mspace{-6mu}&\mspace{-6mu} \frac{1}{3} x_{4} \mspace{-6mu}&\mspace{-6mu} - \mspace{-6mu}&\mspace{-6mu} \frac{1}{3} x_{2}\\ - \hline - z \mspace{-6mu}&\mspace{-6mu} = \mspace{-6mu}&\mspace{-6mu} 5000 \mspace{-6mu}&\mspace{-6mu} - \mspace{-6mu}&\mspace{-6mu} \frac{10}{3} x_{4} \mspace{-6mu}&\mspace{-6mu} + \mspace{-6mu}&\mspace{-6mu} \frac{5}{3} x_{2}\\ - \hline - \end{array} - - The column of the entering variable is green, while the row of the - leaving variable is red in the original dictionary state on the top. - The new state after the update step is shown on the bottom. - """ - deprecation(19097, "ELLUL is deprecated, please use separate " - "enter-leave-update and output commands") - self.enter(entering) - self.leave(leaving) - result = latex(self).rsplit("\n", 1)[0] # Remove \end{array} - # Make an empty line in the array - if generate_real_LaTeX: - result += "\n" r"\multicolumn{2}{c}{}\\[-3ex]" "\n" - else: - result += "\n\\\\\n" - self.update() - result += latex(self).split("\n", 2)[2] # Remove array header - return LatexExpr(result) - def add_row(self, nonbasic_coefficients, constant, basic_variable=None): r""" Return a dictionary with an additional row based on a given dictionary. diff --git a/src/sage/numerical/linear_functions.pyx b/src/sage/numerical/linear_functions.pyx index 8113ca148f5..82fb733f610 100644 --- a/src/sage/numerical/linear_functions.pyx +++ b/src/sage/numerical/linear_functions.pyx @@ -688,10 +688,7 @@ cdef class LinearFunctionsParent_class(Parent): False """ if is_LinearFunction(x): - if x.parent() is self: - return x - else: - return LinearFunction(self, (x)._f) + return LinearFunction(self, (x)._f) return LinearFunction(self, x) cpdef _coerce_map_from_(self, R): diff --git a/src/sage/numerical/linear_tensor.py b/src/sage/numerical/linear_tensor.py index 1510550735e..ffc253494db 100644 --- a/src/sage/numerical/linear_tensor.py +++ b/src/sage/numerical/linear_tensor.py @@ -415,10 +415,7 @@ def _element_constructor_(self, x): """ M = self.free_module() if is_LinearTensor(x): - if x.parent() is self: - return x - else: - x = x.dict() + x = x.dict() elif is_LinearFunction(x): x = dict([key, self._convert_constant(value)] for key, value in x.dict().items()) elif isinstance(x, dict): diff --git a/src/sage/numerical/optimize.py b/src/sage/numerical/optimize.py index d2f83103f2e..a1bbda47be3 100644 --- a/src/sage/numerical/optimize.py +++ b/src/sage/numerical/optimize.py @@ -10,7 +10,6 @@ Functions and Methods ---------------------- """ -from six.moves import range from sage.modules.free_module_element import vector from sage.rings.real_double import RDF @@ -80,14 +79,14 @@ def find_root(f, a, b, xtol=10e-13, rtol=2.0**-50, maxiter=100, full_output=Fals the function need not be defined at the endpoints:: sage: find_root(x^2*log(x,2)-1,0, 2) # abs tol 1e-6 - 1.41421356237 - + 1.41421356237 + The following is an example, again from :trac:`4942` where Brent's method - fails. Currently no other method is implemented, but at least we + fails. Currently no other method is implemented, but at least we acknowledge the fact that the algorithm fails:: - sage: find_root(1/(x-1)+1,0, 2) - 0.0 + sage: find_root(1/(x-1)+1,0, 2) + 0.0 sage: find_root(1/(x-1)+1,0.00001, 2) Traceback (most recent call last): ... @@ -111,7 +110,7 @@ def find_root(f, a, b, xtol=10e-13, rtol=2.0**-50, maxiter=100, full_output=Fals a, b = b, a left = f(a) right = f(b) - + if left > 0 and right > 0: # Refine further -- try to find a point where this # function is negative in the interval @@ -139,15 +138,15 @@ def find_root(f, a, b, xtol=10e-13, rtol=2.0**-50, maxiter=100, full_output=Fals raise RuntimeError("f appears to have no zero on the interval") a = s - # Fixing :trac:`4942` - if the answer on any of the endpoints is NaN, + # Fixing :trac:`4942` - if the answer on any of the endpoints is NaN, # we restrict to looking between minimum and maximum values in the segment - # Note - this could be used in all cases, but it requires some more + # Note - this could be used in all cases, but it requires some more # computation if (left != left) or (right != right): minval, s_1 = find_local_minimum(f, a, b) maxval, s_2 = find_local_maximum(f, a, b) - if ((minval > 0) or (maxval < 0) or + if ((minval > 0) or (maxval < 0) or (minval != minval) or (maxval != maxval)): raise RuntimeError("f appears to have no zero on the interval") a = min(s_1, s_2) @@ -344,7 +343,7 @@ def minimize(func, x0, gradient=None, hessian=None, algorithm="default", sage: minimize(f, [.1,.3,.4]) # abs tol 1e-6 (1.0, 1.0, 1.0) - Try the newton-conjugate gradient method; the gradient and hessian are + Try the newton-conjugate gradient method; the gradient and hessian are computed automatically:: sage: minimize(f, [.1, .3, .4], algorithm="ncg") # abs tol 1e-6 diff --git a/src/sage/parallel/decorate.py b/src/sage/parallel/decorate.py index c0713fa14f2..392227334dc 100644 --- a/src/sage/parallel/decorate.py +++ b/src/sage/parallel/decorate.py @@ -2,7 +2,6 @@ Decorate interface for parallel computation """ from __future__ import print_function, absolute_import -from six import integer_types import types @@ -70,7 +69,7 @@ def __init__(self, p_iter='fork', ncpus=None, **kwds): self.p_iter = None - if isinstance(p_iter, integer_types + (Integer,)): + if isinstance(p_iter, (int, Integer)): p_iter, ncpus = 'fork', p_iter if ncpus is None: diff --git a/src/sage/parallel/map_reduce.py b/src/sage/parallel/map_reduce.py index 7e377dddde0..715a53a5cf8 100644 --- a/src/sage/parallel/map_reduce.py +++ b/src/sage/parallel/map_reduce.py @@ -7,8 +7,7 @@ over which one would like to perform the following kind of operations: * Compute the cardinality of a (very large) set defined recursively - (through a call to :class:`RecursivelyEnumeratedSet - of forest type`) + (through a call to :class:`RecursivelyEnumeratedSet_forest`) * More generally, compute any kind of generating series over this set @@ -40,8 +39,7 @@ How is this different from usual MapReduce ? -------------------------------------------- -This implementation is specific to :class:`RecursivelyEnumeratedSet -of forest type`, and uses its +This implementation is specific to :class:`RecursivelyEnumeratedSet_forest`, and uses its properties to do its job. Not only mapping and reducing but also **generating the elements** of `S` is done on different processors. @@ -51,9 +49,14 @@ How can I use all that stuff? ----------------------------- -First, you need the information necessary to describe a -:class:`RecursivelyEnumeratedSet of forest -type` representing your set `S` (see +First, you need to set the environment variable `SAGE_NUM_THREADS` to the +desired number of parallel threads to be used: + + sage: import os # not tested + sage: os.environ["SAGE_NUM_THREADS"] = '8' # not tested + +Second, you need the information necessary to describe a +:class:`RecursivelyEnumeratedSet_forest` representing your set `S` (see :mod:`sage.sets.recursively_enumerated_set`). Then, you need to provide a "map" function as well as a "reduce" function. Here are some examples: @@ -79,7 +82,7 @@ 2^17 Note that the map and reduce functions here have the default values of the - :meth:`sage.combinat.backtrack.SearchForest.map_reduce` method + :meth:`sage.sets.recursively_enumerated_set.RecursivelyEnumeratedSet_forest.map_reduce` method so that the number of elements can be obtained more simply with:: sage: S.map_reduce() @@ -185,7 +188,7 @@ q^10 + 4*q^9 + 9*q^8 + 15*q^7 + 20*q^6 + 22*q^5 + 20*q^4 + 15*q^3 + 9*q^2 + 4*q + 1 * **Listing the objects.** One can also compute the list of objects in a - :class:`RecursivelyEnumeratedSet of forest type` + :class:`RecursivelyEnumeratedSet_forest>` using :class:`RESetMapReduce`. As an example, we compute the set of numbers between 1 and 63, generated by their binary expansion:: @@ -366,8 +369,7 @@ instance of :class:`RESetMapReduce`) by a bunch of **worker** objects (instances of :class:`RESetMapReduceWorker`). -Each running map reduce instance works on a :class:`RecursivelyEnumeratedSet of -forest type` called here `C` and is +Each running map reduce instance works on a :class:`RecursivelyEnumeratedSet_forest>` called here `C` and is coordinated by a :class:`RESetMapReduce` object called the **master**. The master is in charge of launching the work, gathering the results and cleaning up at the end of the computation. It doesn't perform any computation @@ -532,16 +534,14 @@ """ from __future__ import print_function, absolute_import -import six - from threading import Thread -from six.moves import queue from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet # _generic from sage.misc.lazy_attribute import lazy_attribute import collections import copy import sys import random +import queue import ctypes @@ -568,18 +568,10 @@ logger.addHandler(ch) -if six.PY2: - import multiprocessing as mp - from multiprocessing.queues import SimpleQueue - # Put SimpleQueue in the multiprocessing top-level namespace for - # compatibility with Python 3 - mp.SimpleQueue = SimpleQueue - del SimpleQueue -else: - # Set up a multiprocessing context to use for this modules (using the - # 'fork' method which is basically same as on Python 2) - import multiprocessing as mp - mp = mp.get_context('fork') +# Set up a multiprocessing context to use for this modules (using the +# 'fork' method which is basically same as on Python 2) +import multiprocessing as mp +mp = mp.get_context('fork') def proc_number(max_proc=None): @@ -891,8 +883,7 @@ class RESetMapReduce(object): Description of the set: - - either ``forest=f`` -- where ``f`` is a :class:`RecursivelyEnumeratedSet - of forest type` + - either ``forest=f`` -- where ``f`` is a :class:`RecursivelyEnumeratedSet_forest>` - or a triple ``roots, children, post_process`` as follows diff --git a/src/sage/plot/animate.py b/src/sage/plot/animate.py index 6ee55c0bea4..606e95d5214 100644 --- a/src/sage/plot/animate.py +++ b/src/sage/plot/animate.py @@ -96,13 +96,14 @@ - John Palmieri - Niles Johnson (2013-12): Expand to animate more graphics objects - Martin von Gagern (2014-12): Added APNG support +- Joshua Campbell (2020): interactive animation via Three.js viewer REFERENCES: -- `ImageMagick `_ -- `FFmpeg `_ +- `ImageMagick `_ +- `FFmpeg `_ - `APNG `_ -- `browsers which support it `_ +- `browsers which support it `_ """ @@ -113,12 +114,11 @@ ############################################################################ from __future__ import print_function, absolute_import +import builtins import os import struct import zlib -import six - from sage.misc.fast_methods import WithEqualityById from sage.structure.sage_object import SageObject from sage.misc.temporary_file import tmp_dir, tmp_filename @@ -265,7 +265,6 @@ def _combine_kwds(self, *kwds_tuple): for kwds in kwds_tuple: new_kwds.update(kwds) - from six.moves import builtins for name in ['xmin', 'xmax', 'ymin', 'ymax']: values = [v for v in [kwds.get(name, None) for kwds in kwds_tuple] if v is not None] if values: @@ -560,7 +559,7 @@ def gif(self, delay=20, savefile=None, iterations=0, show_path=False, sage: td = tmp_dir() sage: a.gif() # not tested sage: a.gif(savefile=td + 'my_animation.gif', delay=35, iterations=3) # optional -- ImageMagick - sage: with open(td + 'my_animation.gif', 'rb') as f: print('\x21\xf9\x04\x08\x23\x00' in f.read()) # optional -- ImageMagick + sage: with open(td + 'my_animation.gif', 'rb') as f: print(b'\x21\xf9\x04\x08\x23\x00' in f.read()) # optional -- ImageMagick True sage: a.gif(savefile=td + 'my_animation.gif', show_path=True) # optional -- ImageMagick Animation saved to .../my_animation.gif. @@ -577,11 +576,6 @@ def gif(self, delay=20, savefile=None, iterations=0, show_path=False, packages, so please install one of them and try again. See www.imagemagick.org and www.ffmpeg.org for more information. - - REFERENCES: - - - `ImageMagick `_ - - `FFmpeg `_ """ from sage.misc.sage_ostools import have_program have_convert = have_program('convert') @@ -848,7 +842,7 @@ def ffmpeg(self, savefile=None, show_path=False, output_format=None, If ``savefile`` is not specified: in notebook mode, display the animation; otherwise, save it to a default file name. Use - :func:`sage.misc.misc.set_verbose` with ``level=1`` to see + :func:`sage.misc.verbose.set_verbose` with ``level=1`` to see additional output. EXAMPLES:: @@ -935,12 +929,12 @@ def ffmpeg(self, savefile=None, show_path=False, output_format=None, cmd = 'cd "%s"; sage-native-execute ffmpeg -y -f image2 %s -i %s %s %s' % (pngdir, early_options, pngs, ffmpeg_options, savefile) from subprocess import check_call, CalledProcessError, PIPE try: - if sage.misc.misc.get_verbose() > 0: + if sage.misc.verbose.get_verbose() > 0: set_stderr = None else: set_stderr = PIPE - sage.misc.misc.verbose("Executing '%s'" % cmd,level=1) - sage.misc.misc.verbose("\n---- ffmpeg output below ----\n") + sage.misc.verbose.verbose("Executing '%s'" % cmd,level=1) + sage.misc.verbose.verbose("\n---- ffmpeg output below ----\n") check_call(cmd, shell=True, stderr=set_stderr) if show_path: print("Animation saved to file %s." % savefile) @@ -1022,7 +1016,9 @@ def save(self, filename=None, show_path=False, use_ffmpeg=False, **kwds): files. If filename is None, then in notebook mode, display the - animation; otherwise, save the animation to a GIF file. If + animation; otherwise, save the animation to a GIF file. If + filename ends in '.html', save an :meth:`interactive` version of + the animation to an HTML file that uses the Three.js viewer. If filename ends in '.sobj', save to an sobj file. Otherwise, try to determine the format from the filename extension ('.mpg', '.gif', '.avi', etc.). If the format cannot be @@ -1030,7 +1026,7 @@ def save(self, filename=None, show_path=False, use_ffmpeg=False, **kwds): For GIF files, either ffmpeg or the ImageMagick suite must be installed. For other movie formats, ffmpeg must be installed. - An sobj file can be saved with no extra software installed. + sobj and HTML files can be saved with no extra software installed. EXAMPLES:: @@ -1046,6 +1042,9 @@ def save(self, filename=None, show_path=False, use_ffmpeg=False, **kwds): sage: a.save(td + 'wave0.sobj') sage: a.save(td + 'wave1.sobj', show_path=True) Animation saved to file .../wave1.sobj. + sage: a.save(td + 'wave0.html', online=True) + sage: a.save(td + 'wave1.html', show_path=True, online=True) + Animation saved to file .../wave1.html. TESTS: @@ -1053,26 +1052,30 @@ def save(self, filename=None, show_path=False, use_ffmpeg=False, **kwds): GIF image (see :trac:`18176`):: sage: a.save(td + 'wave.gif') # optional -- ImageMagick - sage: with open(td + 'wave.gif', 'rb') as f: print('!\xf9\x04\x08\x14\x00' in f.read()) # optional -- ImageMagick + sage: with open(td + 'wave.gif', 'rb') as f: print(b'!\xf9\x04\x08\x14\x00' in f.read()) # optional -- ImageMagick True - sage: with open(td + 'wave.gif', 'rb') as f: print('!\xff\x0bNETSCAPE2.0\x03\x01\x00\x00\x00' in f.read()) # optional -- ImageMagick + sage: with open(td + 'wave.gif', 'rb') as f: print(b'!\xff\x0bNETSCAPE2.0\x03\x01\x00\x00\x00' in f.read()) # optional -- ImageMagick True sage: a.save(td + 'wave.gif', delay=35) # optional -- ImageMagick - sage: with open(td + 'wave.gif', 'rb') as f: print('!\xf9\x04\x08\x14\x00' in f.read()) # optional -- ImageMagick + sage: with open(td + 'wave.gif', 'rb') as f: print(b'!\xf9\x04\x08\x14\x00' in f.read()) # optional -- ImageMagick False - sage: with open(td + 'wave.gif', 'rb') as f: print('!\xf9\x04\x08\x23\x00' in f.read()) # optional -- ImageMagick + sage: with open(td + 'wave.gif', 'rb') as f: print(b'!\xf9\x04\x08\x23\x00' in f.read()) # optional -- ImageMagick True sage: a.save(td + 'wave.gif', iterations=3) # optional -- ImageMagick - sage: with open(td + 'wave.gif', 'rb') as f: print('!\xff\x0bNETSCAPE2.0\x03\x01\x00\x00\x00' in f.read()) # optional -- ImageMagick + sage: with open(td + 'wave.gif', 'rb') as f: print(b'!\xff\x0bNETSCAPE2.0\x03\x01\x00\x00\x00' in f.read()) # optional -- ImageMagick False - sage: with open(td + 'wave.gif', 'rb') as f: print('!\xff\x0bNETSCAPE2.0\x03\x01\x03\x00\x00' in f.read()) # optional -- ImageMagick + sage: with open(td + 'wave.gif', 'rb') as f: # optional -- ImageMagick + ....: check1 = b'!\xff\x0bNETSCAPE2.0\x03\x01\x02\x00\x00' + ....: check2 = b'!\xff\x0bNETSCAPE2.0\x03\x01\x03\x00\x00' + ....: data = f.read() + ....: print(check1 in data or check2 in data) True """ if filename is None: suffix = '.gif' else: suffix = os.path.splitext(filename)[1] - if len(suffix) == 0: + if not suffix: suffix = '.gif' if filename is None or suffix == '.gif': @@ -1082,9 +1085,70 @@ def save(self, filename=None, show_path=False, use_ffmpeg=False, **kwds): SageObject.save(self, filename) if show_path: print("Animation saved to file %s." % filename) + elif suffix == '.html': + self.interactive(**kwds).save(filename) + if show_path: + print("Animation saved to file %s." % filename) else: self.ffmpeg(savefile=filename, show_path=show_path, **kwds) + def interactive(self, **kwds): + r""" + Create an interactive depiction of the animation. + + INPUT: + + - ``**kwds`` -- any of the viewing options accepted by show() are valid + as keyword arguments to this function and they will behave in the same + way. Those that are animation-related and recognized by the Three.js + viewer are: ``animate``, ``animation_controls``, ``auto_play``, + ``delay``, and ``loop``. + + OUTPUT: + + A 3D graphics object which, by default, will use the Three.js viewer. + + EXAMPLES:: + + sage: frames = [point3d((sin(x), cos(x), x)) for x in (0, pi/16, .., 2*pi)] + sage: animate(frames).interactive(online=True) + Graphics3d Object + + Works with frames that are 2D or 3D graphics objects or convertible to + 2D or 3D graphics objects via a ``plot`` or ``plot3d`` method:: + + sage: frames = [dodecahedron(), circle(center=(0, 0), radius=1), x^2] + sage: animate(frames).interactive(online=True, delay=100) + Graphics3d Object + + .. SEEALSO:: + + :ref:`threejs_viewer` + + """ + from sage.plot.plot3d.base import Graphics3d, KeyframeAnimationGroup + # Attempt to convert frames to Graphics3d objects. + g3d_frames = [] + for i, frame in enumerate(self._frames): + if not isinstance(frame, Graphics3d): + try: + frame = frame.plot3d() + except (AttributeError, TypeError): + try: + frame = frame.plot().plot3d() + except (AttributeError, TypeError): + frame = None + if not isinstance(frame, Graphics3d): + raise TypeError("Could not convert frame {} to Graphics3d".format(i)) + g3d_frames.append(frame) + # Give preference to this method's keyword arguments over those provided + # to animate or the constructor. + kwds = dict(self._kwds, **kwds) + # Three.js is the only viewer that supports animation at present. + if 'viewer' not in kwds: + kwds['viewer'] = 'threejs' + return KeyframeAnimationGroup(g3d_frames, **kwds) + class APngAssembler(object): r""" @@ -1122,15 +1186,9 @@ class APngAssembler(object): ....: png = os.path.join(pngdir, "{:08d}.png".format(i)) ....: apng.add_frame(png, delay=10*i + 10) ....: return outfile - ....: sage: assembleAPNG() # long time '...png' - - REFERENCES: - - - `APNG `_ """ - magic = b"\x89PNG\x0d\x0a\x1a\x0a" mustmatch = frozenset([b"IHDR", b"PLTE", b"bKGD", b"cHRM", b"gAMA", b"pHYs", b"sBIT", b"tRNS"]) @@ -1531,10 +1589,8 @@ def _hex2bin(cls, h): else: # for PNG magic b.append(ord(h[0])) h = h[1:] - if six.PY2: - return ''.join(map(chr, b)) - else: - return bytes(b) + + return bytes(b) @classmethod def _testData(cls, name, asFile): diff --git a/src/sage/plot/arrow.py b/src/sage/plot/arrow.py index 694b90e4b95..8eb7a6d08b9 100644 --- a/src/sage/plot/arrow.py +++ b/src/sage/plot/arrow.py @@ -118,14 +118,16 @@ def _repr_(self): def _render_on_subplot(self, subplot): """ - Render this arrow in a subplot. This is the key function that - defines how this arrow graphics primitive is rendered in - matplotlib's library. + Render this arrow in a subplot. - EXAMPLES:: + This is the key function that defines how this arrow graphics + primitive is rendered in matplotlib's library. + + EXAMPLES: This function implicitly ends up rendering this arrow on a matplotlib - subplot: + subplot:: + sage: arrow(path=[[(0,1), (2,-1), (4,5)]]) Graphics object consisting of 1 graphics primitive """ diff --git a/src/sage/plot/colors.py b/src/sage/plot/colors.py index 1860b6c948a..9f12c0dbdb4 100644 --- a/src/sage/plot/colors.py +++ b/src/sage/plot/colors.py @@ -20,7 +20,7 @@ For a list of color maps in Sage, evaluate:: sage: sorted(colormaps) - [u'Accent', u'Accent_r', u'Blues', u'Blues_r', u'BrBG', u'BrBG_r', ...] + [u'Accent', u'Blues', u'BrBG', ...] These are imported from matplotlib's cm_ module. @@ -37,7 +37,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -import six import math import collections from colorsys import hsv_to_rgb, hls_to_rgb, rgb_to_hsv, rgb_to_hls @@ -338,7 +337,7 @@ def rgbcolor(c, space='rgb'): if isinstance(c, Color): return c.rgb() - if isinstance(c, six.string_types): + if isinstance(c, str): if len(c) > 0 and c[0] == '#': # Assume an HTML-like color, e.g., #00ffff or #ab0. return html_to_float(c) @@ -802,7 +801,7 @@ def __truediv__(self, right): RGB color (0.29166666666666663, 0.286437908496732, 0.07794117647058824) sage: vector((papayawhip / 2).rgb()) == vector((papayawhip * 0.5).rgb()) True - sage: yellow.__div__(1/4) + sage: yellow.__truediv__(1/4) RGB color (0.0, 0.0, 0.0) TESTS:: @@ -823,27 +822,6 @@ def __truediv__(self, right): """ return self * (1 / float(right)) - def __div__(self, right): - """ - Return a color whose RGB coordinates are this color's - coordinates divided by a scalar. - - INPUT: - - - ``right`` -- a float-convertible, non-zero number - - OUTPUT: - - - a **new** instance of :class:`Color` - - EXAMPLES:: - - sage: from sage.plot.colors import yellow - sage: yellow.__div__(4) - RGB color (0.25, 0.25, 0.0) - """ - return self / right - def __int__(self): """ Return the integer representation of this colour. @@ -1374,7 +1352,7 @@ def get_cmap(cmap): and color names. For a list of map names, evaluate:: sage: sorted(colormaps) - [u'Accent', u'Accent_r', u'Blues', u'Blues_r', ...] + [u'Accent', u'Blues', ...] See :func:`rgbcolor` for valid list/tuple element formats. @@ -1419,7 +1397,7 @@ def get_cmap(cmap): if isinstance(cmap, Colormap): return cmap - elif isinstance(cmap, six.string_types): + elif isinstance(cmap, str): if not cmap in cm.datad: raise RuntimeError("Color map %s not known (type import matplotlib.cm; matplotlib.cm.datad.keys() for valid names)" % cmap) return cm.__dict__[cmap] @@ -1468,7 +1446,7 @@ class Colormaps(collections.MutableMapping): For a list of map names, evaluate:: sage: sorted(colormaps) - [u'Accent', u'Accent_r', u'Blues', u'Blues_r', ...] + [u'Accent', u'Blues', ...] """ def __init__(self): """ @@ -1495,7 +1473,7 @@ def load_maps(self): sage: len(maps.maps) 0 sage: maps.load_maps() - sage: len(maps.maps)>130 + sage: len(maps.maps)>60 True """ global cm @@ -1539,7 +1517,7 @@ def __len__(self): sage: from sage.plot.colors import Colormaps sage: maps = Colormaps() - sage: len(maps)>130 + sage: len(maps)>60 True """ self.load_maps() diff --git a/src/sage/plot/contour_plot.py b/src/sage/plot/contour_plot.py index a7533fa3d94..12a2b1f6701 100644 --- a/src/sage/plot/contour_plot.py +++ b/src/sage/plot/contour_plot.py @@ -223,7 +223,7 @@ def _render_on_subplot(self, subplot): cb.add_lines(CS) -@suboptions('colorbar', orientation='vertical', format=None, spacing=None) +@suboptions('colorbar', orientation='vertical', format=None, spacing='uniform') @suboptions('label', fontsize=9, colors='blue', inline=None, inline_spacing=3, fmt="%1.2f") @options(plot_points=100, fill=True, contours=None, linewidths=None, diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index 2a80a050a50..39129f015fa 100644 --- a/src/sage/plot/graphics.py +++ b/src/sage/plot/graphics.py @@ -24,7 +24,7 @@ """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 Alex Clemesha # Copyright (C) 2006-2008 William Stein # Copyright (C) 2010 Jason Grout @@ -32,15 +32,13 @@ # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import zip -from six import integer_types import os from math import isnan -import sage.misc.misc +import sage.misc.verbose from sage.misc.temporary_file import tmp_filename from sage.misc.fast_methods import WithEqualityById from sage.structure.sage_object import SageObject @@ -80,7 +78,7 @@ def _parse_figsize(figsize): - ``figsize`` -- width or [width, height] in inches; if only the width is provided, the height is computed from matplotlib's default aspect ratio - OUPUT: + OUTPUT: - a pair of ``float``'s representing ``(width, height)`` @@ -510,6 +508,72 @@ def _get_axes_range_dict(self): self._axes_range = {} return self._axes_range + def set_flip(self, flip_x=None, flip_y=None): + """ + Set the flip options for this graphics object. + + INPUT: + + - ``flip_x`` -- boolean (default: ``None``); if not ``None``, set the + ``flip_x`` option to this value + - ``flip_y`` -- boolean (default: ``None``); if not ``None``, set the + ``flip_y`` option to this value + + EXAMPLES:: + + sage: L = line([(1, 0), (2, 3)]) + sage: L.set_flip(flip_y=True) + sage: L.flip() + (False, True) + sage: L.set_flip(True, False) + sage: L.flip() + (True, False) + """ + if flip_x is not None: + self._extra_kwds['flip_x'] = flip_x + if flip_y is not None: + self._extra_kwds['flip_y'] = flip_y + + def flip(self, flip_x=False, flip_y=False): + """ + Get the flip options and optionally mirror this graphics object. + + INPUT: + + - ``flip_x`` -- boolean (default: ``False``); if ``True``, replace the + current ``flip_x`` option by its opposite + - ``flip_y`` -- boolean (default: ``False``); if ``True``, replace the + current ``flip_y`` option by its opposite + + OUTPUT: a tuple containing the new flip options + + EXAMPLES: + + When called without arguments, this just returns the current flip + options:: + + sage: L = line([(1, 0), (2, 3)]) + sage: L.flip() + (False, False) + + Otherwise, the specified options are changed and the new options are + returned:: + + sage: L.flip(flip_y=True) + (False, True) + sage: L.flip(True, True) + (True, False) + """ + a = self._extra_kwds.get('flip_x', self.SHOW_OPTIONS['flip_x']) + b = self._extra_kwds.get('flip_y', self.SHOW_OPTIONS['flip_y']) + if flip_x: + a = not a + self._extra_kwds['flip_x'] = a + if flip_y: + b = not b + self._extra_kwds['flip_y'] = b + return (a, b) + def fontsize(self, s=None): """ Set the font size of axes labels and tick marks. @@ -1055,7 +1119,7 @@ def __radd__(self, other): sage: print(sum(v)) Graphics object consisting of 2 graphics primitives """ - if isinstance(other, integer_types) and other == 0: + if isinstance(other, int) and other == 0: return self raise TypeError @@ -1117,6 +1181,12 @@ def __add__(self, other): sage: p3._legend_opts {'shadow': False} + Flipped axes take precedence over non-flipped axes:: + + sage: p1 = plot(x, x, 0, 1, flip_x=True, flip_y=True) + sage: p2 = plot(x^2, x, 0, 1) + sage: [p._extra_kwds[k] for p in [p1 + p2, p2 + p1] for k in ['flip_x', 'flip_y']] + [True, True, True, True] """ if isinstance(other, int) and other == 0: return self @@ -1133,6 +1203,12 @@ def __add__(self, other): g._legend_colors = self._legend_colors + other._legend_colors g._legend_opts.update(self._legend_opts) g._legend_opts.update(other._legend_opts) + if 'flip_x' in self._extra_kwds and 'flip_x' in other._extra_kwds: + g._extra_kwds['flip_x'] = (self._extra_kwds['flip_x'] + or other._extra_kwds['flip_x']) + if 'flip_y' in self._extra_kwds and 'flip_y' in other._extra_kwds: + g._extra_kwds['flip_y'] = (self._extra_kwds['flip_y'] + or other._extra_kwds['flip_y']) if self.aspect_ratio()=='automatic': g.set_aspect_ratio(other.aspect_ratio()) elif other.aspect_ratio()=='automatic': @@ -1328,15 +1404,15 @@ def _set_scale(self, subplot, scale=None, base=None): if scale == 'linear': basex = basey = 10 elif scale == 'loglog': - subplot.set_xscale('log', basex=basex) - subplot.set_yscale('log', basey=basey) + subplot.set_xscale('log', base=basex) + subplot.set_yscale('log', base=basey) xscale = yscale = 'log' elif scale == 'semilogx': - subplot.set_xscale('log', basex=basex) + subplot.set_xscale('log', base=basex) basey = 10 xscale = 'log' elif scale == 'semilogy': - subplot.set_yscale('log', basey=basey) + subplot.set_yscale('log', base=basey) basex = 10 yscale = 'log' @@ -1355,6 +1431,7 @@ def _set_scale(self, subplot, scale=None, base=None): axes=None, axes_labels=None, axes_labels_size=None, axes_pad=None, base=None, scale=None, xmin=None, xmax=None, ymin=None, ymax=None, + flip_x=False, flip_y=False, # Figure options aspect_ratio=None, dpi=DEFAULT_DPI, fig_tight=True, figsize=None, fontsize=None, frame=False, @@ -1608,6 +1685,12 @@ def show(self, **kwds): - ``ymax`` -- ending y value in the rendered figure. + - ``flip_x`` -- (default: False) boolean. If True, flip the horizontal + axis. + + - ``flip_y`` -- (default: False) boolean. If True, flip the vertical + axis. + - ``typeset`` -- (default: ``"default"``) string. The type of font rendering that should be used for the text. The possible values are @@ -1742,6 +1825,14 @@ def show(self, **kwds): sage: G = plot_vector_field((2^x,y^2),(x,1,10),(y,1,100)) sage: G.show(scale='semilogx',base=2) + Flip the horizontal or vertical axis. + + :: + + sage: G = plot(x^3, -2, 3) + sage: G.show(flip_x=True) + sage: G.show(flip_y=True) + Add grid lines at the major ticks of the axes. :: @@ -2168,16 +2259,16 @@ def get_minmax_data(self): ymax = max(d['ymax'] for d in minmax_data) if isnan(xmin): xmin=0 - sage.misc.misc.verbose("xmin was NaN (setting to 0)", level=0) + sage.misc.verbose.verbose("xmin was NaN (setting to 0)", level=0) if isnan(xmax): xmax=0 - sage.misc.misc.verbose("xmax was NaN (setting to 0)", level=0) + sage.misc.verbose.verbose("xmax was NaN (setting to 0)", level=0) if isnan(ymin): ymin=0 - sage.misc.misc.verbose("ymin was NaN (setting to 0)", level=0) + sage.misc.verbose.verbose("ymin was NaN (setting to 0)", level=0) if isnan(ymax): ymax=0 - sage.misc.misc.verbose("ymax was NaN (setting to 0)", level=0) + sage.misc.verbose.verbose("ymax was NaN (setting to 0)", level=0) else: xmin = xmax = ymin = ymax = 0 @@ -2258,17 +2349,17 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), sage: subplot = Figure().add_subplot(111) sage: p._objects[0]._render_on_subplot(subplot) sage: p._matplotlib_tick_formatter(subplot, **d) - (, + (, , , - , - ) + , + ) """ # This function is created to refactor some code that is repeated # in the matplotlib function from matplotlib.ticker import (FixedLocator, Locator, LogFormatterMathtext, LogLocator, MaxNLocator, - MultipleLocator, NullLocator, OldScalarFormatter) + MultipleLocator, NullLocator, ScalarFormatter) x_locator, y_locator = ticks #---------------------- Location of x-ticks ---------------------# @@ -2324,7 +2415,7 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), if scale[0] == 'log': x_formatter = LogFormatterMathtext(base=base[0]) else: - x_formatter = OldScalarFormatter() + x_formatter = ScalarFormatter() elif x_formatter in SR: x_const = x_formatter x_formatter = FuncFormatter(lambda n,pos: @@ -2349,7 +2440,7 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), if scale[1] == 'log': y_formatter = LogFormatterMathtext(base=base[1]) else: - y_formatter = OldScalarFormatter() + y_formatter = ScalarFormatter() elif y_formatter in SR: y_const = y_formatter y_formatter = FuncFormatter(lambda n,pos: @@ -2512,6 +2603,7 @@ def matplotlib(self, filename=None, xmin=None, xmax=None, ymin=None, ymax=None, figsize=None, figure=None, sub=None, axes=None, axes_labels=None, axes_labels_size=None, + flip_x=False, flip_y=False, fontsize=None, frame=False, verify=True, aspect_ratio = None, gridlines=None, gridlinesstyle=None, @@ -2605,6 +2697,15 @@ def matplotlib(self, filename=None, sage: f = lambda x, y : (abs(cos((x + I * y) ** 4)) - 1) # long time sage: g = implicit_plot(f,(-4, 4),(-3, 3),linewidth=0.6) # long time sage: gm = g.matplotlib() # long time # without the patch, this goes BOOM -- er, TypeError + + If the axes are flipped, the limits of the axes get swapped:: + + sage: p = plot(2*x, 1, 2) + sage: sub, = p.matplotlib(flip_y=True, flip_x=True).axes + sage: xmin, xmax = sub.get_xlim() + sage: ymin, ymax = sub.get_ylim() + sage: xmin > xmax, ymin > ymax + (True, True) """ if not isinstance(ticks, (list, tuple)): ticks = (ticks, None) @@ -2686,10 +2787,10 @@ def matplotlib(self, filename=None, #---------------- Set the axes limits and scale ------------------# self.set_axes_range(xmin, xmax, ymin, ymax) d = self.get_axes_range() - xmin = d['xmin'] - xmax = d['xmax'] - ymin = d['ymin'] - ymax = d['ymax'] + xmin = d['xmax' if flip_x else 'xmin'] + xmax = d['xmin' if flip_x else 'xmax'] + ymin = d['ymax' if flip_y else 'ymin'] + ymax = d['ymin' if flip_y else 'ymax'] xscale, yscale, basex, basey = self._set_scale(subplot, scale=scale, base=base) @@ -2737,7 +2838,8 @@ def matplotlib(self, filename=None, color = lopts.pop('back_color', 'white') leg = subplot.legend(prop=prop, **lopts) if leg is None: - sage.misc.misc.warn("legend requested but no items are labeled") + from warnings import warn + warn("legend requested but no items are labeled") else: # color lframe = leg.get_frame() @@ -3300,12 +3402,12 @@ def description(self): EXAMPLES:: sage: print(polytopes.hypercube(2).plot().description()) - Polygon defined by 4 points: [(1.0, 1.0), (-1.0, 1.0), (-1.0, -1.0), (1.0, -1.0)] - Line defined by 2 points: [(-1.0, -1.0), (-1.0, 1.0)] - Line defined by 2 points: [(-1.0, -1.0), (1.0, -1.0)] - Line defined by 2 points: [(-1.0, 1.0), (1.0, 1.0)] + Polygon defined by 4 points: [(-1.0, -1.0), (1.0, -1.0), (1.0, 1.0), (-1.0, 1.0)] + Line defined by 2 points: [(-1.0, 1.0), (-1.0, -1.0)] + Line defined by 2 points: [(1.0, -1.0), (-1.0, -1.0)] Line defined by 2 points: [(1.0, -1.0), (1.0, 1.0)] - Point set defined by 4 point(s): [(-1.0, -1.0), (-1.0, 1.0), (1.0, -1.0), (1.0, 1.0)] + Line defined by 2 points: [(1.0, 1.0), (-1.0, 1.0)] + Point set defined by 4 point(s): [(1.0, -1.0), (1.0, 1.0), (-1.0, 1.0), (-1.0, -1.0)] """ data = [] for g in self: diff --git a/src/sage/plot/histogram.py b/src/sage/plot/histogram.py index 8dd68e2fee8..e0c608e23ed 100644 --- a/src/sage/plot/histogram.py +++ b/src/sage/plot/histogram.py @@ -1,8 +1,7 @@ """ Histograms """ - -#***************************************************************************** +# **************************************************************************** # Distributed under the terms of the GNU General Public License (GPL) # # This code is distributed in the hope that it will be useful, @@ -12,8 +11,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.plot.primitive import GraphicPrimitive from sage.plot.plot import minmax_data, Graphics from sage.misc.decorators import options @@ -87,6 +86,7 @@ def get_minmax_data(self): {'xmax': 4.0, 'xmin': 0, 'ymax': 2, 'ymin': 0} TESTS:: + sage: h = histogram([10,3,5], normed=True)[0] doctest:warning...: DeprecationWarning: the 'normed' option is deprecated. Use 'density' instead. diff --git a/src/sage/plot/line.py b/src/sage/plot/line.py index 7f13767ea7d..9081181208e 100644 --- a/src/sage/plot/line.py +++ b/src/sage/plot/line.py @@ -105,6 +105,7 @@ def _plot3d_options(self, options=None): sage: L = line([(1,1), (1,2), (2,2), (2,1)], linestyle=":") sage: L.plot3d() Traceback (most recent call last): + ... NotImplementedError: Invalid 3d line style: ':' """ if options is None: diff --git a/src/sage/plot/matrix_plot.py b/src/sage/plot/matrix_plot.py index cf8dc036d2f..768295b40e7 100644 --- a/src/sage/plot/matrix_plot.py +++ b/src/sage/plot/matrix_plot.py @@ -18,7 +18,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import -from six import iteritems from sage.plot.primitive import GraphicPrimitive from sage.misc.decorators import options, suboptions @@ -36,10 +35,12 @@ class MatrixPlot(GraphicPrimitive): the grid - ``xrange`` - tuple of 2 floats indicating range for horizontal direction - (number of columns in the matrix) + (number of columns in the matrix). If ``None``, the defaults are used as + indicated in :func:`matrix_plot`. - ``yrange`` - tuple of 2 floats indicating range for vertical direction - (number of rows in the matrix) + (number of rows in the matrix). If ``None``, the defaults are used as + indicated in :func:`matrix_plot`. - ``options`` - dict of valid plot options to pass to constructor @@ -78,8 +79,8 @@ def __init__(self, xy_data_array, xrange, yrange, options): EXAMPLES:: sage: M = matrix_plot([[mod(i,5)^j for i in range(5)] for j in range(1,6)], cmap='jet') - sage: M[0].xrange - (0, 5) + sage: M[0].get_minmax_data() + {'xmax': 4.5, 'xmin': -0.5, 'ymax': 4.5, 'ymin': -0.5} sage: M[0].options()['cmap'] 'jet' sage: M[0].xy_array_row @@ -104,21 +105,27 @@ def get_minmax_data(self): sage: m = matrix_plot(matrix([[1,3,5,1],[2,4,5,6],[1,3,5,7]]))[0] sage: list(sorted(m.get_minmax_data().items())) - [('xmax', 3.5), ('xmin', -0.5), ('ymax', -0.5), ('ymin', 2.5)] + [('xmax', 3.5), ('xmin', -0.5), ('ymax', 2.5), ('ymin', -0.5)] + TESTS: + We verify that :trac:`27891` is fixed:: + + sage: p = matrix_plot(identity_matrix(5)) + point((2, 2), zorder=1) + sage: sorted(p.get_minmax_data().items()) + [('xmax', 4.5), ('xmin', -0.5), ('ymax', 4.5), ('ymin', -0.5)] """ from sage.plot.plot import minmax_data - limits= minmax_data(self.xrange, self.yrange, dict=True) - if self.options()['origin']!='lower': - # flip y-axis so that the picture looks correct. - limits['ymin'],limits['ymax']=limits['ymax'],limits['ymin'] - - # center the matrix so that, for example, the square representing the - # (0,0) entry is centered on the origin. - for k, v in iteritems(limits): - limits[k] -= 0.5 - return limits + xrange = self.xrange + yrange = self.yrange + # if xrange/yrange are not specified, add offset to the matrix so that, + # for example, the square representing the (0,0) entry is centered on + # the origin. + if not xrange: + xrange = (-.5, self.xy_array_col -.5) + if not yrange: + yrange = (-.5, self.xy_array_row -.5) + return minmax_data(xrange, yrange, dict=True) def _allowed_options(self): """ @@ -142,7 +149,7 @@ def _allowed_options(self): 'norm': "The normalization function", 'vmin': "The minimum value", 'vmax': "The maximum value", - 'origin': "If 'lower', draw the matrix with the first row on the bottom of the graph", + 'flip_y': "If False, draw the matrix with the first row on the bottom of the graph", 'subdivisions': "If True, draw subdivisions of the matrix", 'subdivision_options': "Options (boundaries and style) of the subdivisions"} @@ -167,7 +174,7 @@ def _render_on_subplot(self, subplot): """ options = self.options() cmap = get_cmap(options.pop('cmap',None)) - origin=options['origin'] + flip_y = options['flip_y'] norm=options['norm'] @@ -175,6 +182,7 @@ def _render_on_subplot(self, subplot): import matplotlib norm=matplotlib.colors.NoNorm() + lim=self.get_minmax_data() if options['subdivisions']: subdiv_options=options['subdivision_options'] if isinstance(subdiv_options['boundaries'], (list, tuple)): @@ -194,29 +202,33 @@ def _render_on_subplot(self, subplot): # Make line objects for subdivisions from .line import line2d - lim=self.get_minmax_data() # First draw horizontal lines representing row subdivisions for y in rowsub: - l=line2d([(lim['xmin'],y-0.5), (lim['xmax'],y-0.5)], **rowstyle)[0] + y = lim['ymin'] + ((lim['ymax'] - lim['ymin']) + * y / self.xy_array_row) + l = line2d([(lim['xmin'], y), (lim['xmax'], y)], **rowstyle)[0] l._render_on_subplot(subplot) for x in colsub: - l=line2d([(x-0.5, lim['ymin']), (x-0.5, lim['ymax'])], **colstyle)[0] + x = lim['xmin'] + ((lim['xmax'] - lim['xmin']) + * x / self.xy_array_col) + l = line2d([(x, lim['ymin']), (x, lim['ymax'])], **colstyle)[0] l._render_on_subplot(subplot) if hasattr(self.xy_data_array, 'tocoo'): # Sparse matrix -- use spy opts=options.copy() - for opt in ['vmin', 'vmax', 'norm', 'origin','subdivisions','subdivision_options', - 'colorbar','colorbar_options']: + for opt in ['vmin', 'vmax', 'norm', 'flip_y', 'subdivisions', + 'subdivision_options', 'colorbar', 'colorbar_options']: del opts[opt] - if origin=='lower': - subplot.spy(self.xy_data_array.tocsr()[::-1], **opts) - else: - subplot.spy(self.xy_data_array, **opts) + subplot.spy(self.xy_data_array, **opts) else: + extent = (lim['xmin'], lim['xmax'], + lim['ymax' if flip_y else 'ymin'], + lim['ymin' if flip_y else 'ymax']) opts = dict(cmap=cmap, interpolation='nearest', aspect='equal', norm=norm, vmin=options['vmin'], vmax=options['vmax'], - origin=origin,zorder=options.get('zorder',None)) + origin=('upper' if flip_y else 'lower'), + extent=extent, zorder=options.get('zorder')) image = subplot.imshow(self.xy_data_array, **opts) if options.get('colorbar', False): @@ -225,9 +237,9 @@ def _render_on_subplot(self, subplot): cax,kwds=colorbar.make_axes_gridspec(subplot,**colorbar_options) colorbar.Colorbar(cax, image, **kwds) - if origin == 'upper': + if flip_y: subplot.xaxis.tick_top() - elif origin == 'lower': + else: subplot.xaxis.tick_bottom() subplot.xaxis.set_ticks_position('both') #only tick marks, not tick labels @@ -236,9 +248,9 @@ def _render_on_subplot(self, subplot): @suboptions('colorbar', orientation='vertical', format=None) @suboptions('subdivision',boundaries=None, style=None) @options(aspect_ratio=1, axes=False, cmap='Greys', colorbar=False, - frame=True, marker='.', norm=None, origin='upper', + frame=True, marker='.', norm=None, flip_y=True, subdivisions=False, ticks_integer=True, vmin=None, vmax=None) -def matrix_plot(mat, **options): +def matrix_plot(mat, xrange=None, yrange=None, **options): r""" A plot of a given matrix or 2D array. @@ -262,6 +274,21 @@ def matrix_plot(mat, **options): - ``mat`` - a 2D matrix or array + - ``xrange`` - (default: None) tuple of the horizontal extent + ``(xmin, xmax)`` of the bounding box in which to draw the matrix. The + image is stretched individually along x and y to fill the box. + + If None, the extent is determined by the following conditions. Matrix + entries have unit size in data coordinates. Their centers are on integer + coordinates, and their center coordinates range from 0 to columns-1 + horizontally and from 0 to rows-1 vertically. + + If the matrix is sparse, this keyword is ignored. + + - ``yrange`` - (default: None) tuple of the vertical extent + ``(ymin, ymax)`` of the bounding box in which to draw the matrix. + See ``xrange`` for details. + The following input must all be passed in as named parameters, if default not used: @@ -299,9 +326,9 @@ def matrix_plot(mat, **options): - ``vmax`` - The maximum value (values above this are set to this value) - - ``origin`` - If 'upper' (default), the first row of the matrix - is on the top of the graph. If 'lower', the first row is on the - bottom of the graph. + - ``flip_y`` - (default: True) boolean. If False, the first row of the + matrix is on the bottom of the graph. Otherwise, the first row is on the + top of the graph. - ``subdivisions`` - If True, plot the subdivisions of the matrix as lines. @@ -322,6 +349,11 @@ def matrix_plot(mat, **options): sage: matrix_plot(matrix([[1,3,5,1],[2,4,5,6],[1,3,5,7]])) Graphics object consisting of 1 graphics primitive + .. PLOT:: + + P = matrix_plot(matrix([[1,3,5,1],[2,4,5,6],[1,3,5,7]])) + sphinx_plot(P) + Here we make a random matrix over `\RR` and use ``cmap='hsv'`` to color the matrix elements different RGB colors:: @@ -371,9 +403,26 @@ def matrix_plot(mat, **options): Generally matrices are plotted with the (0,0) entry in the upper left. However, sometimes if we are plotting an image, we'd like the (0,0) entry to be in the lower left. We can do that with the - ``origin`` argument:: + ``flip_y`` argument:: - sage: matrix_plot(identity_matrix(100), origin='lower') + sage: matrix_plot(identity_matrix(100), flip_y=False) + Graphics object consisting of 1 graphics primitive + + A custom bounding box in which to draw the matrix can be specified using + the ``xrange`` and ``yrange`` arguments:: + + sage: P = matrix_plot(identity_matrix(10), xrange=(0, pi), yrange=(-pi, 0)) + sage: P + Graphics object consisting of 1 graphics primitive + sage: P.get_minmax_data() + {'xmax': 3.14159..., 'xmin': 0.0, 'ymax': 0.0, 'ymin': -3.14159...} + + If the horizontal and vertical dimension of the image are very different, + the default ``aspect_ratio=1`` may be unsuitable and can be changed to + ``automatic``:: + + sage: matrix_plot(random_matrix(RDF, 2, 2), (-100, 100), (0, 1), + ....: aspect_ratio='automatic') Graphics object consisting of 1 graphics primitive Another random plot, but over `\GF{389}`:: @@ -455,11 +504,11 @@ def matrix_plot(mat, **options): A plot title can be added to the matrix plot.:: - sage: matrix_plot(identity_matrix(50), origin='lower', title='not identity') + sage: matrix_plot(identity_matrix(50), flip_y=False, title='not identity') Graphics object consisting of 1 graphics primitive - The title position is adjusted upwards if the ``origin`` keyword is set - to ``"upper"`` (this is the default).:: + The title position is adjusted upwards if the ``flip_y`` keyword is set + to True (this is the default).:: sage: matrix_plot(identity_matrix(50), title='identity') Graphics object consisting of 1 graphics primitive @@ -496,7 +545,20 @@ def matrix_plot(mat, **options): sage: P = matrix_plot(random_matrix(RDF, 5)) sage: P.aspect_ratio() 1 + + The origin keyword is deprecated:: + + sage: matrix_plot(identity_matrix(100), origin='lower') + doctest:...: DeprecationWarning: the option 'origin' is replaced by 'flip_y' + See https://trac.sagemath.org/27891 for details. + Graphics object consisting of 1 graphics primitive """ + if 'origin' in options: + from sage.misc.superseded import deprecation + deprecation(27891, "the option 'origin' is replaced by 'flip_y'") + options['flip_y'] = (options['origin'] != 'lower') + del options['origin'] + import numpy as np import scipy.sparse as scipysparse from sage.plot.all import Graphics @@ -535,17 +597,26 @@ def matrix_plot(mat, **options): if len(xy_data_array.shape) < 2: raise TypeError("mat must be a Matrix or a two dimensional array") - xrange = (0, xy_data_array.shape[1]) - yrange = (0, xy_data_array.shape[0]) + # Sparse matrices are not plotted with imshow, so extent is not supported, + # hence we ignore custom bounds. + if sparse: + xrange = None + yrange = None + if xrange: + xrange = tuple(float(v) for v in xrange) + if yrange: + yrange = tuple(float(v) for v in yrange) if options['subdivisions'] and options['subdivision_options']['boundaries'] is None: options['subdivision_options']['boundaries']=orig_mat.get_subdivisions() # Custom position the title. Otherwise it overlaps with tick labels - if options['origin'] == 'upper' and 'title_pos' not in options: + if options['flip_y'] and 'title_pos' not in options: options['title_pos'] = (0.5, 1.05) g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options)) + # Keep flip_y for _render_on_subplot + options['flip_y'] = g._extra_kwds['flip_y'] g.add_primitive(MatrixPlot(xy_data_array, xrange, yrange, options)) return g diff --git a/src/sage/plot/multigraphics.py b/src/sage/plot/multigraphics.py index 3135cebe2dd..72930ca4cfb 100644 --- a/src/sage/plot/multigraphics.py +++ b/src/sage/plot/multigraphics.py @@ -1294,16 +1294,16 @@ def position(self, index): sage: g1 = plot(sin(x), (x, -pi, pi)) sage: g2 = circle((0,1), 1.) sage: G = graphics_array([g1, g2]) - sage: G.position(0) # tol 1.0e-13 - (0.028906249999999998, - 0.038541666666666696, - 0.45390624999999996, - 0.9229166666666667) - sage: G.position(1) # tol 1.0e-13 - (0.5171874999999999, - 0.038541666666666696, - 0.45390624999999996, - 0.9229166666666667) + sage: G.position(0) # tol 5.0e-3 + (0.025045451349937315, + 0.03415488992713045, + 0.4489880779745068, + 0.9345951100728696) + sage: G.position(1) # tol 5.0e-3 + (0.5170637412999687, + 0.20212705964722733, + 0.4489880779745068, + 0.5986507706326758) """ if not self._positions: diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 1000ef2380a..0939fd26daa 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -573,8 +573,6 @@ def f(x): return (x-3)*(x-5)*(x-7)+40 # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range -from six import iteritems from functools import reduce @@ -585,11 +583,11 @@ def f(x): return (x-3)*(x-5)*(x-7)+40 #DEFAULT_FIGSIZE=(6, 3.70820393249937) EMBEDDED_MODE = False -import sage.misc.misc +import sage.misc.verbose from sage.arith.srange import srange from sage.misc.randstate import current_randstate #for plot adaptive refinement -from math import sin, cos, pi #for polar_plot +from math import sin, cos, pi, log, exp #for polar_plot and log scaling from sage.ext.fast_eval import fast_float, is_fast_float @@ -1461,25 +1459,26 @@ def h2(x): return -abs(sqrt(x**3 - 1)) .. PLOT:: - g = plot(sin(x), (x,0,10), plot_points=20, linestyle='', marker='.') + g = plot(sin(x), (x, 0, 10), plot_points=20, linestyle='', marker='.') sphinx_plot(g) The marker can be a TeX symbol as well:: - sage: plot(sin(x), (x,0,10), plot_points=20, linestyle='', marker=r'$\checkmark$') + sage: plot(sin(x), (x, 0, 10), plot_points=20, linestyle='', marker=r'$\checkmark$') Graphics object consisting of 1 graphics primitive .. PLOT:: - g = plot(sin(x), (x,0,10), plot_points=20, linestyle='', marker=r'$\checkmark$') + g = plot(sin(x), (x, 0, 10), plot_points=20, linestyle='', marker=r'$\checkmark$') sphinx_plot(g) Sage currently ignores points that cannot be evaluated :: + sage: from sage.misc.verbose import set_verbose sage: set_verbose(-1) - sage: plot(-x*log(x), (x,0,1)) # this works fine since the failed endpoint is just skipped. + sage: plot(-x*log(x), (x, 0, 1)) # this works fine since the failed endpoint is just skipped. Graphics object consisting of 1 graphics primitive sage: set_verbose(0) @@ -1489,42 +1488,42 @@ def h2(x): return -abs(sqrt(x**3 - 1)) :: sage: set_verbose(-1) - sage: plot(x^(1/3), (x,-1,1)) + sage: plot(x^(1/3), (x, -1, 1)) Graphics object consisting of 1 graphics primitive sage: set_verbose(0) .. PLOT:: set_verbose(-1) - g = plot(x**(1.0/3.0), (x,-1,1)) + g = plot(x**(1.0/3.0), (x, -1, 1)) sphinx_plot(g) set_verbose(0) - Plotting the real cube root function for negative input - requires avoiding the complex numbers one would usually get. - The easiest way is to use absolute value:: + Plotting the real cube root function for negative input requires avoiding + the complex numbers one would usually get. The easiest way is to use + :class:`real_nth_root(x, n)` :: - sage: plot(sign(x)*abs(x)^(1/3), (x,-1,1)) + sage: plot(real_nth_root(x, 3), (x, -1, 1)) Graphics object consisting of 1 graphics primitive .. PLOT:: - g = plot(sign(x)*abs(x)**(1.0/3.0), (x,-1,1)) + g = plot(real_nth_root(x, 3), (x, -1, 1)) sphinx_plot(g) - We can also use the following:: + We can also get the same plot in the following way:: - sage: plot(sign(x)*(x*sign(x))^(1/3), (x,-4,4)) + sage: plot(sign(x)*abs(x)^(1/3), (x, -1, 1)) Graphics object consisting of 1 graphics primitive .. PLOT:: - g = plot(sign(x)*(x*sign(x))**(1.0/3.0), (x,-4,4)) + g = plot(sign(x)*abs(x)**(1./3.), (x, -1, 1)) sphinx_plot(g) - A way that points to how to plot other functions without - symbolic variants is using lambda functions:: + A way to plot other functions without symbolic variants is to use lambda + functions:: sage: plot(lambda x : RR(x).nth_root(3), (x,-1, 1)) Graphics object consisting of 1 graphics primitive @@ -1947,6 +1946,8 @@ def f(x): return (floor(x)+0.5) / (1-(x-0.5)**2) elif 'color' in kwds: kwds['rgbcolor'] = kwds.pop('color', (0,0,1)) # take blue as default ``rgbcolor`` G_kwds = Graphics._extract_kwds_for_show(kwds, ignore=['xmin', 'xmax']) + if 'scale' in G_kwds: + kwds['scale'] = G_kwds['scale'] # pass scaling information to _plot too original_opts = kwds.pop('__original_opts', {}) do_show = kwds.pop('show',False) @@ -1995,7 +1996,7 @@ def f(x): return (floor(x)+0.5) / (1-(x-0.5)**2) xmax = kwds.pop('xmax', 1) G = _plot(funcs, (xmin, xmax), *args, **kwds) else: - sage.misc.misc.verbose("there were %s extra arguments (besides %s)" % (n, funcs), level=0) + sage.misc.verbose.verbose("there were %s extra arguments (besides %s)" % (n, funcs), level=0) G._set_extra_kwds(G_kwds) if do_show: @@ -2275,22 +2276,45 @@ def golden_rainbow(i,lightness=0.4): initial_points = reduce(lambda a,b: a+b, [[x - epsilon, x + epsilon] for x in excluded_points], []) - data = generate_plot_points(f, xrange, plot_points, + else: + initial_points = None + + # If we are a log scale plot on the x axis, do a change of variables + # so we sample the range in log scale + is_log_scale = ('scale' in options.keys() and + not parametric and + options['scale'] in ['loglog', 'semilogx']) + if is_log_scale: + f_exp = lambda x: f(exp(x)) + log_xrange = (log(xrange[0]), log(xrange[1])) + if initial_points is None: + log_initial_points = None + else: + log_initial_points = [log(x) for x in initial_points] + data = generate_plot_points(f_exp, log_xrange, plot_points, adaptive_tolerance, adaptive_recursion, - randomize, initial_points) + randomize, log_initial_points) + average_distance_between_points = abs(log_xrange[1] - log_xrange[0])/plot_points else: data = generate_plot_points(f, xrange, plot_points, adaptive_tolerance, adaptive_recursion, - randomize) - + randomize, initial_points) + average_distance_between_points = abs(xrange[1] - xrange[0])/plot_points for i in range(len(data)-1): # If the difference between consecutive x-values is more than - # 2 times the difference between two consecutive plot points, then + # 2 times the average difference between two consecutive plot points, then # add an exclusion point. - if abs(data[i+1][0] - data[i][0]) > 2*abs(xmax - xmin)/plot_points: + if abs(data[i+1][0] - data[i][0]) > 2*average_distance_between_points: excluded_points.append((data[i][0] + data[i+1][0])/2) + # If we did a change in variables, undo it now + if is_log_scale: + for i,(a,fa) in enumerate(data): + data[i] = (exp(a), fa) + for i,p in enumerate(excluded_points): + excluded_points[i] = exp(p) + if parametric: # We need the original x-values to be able to exclude points in parametric plots exclude_data = data @@ -2327,7 +2351,7 @@ def golden_rainbow(i,lightness=0.4): else: fstr = 'min' msg = "WARNING: You use the built-in function %s for filling. You probably wanted the string '%s'." % (fstr, fstr) - sage.misc.misc.verbose(msg, level=0) + sage.misc.verbose.verbose(msg, level=0) if not is_fast_float(fill): fill_f = fast_float(fill, expect_one_var=True) else: @@ -2999,9 +3023,9 @@ def list_plot(data, plotjoined=False, **kwargs): "and 'y' against each other, use 'list_plot(list(zip(x,y)))'.") if isinstance(data, dict): if plotjoined: - list_data = sorted(list(iteritems(data))) + list_data = sorted(data.items()) else: - list_data = list(iteritems(data)) + list_data = list(data.items()) return list_plot(list_data, plotjoined=plotjoined, **kwargs) try: from sage.rings.all import RDF @@ -3124,6 +3148,20 @@ def plot_semilogx(funcs, *args, **kwds): g = plot_semilogx(exp, (1,10), base=2) # with base 2 sphinx_plot(g) + :: + + sage: s = var('s') # Samples points logarithmically so graph is smooth + sage: f = 4000000/(4000000 + 4000*s*i - s*s) + sage: plot_semilogx(20*log(abs(f), 10), (s, 1, 1e6)) + Graphics object consisting of 1 graphics primitive + + .. PLOT:: + + s = var('s') # Samples points logarithmically so graph is smooth + f = 4000000/(4000000 + 4000*s*i - s*s) + g = plot_semilogx(20*log(abs(f), 10), (s, 1, 1e6)) + sphinx_plot(g) + """ return plot(funcs, *args, scale='semilogx', **kwds) @@ -3770,12 +3808,12 @@ def adaptive_refinement(f, p1, p2, adaptive_tolerance=0.01, adaptive_recursion=5 try: y = float(f(x)) if str(y) in ['nan', 'NaN', 'inf', '-inf']: - sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, x),1) + sage.misc.verbose.verbose("%s\nUnable to compute f(%s)"%(msg, x),1) # give up for this branch return [] except (ZeroDivisionError, TypeError, ValueError, OverflowError) as msg: - sage.misc.misc.verbose("%s\nUnable to compute f(%s)"%(msg, x), 1) + sage.misc.verbose.verbose("%s\nUnable to compute f(%s)"%(msg, x), 1) # give up for this branch return [] @@ -3898,12 +3936,12 @@ def generate_plot_points(f, xrange, plot_points=5, adaptive_tolerance=0.01, adap data[i] = (float(xi), float(f(xi))) if str(data[i][1]) in ['nan', 'NaN', 'inf', '-inf']: msg = "Unable to compute f(%s)" % xi - sage.misc.misc.verbose(msg, 1) + sage.misc.verbose.verbose(msg, 1) exceptions += 1 exception_indices.append(i) except (ArithmeticError, TypeError, ValueError) as m: - sage.misc.misc.verbose("%s\nUnable to compute f(%s)" % (m, xi), 1) + sage.misc.verbose.verbose("%s\nUnable to compute f(%s)" % (m, xi), 1) if i == 0: # Given an error for left endpoint, try to move it in slightly for j in range(1, 99): @@ -3957,7 +3995,7 @@ def generate_plot_points(f, xrange, plot_points=5, adaptive_tolerance=0.01, adap i += 1 if (len(data) == 0 and exceptions > 0) or exceptions > 10: - sage.misc.misc.verbose("WARNING: When plotting, failed to evaluate function at %s points." % exceptions, level=0) - sage.misc.misc.verbose("Last error message: '%s'" % msg, level=0) + sage.misc.verbose.verbose("WARNING: When plotting, failed to evaluate function at %s points." % exceptions, level=0) + sage.misc.verbose.verbose("Last error message: '%s'" % msg, level=0) return data diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index 8bda9fa0e3c..047ded4db2d 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -11,6 +11,8 @@ AUTHORS: - Paul Masson (2016): Three.js support +- Joshua Campbell (2020): Three.js animation support + .. TODO:: finish integrating tachyon -- good default lights, camera @@ -39,8 +41,8 @@ import sys import zipfile from functools import reduce +from io import StringIO from random import randint -from six.moves import cStringIO as StringIO from sage.misc.misc import sage_makedirs from sage.misc.temporary_file import tmp_filename @@ -362,26 +364,89 @@ cdef class Graphics3d(SageObject): sage: sphere(online=True)._rich_repr_threejs() OutputSceneThreejs container + + TESTS:: + + sage: js = '// animation.js' + sage: css = '/* animation.css */' + sage: html = '' + sage: d = dodecahedron() + sage: i = icosahedron() + sage: g1 = animate([d]).interactive() + sage: g2 = animate([d, i]).interactive() + + Animation files are only included when at least 2 frames are present:: + + sage: str = g1._rich_repr_threejs(online=True).html.get_str() + sage: (js in str) or (css in str) or (html in str) + False + sage: str = g2._rich_repr_threejs(online=True).html.get_str() + sage: (js in str) and (css in str) and (html in str) + True + + Animation can be explicitly disabled by setting animate=False:: + + sage: str = g2._rich_repr_threejs(online=True, animate=False).html.get_str() + sage: (js in str) or (css in str) or (html in str) + False + + Animation CSS and HTML are not included when animation_controls=False:: + + sage: str = g2._rich_repr_threejs(online=True, animation_controls=False).html.get_str() + sage: js in str + True + sage: (css in str) or (html in str) + False + """ options = self._process_viewing_options(kwds) - # Threejs specific options - options.setdefault('axes_labels', ['x','y','z']) - options.setdefault('decimals', 2) options.setdefault('online', False) - options.setdefault('projection', 'perspective') - if options['projection'] not in ['perspective', 'orthographic']: + + js_options = {} # options passed to Three.js template + + js_options['animate'] = options.get('animate', True) + js_options['animationControls'] = options.get('animation_controls', True) + js_options['aspectRatio'] = options.get('aspect_ratio', [1,1,1]) + js_options['autoPlay'] = options.get('auto_play', True) + js_options['axes'] = options.get('axes', False) + js_options['axesLabels'] = options.get('axes_labels', ['x','y','z']) + js_options['decimals'] = options.get('decimals', 2) + js_options['delay'] = options.get('delay', 20) + js_options['frame'] = options.get('frame', True) + js_options['loop'] = options.get('loop', True) + js_options['projection'] = options.get('projection', 'perspective') + js_options['viewpoint'] = options.get('viewpoint', False) + + if js_options['projection'] not in ['perspective', 'orthographic']: import warnings - warnings.warn('projection={} is not supported; using perspective'.format(options['projection'])) - options['projection'] = 'perspective' + warnings.warn('projection={} is not supported; using perspective'.format(js_options['projection'])) + js_options['projection'] = 'perspective' + # Normalization of options values for proper JSONing - options['aspect_ratio'] = [float(i) for i in options['aspect_ratio']] - options['decimals'] = int(options['decimals']) + js_options['aspectRatio'] = [float(i) for i in js_options['aspectRatio']] + js_options['decimals'] = int(js_options['decimals']) + js_options['delay'] = int(js_options['delay']) + + if js_options['viewpoint']: + if len(js_options['viewpoint']) != 2 or len(js_options['viewpoint'][0]) != 3: + import warnings + warnings.warn('viewpoint must be of the form [[x,y,z],angle]') + js_options['viewpoint'] = False + else: + if type(js_options['viewpoint']) is tuple: + js_options['viewpoint'] = list(js_options['viewpoint']) + if type(js_options['viewpoint'][0]) is tuple: + js_options['viewpoint'][0] = list(js_options['viewpoint'][0]) + js_options['viewpoint'][0] = [float(i) for i in js_options['viewpoint'][0]] + js_options['viewpoint'][1] = float(js_options['viewpoint'][1]) - if not options['frame']: - options['axes_labels'] = False + if not js_options['frame']: + js_options['axesLabels'] = False from sage.repl.rich_output import get_display_manager scripts = get_display_manager().threejs_scripts(options['online']) + styles = '' + extra_html = '' b = self.bounding_box() bounds = '[{{"x":{}, "y":{}, "z":{}}}, {{"x":{}, "y":{}, "z":{}}}]'.format( @@ -393,63 +458,46 @@ cdef class Graphics3d(SageObject): ambient = '{{"color":"{}"}}'.format(Color(.5,.5,.5).html_color()) import json - from sage.plot.plot3d.shapes import arrow3d - points, lines, texts = [], [], [] - - if not hasattr(self, 'all'): - self += Graphics3d() - for p in self.flatten().all: - if hasattr(p, 'loc'): - color = p._extra_kwds.get('color', 'blue') - opacity = float(p._extra_kwds.get('opacity', 1)) - points.append('{{"point":{}, "size":{}, "color":"{}", "opacity":{}}}'.format( - json.dumps(p.loc), p.size, color, opacity)) - if hasattr(p, 'points'): - color = p._extra_kwds.get('color', 'blue') - opacity = float(p._extra_kwds.get('opacity', 1)) - thickness = p._extra_kwds.get('thickness', 1) - lines.append('{{"points":{}, "color":"{}", "opacity":{}, "linewidth":{}}}'.format( - json.dumps(p.points), color, opacity, thickness)) - if hasattr(p, '_trans'): - m = p.get_transformation().get_matrix() - t = (m[0,3], m[1,3], m[2,3]) - if hasattr(p.all[0], 'points'): - translated = [[sum(x) for x in zip(t,u)] for u in p.all[0].points] - width = .5 * p.all[0].thickness - color = p.all[0].texture.color - opacity = p.all[0].texture.opacity - self += arrow3d(translated[0], translated[1], width=width, color=color, opacity=opacity) - if hasattr(p.all[0], 'string'): - texts.append('{{"text":"{}", "x":{}, "y":{}, "z":{}}}'.format( - p.all[0].string, t[0], t[1], t[2])) - - points = '[' + ','.join(points) + ']' - lines = '[' + ','.join(lines) + ']' - texts = '[' + ','.join(texts) + ']' - - surfaces = self.json_repr(self.default_render_params()) - surfaces = flatten_list(surfaces) - surfaces = '[' + ','.join(surfaces) + ']' + + reprs = {'point': [], 'line': [], 'text': [], 'surface': []} + frame_count = 0 + for kind, desc in self.threejs_repr(self.default_render_params()): + reprs[kind].append(desc) + keyframe = int(desc.get('keyframe', -1)) + frame_count = max(frame_count, keyframe + 1) + reprs = {kind: json.dumps(descs) for kind, descs in reprs.items()} from sage.env import SAGE_EXTCODE with open(os.path.join( SAGE_EXTCODE, 'threejs', 'threejs_template.html')) as f: html = f.read() + js_options['animate'] = js_options['animate'] and frame_count > 1 + if js_options['animate']: + if js_options['animationControls']: + with open(os.path.join(SAGE_EXTCODE, 'threejs', 'animation.css')) as f: + css = f.read() + css = css.replace('SAGE_FRAME_COUNT', str(frame_count)) + styles += '' + with open(os.path.join(SAGE_EXTCODE, 'threejs', 'animation.html')) as f: + extra_html += f.read() + with open(os.path.join(SAGE_EXTCODE, 'threejs', 'animation.js')) as f: + extra_html += '' + html = html.replace('SAGE_SCRIPTS', scripts) - js_options = dict((key, options[key]) for key in - ['aspect_ratio', 'axes', 'axes_labels', 'decimals', 'frame', 'projection']) + html = html.replace('SAGE_STYLES', styles) + html = html.replace('SAGE_EXTRA_HTML', extra_html) html = html.replace('SAGE_OPTIONS', json.dumps(js_options)) html = html.replace('SAGE_BOUNDS', bounds) html = html.replace('SAGE_LIGHTS', lights) html = html.replace('SAGE_AMBIENT', ambient) - html = html.replace('SAGE_TEXTS', str(texts)) - html = html.replace('SAGE_POINTS', str(points)) - html = html.replace('SAGE_LINES', str(lines)) - html = html.replace('SAGE_SURFACES', str(surfaces)) + html = html.replace('SAGE_TEXTS', str(reprs['text'])) + html = html.replace('SAGE_POINTS', str(reprs['point'])) + html = html.replace('SAGE_LINES', str(reprs['line'])) + html = html.replace('SAGE_SURFACES', str(reprs['surface'])) from sage.repl.rich_output.output_catalog import OutputSceneThreejs - return OutputSceneThreejs(html); + return OutputSceneThreejs(html) def __str__(self): """ @@ -1158,6 +1206,21 @@ end_scene""" % (render_params.antialiasing, """ return [] + def threejs_repr(self, render_params): + """ + A flat list of ``(kind, desc)`` tuples where ``kind`` is one of: + 'point', 'line', 'text', or 'surface'; and where ``desc`` is a dictionary + describing a point, line, text, or surface. + + EXAMPLES:: + + sage: G = sage.plot.plot3d.base.Graphics3d() + sage: G.threejs_repr(G.default_render_params()) + [] + + """ + return [] + def texture_set(self): """ Often the textures of a 3d file format are kept separate from the @@ -1584,6 +1647,8 @@ end_scene""" % (render_params.antialiasing, - a Sage object file (of type ``.sobj``) that you can load back later (a pickle), + - an HTML file depicting the graphic using the Three.js viewer, + - a data file (of type: X3D, STL, AMF, PLY) for export and use in other software. @@ -1629,6 +1694,12 @@ end_scene""" % (render_params.antialiasing, sage: open(f).read().splitlines()[7] "" + + Producing a Three.js-based HTML file:: + + sage: f = tmp_filename(ext='.html') + sage: G.save(f, frame=False, online=True) + """ ext = os.path.splitext(filename)[1].lower() if ext == '' or ext == '.sobj': @@ -1652,6 +1723,8 @@ end_scene""" % (render_params.antialiasing, elif ext == '.ply': with open(filename, 'w') as outfile: outfile.write(self.ply_ascii_string()) + elif ext == '.html': + self._rich_repr_threejs(**kwds).html.save_as(filename) else: raise ValueError('filetype {} not supported by save()'.format(ext)) @@ -1751,11 +1824,11 @@ end_scene""" % (render_params.antialiasing, sage: Q = P.plot().all[-1] sage: print(Q.stl_ascii_string().splitlines()[:7]) ['solid surface', - 'facet normal 0.8506508083520398 -0.0 0.5257311121191338', + 'facet normal 0.0 0.5257311121191338 0.8506508083520399', ' outer loop', - ' vertex 1.2360679774997898 -0.4721359549995796 0.0', - ' vertex 1.2360679774997898 0.4721359549995796 0.0', - ' vertex 0.7639320225002102 0.7639320225002102 0.7639320225002102', + ' vertex -0.7639320225002102 0.7639320225002102 0.7639320225002102', + ' vertex -0.4721359549995796 0.0 1.2360679774997898', + ' vertex 0.4721359549995796 0.0 1.2360679774997898', ' endloop'] """ from sage.modules.free_module import FreeModule @@ -2143,6 +2216,31 @@ class Graphics3dGroup(Graphics3d): """ return [g.jmol_repr(render_params) for g in self.all] + def threejs_repr(self, render_params): + r""" + The three.js representation of a group is the concatenation of the + representations of its objects. + + EXAMPLES:: + + sage: G = point3d((1,2,3)) + point3d((4,5,6)) + line3d([(1,2,3), (4,5,6)]) + sage: G.threejs_repr(G.default_render_params()) + [('point', + {'color': '#6666ff', 'opacity': 1.0, 'point': (1.0, 2.0, 3.0), 'size': 5.0}), + ('point', + {'color': '#6666ff', 'opacity': 1.0, 'point': (4.0, 5.0, 6.0), 'size': 5.0}), + ('line', + {'color': '#6666ff', + 'linewidth': 1.0, + 'opacity': 1.0, + 'points': [(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)]})] + + """ + reprs = [] + for g in self.all: + reprs += g.threejs_repr(render_params) + return reprs + def stl_binary_repr(self, render_params): r""" The stl binary representation of a group is simply the @@ -2399,6 +2497,29 @@ class TransformGroup(Graphics3dGroup): render_params.pop_transform() return rep + def threejs_repr(self, render_params): + r""" + Transformations for three.js are applied at the leaf nodes. + + EXAMPLES:: + + sage: G = point3d((1,2,3)) + point3d((4,5,6)) + sage: G = G.translate(-1, -2, -3).scale(10) + sage: G.threejs_repr(G.default_render_params()) + [('point', + {'color': '#6666ff', 'opacity': 1.0, 'point': (0.0, 0.0, 0.0), 'size': 5.0}), + ('point', + {'color': '#6666ff', + 'opacity': 1.0, + 'point': (30.0, 30.0, 30.0), + 'size': 5.0})] + + """ + render_params.push_transform(self.get_transformation()) + rep = Graphics3dGroup.threejs_repr(self, render_params) + render_params.pop_transform() + return rep + def get_transformation(self): """ Return the actual transformation object associated with ``self``. @@ -2465,6 +2586,58 @@ class TransformGroup(Graphics3dGroup): return Graphics3d.transform(self, **kwds) +class KeyframeAnimationGroup(Graphics3dGroup): + """A group of objects, each depicting a single frame of animation""" + def __init__(self, all=(), **kwds): + r""" + EXAMPLES:: + + sage: frames = [dodecahedron(), icosahedron(), tetrahedron()] + sage: sage.plot.plot3d.base.KeyframeAnimationGroup(frames) + Graphics3d Object + + They are usually constructed from an class:`~sage.plot.animate.Animation`:: + + sage: type(animate(frames).interactive()) + + + """ + Graphics3dGroup.__init__(self, all) + self._extra_kwds.update(kwds) + + def threejs_repr(self, render_params): + r""" + Adds keyframe information to the representations of the group's contents. + + EXAMPLES:: + + sage: a = point3d((0, 0, 1)) + sage: b = point3d((0, 1, 0)) + sage: c = point3d((1, 0, 0)) + sage: g = sage.plot.plot3d.base.KeyframeAnimationGroup([a, b, c]) + sage: g.threejs_repr(g.default_render_params()) + [('point', {..., 'keyframe': 0, ..., 'point': (0.0, 0.0, 1.0), ...}), + ('point', {..., 'keyframe': 1, ..., 'point': (0.0, 1.0, 0.0), ...}), + ('point', {..., 'keyframe': 2, ..., 'point': (1.0, 0.0, 0.0), ...})] + + Only top-level objects get a unique keyframe. Nested objects share the + same keyframe:: + + sage: g = sage.plot.plot3d.base.KeyframeAnimationGroup([a + b, c]) + sage: g.threejs_repr(g.default_render_params()) + [('point', {..., 'keyframe': 0, ..., 'point': (0.0, 0.0, 1.0), ...}), + ('point', {..., 'keyframe': 0, ..., 'point': (0.0, 1.0, 0.0), ...}), + ('point', {..., 'keyframe': 1, ..., 'point': (1.0, 0.0, 0.0), ...})] + + """ + reprs = [] + for i, g in enumerate(self.all): + for (kind, desc) in g.threejs_repr(render_params): + desc['keyframe'] = i + reprs.append((kind, desc)) + return reprs + + class Viewpoint(Graphics3d): """ This class represents a viewpoint, necessary for x3d. @@ -2594,6 +2767,28 @@ cdef class PrimitiveObject(Graphics3d): """ return self.triangulation().jmol_repr(render_params) + def threejs_repr(self, render_params): + r""" + Default behavior is to render the triangulation. + + EXAMPLES:: + + sage: from sage.plot.plot3d.base import PrimitiveObject + sage: class SimpleTriangle(PrimitiveObject): + ....: def triangulation(self): + ....: return polygon3d([(0,0,0), (1,0,0), (0,1,0)]) + sage: G = SimpleTriangle() + sage: G.threejs_repr(G.default_render_params()) + [('surface', + {'color': '#0000ff', + 'faces': [[0, 1, 2]], + 'opacity': 1.0, + 'vertices': [{'x': 0.0, 'y': 0.0, 'z': 0.0}, + {'x': 1.0, 'y': 0.0, 'z': 0.0}, + {'x': 0.0, 'y': 1.0, 'z': 0.0}]})] + + """ + return self.triangulation().threejs_repr(render_params) class BoundingSphere(SageObject): diff --git a/src/sage/plot/plot3d/implicit_surface.pyx b/src/sage/plot/plot3d/implicit_surface.pyx index c55246f6674..c9828cbd195 100644 --- a/src/sage/plot/plot3d/implicit_surface.pyx +++ b/src/sage/plot/plot3d/implicit_surface.pyx @@ -1144,6 +1144,30 @@ cdef class ImplicitSurface(IndexFaceSet): self.triangulate() return IndexFaceSet.json_repr(self, render_params) + def threejs_repr(self, render_params): + r""" + Return a represention of the surface suitable for plotting with three.js. + + EXAMPLES:: + + sage: from sage.plot.plot3d.implicit_surface import ImplicitSurface + sage: _ = var('x,y,z') + sage: G = ImplicitSurface(x + y + z, (x,-1, 1), (y,-1, 1), (z,-1, 1)) + sage: G.threejs_repr(G.default_render_params()) + [('surface', + {'color': '#6666ff', + 'faces': [[0, 1, 2], + ... + 'opacity': 1.0, + 'vertices': [{'x': ..., + 'y': -0.9743589743589..., + 'z': -0.02564102564102...}, + ... + {'x': -1.0, 'y': 0.9487179487179..., 'z': 0.05128205128205...}]})] + """ + self.triangulate() + return IndexFaceSet.threejs_repr(self, render_params) + def triangulate(self, force=False): """ The IndexFaceSet will be empty until you call this method, diff --git a/src/sage/plot/plot3d/index_face_set.pyx b/src/sage/plot/plot3d/index_face_set.pyx index 53cee57d827..41a47560a07 100644 --- a/src/sage/plot/plot3d/index_face_set.pyx +++ b/src/sage/plot/plot3d/index_face_set.pyx @@ -1284,6 +1284,124 @@ cdef class IndexFaceSet(PrimitiveObject): return json + def threejs_repr(self, render_params): + r""" + Return representation of the surface suitable for plotting with three.js. + + EXAMPLES: + + A simple triangle:: + + sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)]) + sage: G.threejs_repr(G.default_render_params()) + [('surface', + {'color': '#0000ff', + 'faces': [[0, 1, 2]], + 'opacity': 1.0, + 'vertices': [{'x': 0.0, 'y': 0.0, 'z': 1.0}, + {'x': 1.0, 'y': 1.0, 'z': 1.0}, + {'x': 2.0, 'y': 0.0, 'z': 1.0}]})] + + The same but with more options applied:: + + sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)], color='red', opacity=0.5, + ....: render_order=2, threejs_flat_shading=True, + ....: single_side=True, mesh=True) + sage: G.threejs_repr(G.default_render_params()) + [('surface', + {'color': '#ff0000', + 'faces': [[0, 1, 2]], + 'opacity': 0.5, + 'renderOrder': 2.0, + 'showMeshGrid': True, + 'singleSide': True, + 'useFlatShading': True, + 'vertices': [{'x': 0.0, 'y': 0.0, 'z': 1.0}, + {'x': 1.0, 'y': 1.0, 'z': 1.0}, + {'x': 2.0, 'y': 0.0, 'z': 1.0}]})] + + TESTS: + + Transformations apply to the surface's vertices:: + + sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)]).scale(2,1,-1) + sage: G.threejs_repr(G.default_render_params()) + [('surface', + {'color': '#0000ff', + 'faces': [[0, 1, 2]], + 'opacity': 1.0, + 'vertices': [{'x': 0.0, 'y': 0.0, 'z': -1.0}, + {'x': 2.0, 'y': 1.0, 'z': -1.0}, + {'x': 4.0, 'y': 0.0, 'z': -1.0}]})] + + Per-face colors:: + + sage: from sage.plot.plot3d.index_face_set import IndexFaceSet + sage: from sage.plot.plot3d.texture import Texture + sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)] + sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]] + sage: col = rainbow(10, 'rgbtuple') + sage: t_list=[Texture(col[i]) for i in range(10)] + sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list) + sage: S.threejs_repr(S.default_render_params()) + [('surface', + {'faceColors': ['#ff0000', '#ff9900', '#cbff00', '#33ff00'], + 'faces': [[0, 4, 5], [3, 4, 5], [2, 3, 4], [1, 3, 5]], + 'opacity': 1.0, + 'vertices': [{'x': 2.0, 'y': 0.0, 'z': 0.0}, + {'x': 0.0, 'y': 2.0, 'z': 0.0}, + {'x': 0.0, 'y': 0.0, 'z': 2.0}, + {'x': 0.0, 'y': 1.0, 'z': 1.0}, + {'x': 1.0, 'y': 0.0, 'z': 1.0}, + {'x': 1.0, 'y': 1.0, 'z': 0.0}]})] + + """ + surface = {} + + vertices = [] + cdef Transformation transform = render_params.transform + cdef point_c res + for i from 0 <= i < self.vcount: + if transform is None: + res = self.vs[i] + else: + transform.transform_point_c(&res, self.vs[i]) + vertices.append(dict(x=float(res.x), y=float(res.y), z=float(res.z))) + surface['vertices'] = vertices + + faces = [] + cdef face_c face + for i from 0 <= i < self.fcount: + face = self._faces[i] + faces.append([int(face.vertices[j]) for j from 0 <= j < face.n]) + surface['faces'] = faces + + if self.global_texture: + surface['color'] = '#' + str(self.texture.hex_rgb()) + else: + face_colors = [] + for i from 0 <= i < self.fcount: + face = self._faces[i] + color = Color(face.color.r, face.color.g, face.color.b) + face_colors.append(str(color.html_color())) + surface['faceColors'] = face_colors + + surface['opacity'] = float(self._extra_kwds.get('opacity', 1.0)) + + if 'render_order' in self._extra_kwds: + surface['renderOrder'] = float(self._extra_kwds['render_order']) + + if self._extra_kwds.get('single_side'): + surface['singleSide'] = True + + if self._extra_kwds.get('threejs_flat_shading'): + surface['useFlatShading'] = True + + if self._extra_kwds.get('mesh'): + surface['showMeshGrid'] = True + + return [('surface', surface)] + def obj_repr(self, render_params): """ Return an obj representation for ``self``. @@ -1681,25 +1799,6 @@ cdef class VertexIter: self.set.vs[self.i-1].z) -def len3d(v): - """ - Return the norm of a vector in three dimensions. - - This is deprecated since :trac:`27450` . - - EXAMPLES:: - - sage: from sage.plot.plot3d.index_face_set import len3d - sage: len3d((1,2,3)) - doctest:warning...: - DeprecationWarning: len3d is deprecated, use point_c_len instead - See https://trac.sagemath.org/27450 for details. - 3.7416573867739413 - """ - deprecation(27450, "len3d is deprecated, use point_c_len instead") - return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]) - - def sticker(face, width, hover): """ Return a sticker over the given face. diff --git a/src/sage/plot/plot3d/list_plot3d.py b/src/sage/plot/plot3d/list_plot3d.py index b823460d94f..4c1a18231c8 100644 --- a/src/sage/plot/plot3d/list_plot3d.py +++ b/src/sage/plot/plot3d/list_plot3d.py @@ -2,7 +2,6 @@ List Plots """ from __future__ import absolute_import -from six.moves import range from sage.structure.element import is_Matrix from sage.matrix.all import matrix diff --git a/src/sage/plot/plot3d/parametric_plot3d.py b/src/sage/plot/plot3d/parametric_plot3d.py index da98407f2a9..ee1a959a7bd 100644 --- a/src/sage/plot/plot3d/parametric_plot3d.py +++ b/src/sage/plot/plot3d/parametric_plot3d.py @@ -416,7 +416,7 @@ def g(x,y): return x, y+sin(y), x**2 + y**2 f_z = cos(u) / (1 + sqrt(2)) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), frame=False, color="green")) - Boy's surface (:wikipedia:`Boy's_surface` and https://mathcurve.com/surfaces/boy/boy.shtml):: + Boy's surface (:wikipedia:`Boy%27s_surface` and https://mathcurve.com/surfaces/boy/boy.shtml):: sage: u, v = var('u,v') sage: K = cos(u) / (sqrt(2) - cos(2*u)*sin(3*v)) diff --git a/src/sage/plot/plot3d/parametric_surface.pyx b/src/sage/plot/plot3d/parametric_surface.pyx index 1e5784854ca..42d3cb37430 100644 --- a/src/sage/plot/plot3d/parametric_surface.pyx +++ b/src/sage/plot/plot3d/parametric_surface.pyx @@ -309,6 +309,28 @@ cdef class ParametricSurface(IndexFaceSet): self.triangulate(render_params) return IndexFaceSet.json_repr(self, render_params) + def threejs_repr(self, render_params): + r""" + Return a represention of the surface suitable for plotting with three.js. + + EXAMPLES:: + + sage: _ = var('x,y') + sage: P = plot3d(x^2-y^2, (x, -2, 2), (y, -2, 2)) + sage: P.threejs_repr(P.default_render_params()) + [('surface', + {'color': '#6666ff', + 'faces': [[0, 1, 2, 3], + ... + 'opacity': 1.0, + 'vertices': [{'x': -2.0, 'y': -2.0, 'z': 0.0}, + ... + {'x': 2.0, 'y': 2.0, 'z': 0.0}]})] + + """ + self.triangulate(render_params) + return IndexFaceSet.threejs_repr(self, render_params) + def is_enclosed(self): """ Return a boolean telling whether or not it is necessary to @@ -723,7 +745,7 @@ cdef class ParametricSurface(IndexFaceSet): Draw a 3D plot of this graphics object, which just returns this object since this is already a 3D graphics object. Needed to support PLOT in doctrings, see :trac:`17498` - + EXAMPLES:: sage: S = parametric_plot3d( (sin, cos, lambda u: u/10), (0, 20)) @@ -754,7 +776,7 @@ class MoebiusStrip(ParametricSurface): sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip sage: M = MoebiusStrip(3,3) - sage: M.show() + sage: M.show() """ def __init__(self, r, width, twists=1, **kwds): diff --git a/src/sage/plot/plot3d/plot3d.py b/src/sage/plot/plot3d/plot3d.py index 8adbca5e3ce..79f1b368314 100644 --- a/src/sage/plot/plot3d/plot3d.py +++ b/src/sage/plot/plot3d/plot3d.py @@ -144,8 +144,6 @@ def f(x,y): return math.exp(x/5)*math.cos(y) from __future__ import absolute_import import inspect -from six import iteritems - from .tri_plot import TrianglePlot from .index_face_set import IndexFaceSet from .shapes import arrow3d @@ -1176,7 +1174,7 @@ def plot3d_adaptive(f, x_range, y_range, color="automatic", span = (len(texture)-1) / (max_z - min_z) # max to avoid dividing by 0 parts = P.partition(lambda x, y, z: int((z-min_z)*span)) all = [] - for k, G in iteritems(parts): + for k, G in parts.items(): G.set_texture(texture[k], opacity=opacity) all.append(G) P = Graphics3dGroup(all) diff --git a/src/sage/plot/plot3d/shapes.pyx b/src/sage/plot/plot3d/shapes.pyx index f448b7c63b2..6b6150ed5ab 100644 --- a/src/sage/plot/plot3d/shapes.pyx +++ b/src/sage/plot/plot3d/shapes.pyx @@ -106,7 +106,7 @@ class Box(IndexFaceSet): sage: from sage.plot.plot3d.shapes import Box A square black box:: - + sage: show(Box([1,1,1]), color='black') .. PLOT:: @@ -124,7 +124,7 @@ class Box(IndexFaceSet): sphinx_plot(Box([2,3,4], color="red")) A stack of boxes:: - + sage: show(sum([Box([2,3,1], color="red").translate((0,0,6*i)) for i in [0..3]])) .. PLOT:: @@ -134,7 +134,7 @@ class Box(IndexFaceSet): sphinx_plot(P) A sinusoidal stack of multicolored boxes:: - + sage: B = sum([Box([2,4,1/4], color=(i/4,i/5,1)).translate((sin(i),0,5-i)) for i in [0..20]]) sage: show(B, figsize=6) @@ -209,13 +209,13 @@ def ColorCube(size, colors, opacity=1, **kwds): sage: c.show() .. PLOT:: - + from sage.plot.plot3d.shapes import ColorCube c = ColorCube([1,2,3], ['red', 'blue', 'green', 'black', 'white', 'orange'], opacity=0.5) sphinx_plot(c) - + :: - + sage: list(c.texture_set())[0].opacity 0.5 @@ -299,14 +299,14 @@ cdef class Cone(ParametricSurface): sage: T = sum(Cone(exp(-n/5), 4/3*exp(-n/5), color=(0, .5, 0)).translate(0, 0, -3*exp(-n/5)) for n in [1..7]) sage: T += Cone(1/8, 1, color='brown').translate(0, 0, -3) sage: T.show(aspect_ratio=1, frame=False) - + .. PLOT:: from sage.plot.plot3d.shapes import Cone T = sum(Cone(exp(-n/5.0), 4/3*exp(-n/5.0), color=(0, .5, 0)).translate(0, 0, -3*exp(-n/5.0)) for n in range(8)) T += Cone(1/8, 1, color='brown').translate(0, 0, -3) sphinx_plot(T) - + """ def __init__(self, radius, height, closed=True, **kwds): """ @@ -819,7 +819,7 @@ cdef class Sphere(ParametricSurface): sage: S = Sphere(1).scale(1,2,1/2) sage: S.show(aspect_ratio=1) - + .. PLOT:: from sage.plot.plot3d.shapes import Sphere @@ -1162,6 +1162,35 @@ class Text(PrimitiveObject): self.get_texture().jmol_str("atom"), 'label "%s"' % self.string] #.replace('\n', '|')] + def threejs_repr(self, render_params): + r""" + Return representation of the text suitable for plotting in three.js. + + EXAMPLES:: + + sage: T = text3d("Hi", (1, 2, 3), color='red') + sage: T.threejs_repr(T.default_render_params()) + [('text', {'color': '#ff0000', 'text': 'Hi', 'x': 1.0, 'y': 2.0, 'z': 3.0})] + + TESTS: + + When created directly via the ``Text`` constructor instead of ``text3d``, + the text is located at the origin:: + + sage: from sage.plot.plot3d.shapes import Text + sage: T = Text("Hi") + sage: T.threejs_repr(T.default_render_params()) + [('text', {'color': '#6666ff', 'text': 'Hi', 'x': 0.0, 'y': 0.0, 'z': 0.0})] + + """ + center = (float(0), float(0), float(0)) + if render_params.transform is not None: + center = render_params.transform.transform_point(center) + color = '#' + str(self.texture.hex_rgb()) + string = str(self.string) + text = dict(text=string, x=center[0], y=center[1], z=center[2], color=color) + return [('text', text)] + def bounding_box(self): """ Text labels have no extent:: diff --git a/src/sage/plot/plot3d/shapes2.py b/src/sage/plot/plot3d/shapes2.py index b4e9b287baa..07e00c048da 100644 --- a/src/sage/plot/plot3d/shapes2.py +++ b/src/sage/plot/plot3d/shapes2.py @@ -817,6 +817,49 @@ def jmol_repr(self, render_params): cen = self.loc if transform is None else transform(self.loc) return ["draw %s DIAMETER %s {%s %s %s}\n%s" % (name, int(self.size), cen[0], cen[1], cen[2], self.texture.jmol_str('$' + name))] + def threejs_repr(self, render_params): + r""" + Return representation of the point suitable for plotting with three.js. + + EXAMPLES:: + + sage: P = point3d((1,2,3), color=(0,1,0), opacity=0.5, size=10) + sage: P.threejs_repr(P.default_render_params()) + [('point', + {'color': '#00ff00', 'opacity': 0.5, 'point': (1.0, 2.0, 3.0), 'size': 10.0})] + + TESTS: + + Transformations apply to the point's location:: + + sage: P = point3d((1,2,3)).translate(-1, -2, -3) + sage: P.threejs_repr(P.default_render_params()) + [('point', + {'color': '#6666ff', 'opacity': 1.0, 'point': (0.0, 0.0, 0.0), 'size': 5.0})] + + """ + transform = render_params.transform + center = tuple(float(coord) for coord in self.loc) + if transform is not None: + center = transform(center) + color = '#' + str(self.texture.hex_rgb()) + opacity = float(self.texture.opacity) + size = float(self.size) + point = dict(point=center, size=size, color=color, opacity=opacity) + return [('point', point)] + + def stl_binary_repr(self, render_params): + """ + Return an empty list, as this is not useful for STL export. + + EXAMPLES:: + + sage: P = point3d((1,2,3)).translate(-1, -2, -3) + sage: P.stl_binary_repr(P.default_render_params()) + [] + """ + return [] + class Line(PrimitiveObject): r""" @@ -1006,7 +1049,7 @@ def corners(self, corner_cutoff=None, max_len=None): - ``corner_cutoff`` -- (optional, default ``None``) If the cosine of the angle between adjacent line segments is smaller than - this bound, then there will be a sharp corner in the path. + this bound, then there will be a sharp corner in the path. Otherwise, the path is smoothed. If ``None``, then the default value 0.5 is used. @@ -1094,6 +1137,109 @@ def dot(x0_y0_z0, x1_y1_z1): count += 1 return corners + def threejs_repr(self, render_params): + r""" + Return representation of the line suitable for plotting with three.js. + + EXAMPLES:: + + sage: L = line3d([(1,2,3), (4,5,6)], thickness=10, color=(1,0,0), opacity=0.5) + sage: L.threejs_repr(L.default_render_params()) + [('line', + {'color': '#ff0000', + 'linewidth': 10.0, + 'opacity': 0.5, + 'points': [(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)]})] + + TESTS: + + Transformations apply to the line's vertices:: + + sage: L = line3d([(1,2,3), (4,5,6)]).translate(-1, -2, -3) + sage: L.threejs_repr(L.default_render_params()) + [('line', + {'color': '#6666ff', + 'linewidth': 1.0, + 'opacity': 1.0, + 'points': [(0.0, 0.0, 0.0), (3.0, 3.0, 3.0)]})] + + When setting ``arrow_head=True``, the last line segment is replaced by + an arrow with a width half the thickness of the line:: + + sage: L = line3d([(0,0,0), (1,1,1), (2,2,2)], thickness=4, arrow_head=True) + sage: L_repr = L.threejs_repr(L.default_render_params()) + sage: L_repr[-1] + ('line', + {'color': '#6666ff', + 'linewidth': 4.0, + 'opacity': 1.0, + 'points': [(0.0, 0.0, 0.0), (1.0, 1.0, 1.0)]}) + sage: A = arrow3d((1,1,1), (2,2,2), width=2) + sage: A_repr = A.threejs_repr(A.default_render_params()) + sage: A_repr == L_repr[:-1] + True + + The arrow shares the transformation, color, and opacity of the line:: + + sage: L = line3d([(0,0,0), (1,1,1), (2,2,2)], thickness=4, + ....: arrow_head=True, color=(1,0,0), opacity=0.5) + sage: L = L.translate(-1, -1, -1) + sage: L_repr = L.threejs_repr(L.default_render_params()) + sage: L_repr[-1] + ('line', + {'color': '#ff0000', + 'linewidth': 4.0, + 'opacity': 0.5, + 'points': [(-1.0, -1.0, -1.0), (0.0, 0.0, 0.0)]}) + sage: A = arrow3d((1,1,1), (2,2,2), width=2, color=(1,0,0), opacity=0.5) + sage: A = A.translate(-1, -1, -1) + sage: A_repr = A.threejs_repr(A.default_render_params()) + sage: A_repr == L_repr[:-1] + True + + If there were only two points to begin with, only the arrow head's + representation is returned:: + + sage: L = line3d([(0,0,0), (1,1,1)], thickness=2, arrow_head=True) + sage: L_repr = L.threejs_repr(L.default_render_params()) + sage: A = arrow3d((0,0,0), (1,1,1), width=1) + sage: A_repr = A.threejs_repr(A.default_render_params()) + sage: A_repr == L_repr + True + + """ + reprs = [] + points = [tuple(float(coord) for coord in p) for p in self.points] + color = '#' + str(self.texture.hex_rgb()) + opacity = float(self.texture.opacity) + thickness = float(self.thickness) + if self.arrow_head: + width = thickness / 2.0 + arrow = shapes.arrow3d(start=points[-2], end=points[-1], width=width, + color=color, opacity=opacity) + reprs += arrow.threejs_repr(render_params) + points = points[:-1] # The arrow replaces the last line segment. + if len(points) > 1: + transform = render_params.transform + if transform is not None: + points = [transform(p) for p in points] + line = dict(points=points, color=color, opacity=opacity, linewidth=thickness) + reprs.append(('line', line)) + return reprs + + def stl_binary_repr(self, render_params): + """ + Return an empty list, as this is not useful for STL export. + + EXAMPLES:: + + sage: L = line3d([(1,2,3), (4,5,6)]).translate(-1, -2, -3) + sage: L.stl_binary_repr(L.default_render_params()) + [] + """ + return [] + + @rename_keyword(alpha='opacity') def point3d(v, size=5, **kwds): """ @@ -1125,7 +1271,7 @@ def point3d(v, size=5, **kwds): sage: c = polytopes.hypercube(3) sage: v = c.vertices()[0]; v - A vertex at (-1, -1, -1) + A vertex at (1, -1, -1) sage: print(point(v)) Graphics3d Object diff --git a/src/sage/plot/plot3d/tachyon.py b/src/sage/plot/plot3d/tachyon.py index 871abcbce37..c4b4eface6c 100644 --- a/src/sage/plot/plot3d/tachyon.py +++ b/src/sage/plot/plot3d/tachyon.py @@ -143,7 +143,7 @@ from sage.misc.fast_methods import WithEqualityById from sage.structure.sage_object import SageObject -from sage.misc.misc import get_verbose +from sage.misc.verbose import get_verbose from sage.misc.temporary_file import tmp_filename #from sage.ext import fast_tachyon_routines @@ -449,7 +449,7 @@ def save(self, filename='sage.png', verbose=None, extra_opts=''): - ``verbose`` - integer (default: None); if no verbosity setting is supplied, the verbosity level set by - sage.misc.misc.set_verbose is used. + ``sage.misc.verbose.set_verbose`` is used. - ``0`` - silent @@ -528,6 +528,7 @@ def show(self, **kwds): sage: def f(x,y): return float(sin(x*y)) sage: h.texture('t0', ambient=0.1, diffuse=0.9, specular=0.1, opacity=1.0, color=(1.0,0,0)) sage: h.plot(f,(-4,4),(-4,4),"t0",max_depth=5,initial_depth=3, num_colors=60) # increase min_depth for better picture + sage: from sage.misc.verbose import set_verbose, get_verbose sage: set_verbose(0) sage: h.show() diff --git a/src/sage/plot/polygon.py b/src/sage/plot/polygon.py index 5ebfd541e7b..33090764311 100644 --- a/src/sage/plot/polygon.py +++ b/src/sage/plot/polygon.py @@ -17,7 +17,6 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from sage.plot.primitive import GraphicPrimitive_xydata from sage.misc.decorators import options, rename_keyword diff --git a/src/sage/plot/primitive.py b/src/sage/plot/primitive.py index 4c71c4103aa..6d266731bc7 100644 --- a/src/sage/plot/primitive.py +++ b/src/sage/plot/primitive.py @@ -19,7 +19,7 @@ #***************************************************************************** from sage.misc.fast_methods import WithEqualityById from sage.structure.sage_object import SageObject -from sage.misc.misc import verbose +from sage.misc.verbose import verbose class GraphicPrimitive(WithEqualityById, SageObject): """ diff --git a/src/sage/quadratic_forms/genera/genus.py b/src/sage/quadratic_forms/genera/genus.py index 8a75fe81bca..62b6c13475c 100644 --- a/src/sage/quadratic_forms/genera/genus.py +++ b/src/sage/quadratic_forms/genera/genus.py @@ -20,8 +20,8 @@ # **************************************************************************** from __future__ import print_function -from sage.misc.all import prod -from sage.arith.all import LCM +from sage.misc.all import prod, cached_method +from sage.arith.all import LCM, fundamental_discriminant from sage.matrix.matrix_space import MatrixSpace from sage.matrix.constructor import matrix from sage.rings.integer_ring import IntegerRing, ZZ @@ -31,7 +31,15 @@ from sage.libs.pari import pari from sage.rings.finite_rings.finite_field_constructor import FiniteField from copy import copy, deepcopy -from sage.misc.misc import verbose +from sage.misc.verbose import verbose +from sage.interfaces.magma import magma +from sage.functions.gamma import gamma +from sage.functions.transcendental import zeta +from sage.symbolic.constants import pi +from sage.symbolic.ring import SR +from sage.quadratic_forms.special_values import quadratic_L_function__exact + + def genera(sig_pair, determinant, max_scale=None, even=False): r""" @@ -1251,6 +1259,25 @@ class Genus_Symbol_p_adic_ring(object): - ``prime`` -- a prime number - ``symbol`` -- the list of invariants for Jordan blocks `A_t,...,A_t` given as a list of lists of integers + + EXAMPLES:: + + sage: from sage.quadratic_forms.genera.genus import p_adic_symbol + sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring + + sage: A = diagonal_matrix(ZZ, [1,2,3,4]) + sage: p = 2 + sage: s2 = p_adic_symbol(A, p, 2); s2 + [[0, 2, 3, 1, 4], [1, 1, 1, 1, 1], [2, 1, 1, 1, 1]] + sage: G2 = Genus_Symbol_p_adic_ring(p,s2);G2 + Genus symbol at 2: [1^-2 2^1 4^1]_6 + + sage: A = diagonal_matrix(ZZ, [1,2,3,4]) + sage: p = 3 + sage: s3 = p_adic_symbol(A, p, 1); s3 + [[0, 3, -1], [1, 1, 1]] + sage: G3 = Genus_Symbol_p_adic_ring(p,s3);G3 + Genus symbol at 3: 1^-3 3^1 """ def __init__(self, prime, symbol, check = True): r""" @@ -1261,30 +1288,27 @@ def __init__(self, prime, symbol, check = True): sage: from sage.quadratic_forms.genera.genus import p_adic_symbol sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring - sage: A = diagonal_matrix(ZZ, [1,2,3,4]) sage: p = 2 sage: s2 = p_adic_symbol(A, p, 2); s2 [[0, 2, 3, 1, 4], [1, 1, 1, 1, 1], [2, 1, 1, 1, 1]] - sage: G = Genus_Symbol_p_adic_ring(p,s2);G + sage: G2 = Genus_Symbol_p_adic_ring(p,s2);G2 Genus symbol at 2: [1^-2 2^1 4^1]_6 - sage: G == loads(dumps(G)) - True sage: A = diagonal_matrix(ZZ, [1,2,3,4]) sage: p = 3 sage: s3 = p_adic_symbol(A, p, 1); s3 [[0, 3, -1], [1, 1, 1]] - sage: G = Genus_Symbol_p_adic_ring(p,s3);G + sage: G3 = Genus_Symbol_p_adic_ring(p,s3);G3 Genus symbol at 3: 1^-3 3^1 - sage: G == loads(dumps(G)) + sage: G2 == loads(dumps(G2)) + True + sage: G3 == loads(dumps(G3)) True - - """ if check: pass - self._prime = prime + self._prime = ZZ(prime) self._symbol = symbol self._canonical_symbol = None @@ -1308,7 +1332,6 @@ def __repr__(self): Check that :trac:`25776` is fixed:: - sage: from sage.quadratic_forms.genera.genus import Genus sage: G = Genus(matrix.diagonal([2,2,64])) sage: G Genus of @@ -1507,6 +1530,171 @@ def __ne__(self, other): # return len(self._symbol) ## ------------------------------------------------------ + def automorphous_numbers(self): + r""" + Return generators of the automorphous square classes at this prime. + + A `p`-adic square class `r` is called automorphous if it is + the spinor norm of a proper `p`-adic integral automorphism of this form. + These classes form a group. See [Co1999]_ Chapter 15, 9.6 for details. + + OUTPUT: + + - a list of integers representing the square classes of generators of + the automorphous numbers + + EXAMPLES: + + The following examples are given in + [Co1999]_ 3rd edition, Chapter 15, 9.6 pp. 392:: + + sage: A = matrix.diagonal([3,16]) + sage: G = Genus(A) + sage: sym2 = G.local_symbols()[0] + sage: sym2 + Genus symbol at 2: [1^-1]_3:[16^1]_1 + sage: sym2.automorphous_numbers() + [3, 5] + + sage: A = matrix(ZZ,3,[2,1,0, 1,2,0, 0,0,18]) + sage: G = Genus(A) + sage: sym = G.local_symbols() + sage: sym[0] + Genus symbol at 2: 1^-2 [2^1]_1 + sage: sym[0].automorphous_numbers() + [1, 3, 5, 7] + sage: sym[1] + Genus symbol at 3: 1^-1 3^-1 9^-1 + sage: sym[1].automorphous_numbers() + [1, 3] + + Note that the generating set given is not minimal. + The first supplementation rule is used here:: + + sage: A = matrix.diagonal([2,2,4]) + sage: G = Genus(A) + sage: sym = G.local_symbols() + sage: sym[0] + Genus symbol at 2: [2^2 4^1]_3 + sage: sym[0].automorphous_numbers() + [1, 2, 3, 5, 7] + + but not there:: + + sage: A = matrix.diagonal([2,2,32]) + sage: G = Genus(A) + sage: sym = G.local_symbols() + sage: sym[0] + Genus symbol at 2: [2^2]_2:[32^1]_1 + sage: sym[0].automorphous_numbers() + [1, 2, 5] + + Here the second supplementation rule is used:: + + sage: A = matrix.diagonal([2,2,64]) + sage: G = Genus(A) + sage: sym = G.local_symbols() + sage: sym[0] + Genus symbol at 2: [2^2]_2:[64^1]_1 + sage: sym[0].automorphous_numbers() + [1, 2, 5] + """ + from .normal_form import collect_small_blocks, _min_nonsquare + automorphs = [] + sym = self.symbol_tuple_list() + G = self.gram_matrix().change_ring(ZZ) + p = self.prime() + if p != 2: + up = ZZ(_min_nonsquare(p)) + I = G.diagonal() + for r in I: + # We need to consider all pairs in I + # since at most 2 elements are part of a pair + # we need need at most 2 of each type + if I.count(r) > 2: + I.remove(r) + # products of all pairs + for r1 in I: + for r2 in I: + automorphs.append(r1*r2) + # supplement (i) + for block in sym: + if block[1] >= 2: + automorphs.append(up) + break + # normalize the square classes and remove duplicates + automorphs1 = set() + for s in automorphs: + u = 1 + if s.prime_to_m_part(p).kronecker(p) == -1: + u = up + v = (s.valuation(p) % 2) + sq = u * p**v + automorphs1.add(sq) + return list(automorphs1) + + # p = 2 + I = [] + II = [] + for block in collect_small_blocks(G): + if block.ncols() == 1: + u = block[0,0] + if I.count(u) < 2: + I.append(block[0,0]) + else: # rank2 + q = block[0,1] + II += [2*q, 3*2*q, 5*2*q, 7*2*q] + + L = I + II + # We need to consider all pairs in L + # since at most 2 elements are part of a pair + # we need need at most 2 of each type + for r in L: # remove triplicates + if L.count(r) > 2: + L.remove(r) + n = len(L) + for i in range(n): + for j in range(i): + r = L[i]*L[j] + automorphs.append(r) + + # supplement (i) + for k in range(len(sym)): + s = sym[k:k+3] + if sum([b[1] for b in s if b[0] - s[0][0] < 4]) >= 3: + automorphs += [ZZ(1), ZZ(3), ZZ(5), ZZ(7)] + break + + # supplement (ii) + I.sort(key=lambda x: x.valuation(2)) + n = len(I) + for i in range(n): + for j in range(i): + r = I[i] / I[j] + v, u = r.val_unit(ZZ(2)) + u = u % 8 + assert v >= 0 + if v==0 and u==1: + automorphs.append(ZZ(2)) + if v==0 and u==5: + automorphs.append(ZZ(6)) + if v in [0, 2, 4]: # this overlaps with the first two cases! + automorphs.append(ZZ(5)) + if v in [1, 3] and u in [1, 5]: + automorphs.append(ZZ(3)) + if v in [1, 3] and u in [3, 7]: + automorphs.append(ZZ(7)) + + # normalize the square classes and remove duplicates + automorphs1 = set() + for s in automorphs: + v, u = s.val_unit(ZZ(2)) + v = v % 2 + u = u % 8 + sq = u * 2**v + automorphs1.add(sq) + return list(automorphs1) + def canonical_symbol(self): r""" Return (and cache) the canonical p-adic genus symbol. This is @@ -1611,6 +1799,141 @@ def gram_matrix(self, check=True): assert Genus_Symbol_p_adic_ring(p, symG) == self, "oops" return G + def mass(self): + r""" + Return the local mass `m_p` of this genus as defined by Conway. + + See Equation (3) in [CS1988]_. + + EXAMPLES:: + + sage: G = Genus(matrix.diagonal([1,3,9])) + sage: G.local_symbol(3).mass() + 9/8 + + TESTS:: + + sage: G = Genus(matrix([1])) + sage: G.local_symbol(2).mass() + Traceback (most recent call last): + .... + ValueError: the dimension must be at least 2 + """ + if self.dimension() <= 1: + raise ValueError("the dimension must be at least 2") + p = self.prime() + sym = self._symbol + ############## + #diagonal product + ############## + + # diagonal factors + m_p = ZZ.prod(M_p(species, p) for species in self._species_list()) + # cross terms + r = len(sym) + ct = 0 + for j in range(r): + for i in range(j): + ct += (sym[j][0] - sym[i][0]) * sym[i][1] * sym[j][1] + ct = ct / QQ(2) + m_p *= p**ct + + if p != 2: + return m_p + + # type factors + nII = ZZ.sum(fq[1] for fq in sym if fq[3] == 0) + + nI_I = ZZ(0) # the total number of pairs of adjacent constituents f_q, + # f_2q that are both of type I (odd) + for k in range(r-1): + if sym[k][3] == sym[k+1][3] == 1 and sym[k][0] + 1 == sym[k+1][0]: + nI_I += ZZ(1) + return m_p * ZZ(2)**(nI_I - nII) + + def _standard_mass(self): + r""" + Return the standard p-mass of this local genus. + + See Equation (6) of [CS1988]_. + + EXAMPLES:: + + sage: G = Genus(matrix.diagonal([1,3,9])) + sage: g3 = G.local_symbol(3) + sage: g3._standard_mass() + 9/16 + """ + n = self.dimension() + p = self.prime() + s = (n + 1) // ZZ(2) + std = 2 * QQ.prod(1-p**(-2*k) for k in range(1, s)) + if n % 2 == 0: + D = ZZ(-1)**s * self.determinant() + epsilon = (4*D).kronecker(p) + std *= (1 - epsilon*p**(-s)) + return QQ(1) / std + + def _species_list(self): + r""" + Return the species list. + + See Table 1 in [CS1988]_. + + EXAMPLES:: + + sage: G = Genus(matrix.diagonal([1,3,27])) + sage: g3 = G.local_symbol(3) + sage: g3._species_list() + [1, 1, 1] + """ + p = self.prime() + species_list = [] + sym = self._symbol + if self.prime() != 2: + for k in range(len(sym)): + n = ZZ(sym[k][1]) + d = sym[k][2] + if n % 2 == 0 and d != ZZ(-1).kronecker(p)**(n//ZZ(2)): + species = -n + else: + species = n + species_list.append(species) + return species_list + + # p == 2 + # create a dense list of symbols + symbols = [] + s = 0 + for k in range(sym[-1][0] + 1): + if sym[s][0] == k: + symbols.append(sym[s]) + s +=1 + else: + symbols.append([k,0,1,0,0]) + # avoid a case distinction + sym = [[-2,0,1,0,0],[-1,0,1,0,0]] + symbols + [[sym[-1][0]+1,0,1,0,0],[sym[-1][0]+2,0,1,0,0]] + for k in range(1, len(sym)-1): + free = True + if sym[k-1][3]==1 or sym[k+1][3]==1: + free = False + n = sym[k][1] + o = sym[k][4] + if ZZ(sym[k][2]).kronecker(2) == -1: + o = (o + ZZ(4)) % 8 + if sym[k][3] == 0 or n % 2 == 1: + t = n // ZZ(2) + else: + t = (n // ZZ(2)) - ZZ(1) + if free and (o == 0 or o == 1 or o == 7): + species = 2*t + elif free and (o == 3 or o == 5 or o == 4): + species = -2*t + else: + species = 2*t + 1 + species_list.append(species) + return species_list + def prime(self): r""" Return the prime number `p` of this `p`-adic local symbol. @@ -1788,6 +2111,63 @@ def dimension(self): dim = dimension rank = dimension + def direct_sum(self, other): + r""" + Return the local genus of the direct sum of two representatives. + + EXAMPLES:: + + sage: from sage.quadratic_forms.genera.genus import p_adic_symbol + sage: from sage.quadratic_forms.genera.genus import Genus_Symbol_p_adic_ring + sage: A = matrix.diagonal([1,2,3,4]) + sage: p = 2 + sage: G2 = Genus_Symbol_p_adic_ring(p, p_adic_symbol(A, p, 2)); G2 + Genus symbol at 2: [1^-2 2^1 4^1]_6 + sage: G2.direct_sum(G2) + Genus symbol at 2: [1^4 2^2 4^2]_4 + + TESTS:: + + sage: G = Genus(matrix([6])) + sage: G2 = G.local_symbol(2) + sage: G3 = G.local_symbol(3) + sage: G2.direct_sum(G3) + Traceback (most recent call last): + ... + ValueError: the local genus symbols must be over the same prime + """ + if self.prime() != other.prime(): + raise ValueError("the local genus symbols must be over the same prime") + sym1 = self.symbol_tuple_list() + sym2 = other.symbol_tuple_list() + m = max(sym1[-1][0], sym2[-1][0]) + sym1 = dict([[s[0], s] for s in sym1]) + sym2 = dict([[s[0], s] for s in sym2]) + + symbol = [] + for k in range(m + 1): + if self.prime() == 2: + b = [k, 0, 1, 0, 0] + else: + b = [k, 0, 1] + for sym in [sym1, sym2]: + try: + s = sym[k] + b[1] += s[1] + b[2] *= s[2] + if self.prime() == 2: + b[2] = b[2] % 8 + if s[3] == 1: + b[3] = s[3] + b[4] = (b[4] + s[4]) % 8 + except KeyError: + pass + if b[1] != 0: + symbol.append(b) + if self.rank() == other.rank() == 0: + symbol = self.symbol_tuple_list() + return Genus_Symbol_p_adic_ring(self.prime(), symbol) + def excess(self): r""" Returns the p-excess of the quadratic form whose Hessian @@ -1969,8 +2349,6 @@ def __init__(self, signature_pair, local_symbols, representative=None, check=Tru EXAMPLES:: - sage: from sage.quadratic_forms.genera.genus import Genus - sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix() sage: G = Genus(A) sage: G == loads(dumps(G)) @@ -2008,7 +2386,6 @@ def __repr__(self): EXAMPLES:: - sage: from sage.quadratic_forms.genera.genus import Genus sage: A = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix() sage: GS = Genus(A) sage: GS @@ -2080,8 +2457,6 @@ def __eq__(self, other): EXAMPLES:: - sage: from sage.quadratic_forms.genera.genus import Genus - sage: A1 = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix() sage: GS1 = Genus(A1) sage: A2 = DiagonalQuadraticForm(ZZ, [1,2,3,5]).Hessian_matrix() @@ -2136,8 +2511,6 @@ def __ne__(self, other): EXAMPLES:: - sage: from sage.quadratic_forms.genera.genus import Genus - sage: A1 = DiagonalQuadraticForm(ZZ, [1,2,3,4]).Hessian_matrix() sage: GS1 = Genus(A1) sage: A2 = DiagonalQuadraticForm(ZZ, [1,2,3,5]).Hessian_matrix() @@ -2248,6 +2621,36 @@ def dimension(self): dim = dimension rank = dimension + def direct_sum(self, other): + r""" + Return the genus of the direct sum of ``self`` and ``other``. + + The direct sum is defined as the direct sum of representatives. + + EXAMPLES:: + + sage: G = IntegralLattice("A4").twist(3).genus() + sage: G.direct_sum(G) + Genus of + None + Signature: (8, 0) + Genus symbol at 2: 1^8 + Genus symbol at 3: 3^8 + Genus symbol at 5: 1^6 5^2 + """ + p1, n1 = self.signature_pair() + p2, n2 = other.signature_pair() + signature_pair = (p1 + p2, n1 + n2) + + primes = [s.prime() for s in self.local_symbols()] + primes += [s.prime() for s in other.local_symbols() if not s.prime() in primes] + primes.sort() + local_symbols = [] + for p in primes: + sym_p = self.local_symbol(p=p).direct_sum(other.local_symbol(p=p)) + local_symbols.append(sym_p) + return GenusSymbol_global_ring(signature_pair, local_symbols) + def discriminant_form(self): r""" Return the discriminant form associated to this genus. @@ -2345,9 +2748,7 @@ def _compute_representative(self, LLL=True): ....: assert all(g==Genus(g.representative()) for g in G) # long time """ from sage.modules.free_quadratic_module_integer_symmetric import IntegralLattice, local_modification - even = self.is_even() q = self.rational_representative() - n = q.nrows() # the associated quadratic form xGx.T/2 should be integral L = IntegralLattice(4*q).maximal_overlattice() p = 2 @@ -2420,7 +2821,6 @@ def local_symbols(self): EXAMPLES:: - sage: from sage.quadratic_forms.genera.genus import Genus sage: A = matrix.diagonal(ZZ, [2,-4,6,8]) sage: GS = Genus(A) sage: GS.local_symbols() @@ -2429,6 +2829,142 @@ def local_symbols(self): """ return deepcopy(self._local_symbols) + def local_symbol(self, p): + r""" + Return a copy of the local symbol at the prime `p`. + + EXAMPLES:: + + sage: A = matrix.diagonal(ZZ, [2,-4,6,8]) + sage: GS = Genus(A) + sage: GS.local_symbol(3) + Genus symbol at 3: 1^-3 3^-1 + """ + p = ZZ(p) + for sym in self._local_symbols: + if p == sym.prime(): + return deepcopy(sym) + assert p != 2 + sym_p = [[0, self.rank(), self.det().kronecker(p)]] + return Genus_Symbol_p_adic_ring(p, sym_p) + + def _standard_mass(self): + r""" + Return the standard mass of this genus. + + It depends only on the dimension and determinant. + + EXAMPLES:: + + sage: A = matrix.diagonal(ZZ, [1,1,1,1]) + sage: GS = Genus(A) + sage: GS._standard_mass() + 1/48 + + """ + n = self.dimension() + if n % 2 == 0: + s = n // 2 + else: + s = (n // 2) + 1 + std = QQ(2) * pi**(-n*(n+1)/QQ(4)) + std *= SR.prod(gamma(QQ(j)/QQ(2)) for j in range(1, n+1)) + std *= SR.prod(zeta(ZZ(2)*ZZ(k)) for k in range(1, s)) + if n % 2 == 0: + D = ZZ(-1)**(s)*self.determinant() + std *= quadratic_L_function__exact(ZZ(s), D) + d = fundamental_discriminant(D) + # since quadratic_L_function__exact is different + # from \zeta_D as defined by Conway and Sloane + # we have to compensate + # the missing Euler factors + for sym in self.local_symbols(): + p = sym.prime() + std *= (1 - d.kronecker(p)*p**(-s)) + return std + + @cached_method + def mass(self, backend='sage'): + r""" + Return the mass of this genus. + + The genus must be definite. + Let `L_1, ... L_n` be a complete list of representatives + of the isometry classes in this genus. + Its mass is defined as + + .. MATH:: + + \sum_{i=1}^n \frac{1}{|O(L_i)|}. + + INPUT: + + - ``backend`` -- default: ``'sage'``, or ``'magma'`` + + OUTPUT: + + a rational number + + EXAMPLES:: + + sage: from sage.quadratic_forms.genera.genus import genera + sage: G = genera((8,0),1,even=True)[0] + sage: G.mass() + 1/696729600 + sage: G.mass(backend='magma') # optional - magma + 1/696729600 + + The `E_8` lattice is unique in its genus:: + + sage: E8 = QuadraticForm(G.representative()) + sage: E8.number_of_automorphisms() + 696729600 + + TESTS: + + Check a random genus with magma:: + + sage: d = ZZ.random_element(1,1000) + sage: n = ZZ.random_element(2,10) + sage: L = genera((n,0),d,d,even=False) + sage: k = ZZ.random_element(0,len(L)) + sage: G = L[k] + sage: G.mass()==G.mass(backend='magma') # optional - magma + True + + Error messages:: + + sage: G.mass(backend='foo') + Traceback (most recent call last): + ... + ValueError: unknown backend: foo + sage: G = Genus(matrix(ZZ, 2, [0, 1, 1, 0])) + sage: G.mass() + Traceback (most recent call last): + ... + ValueError: the genus must be definite. + """ + pos, neg = self.signature_pair() + if pos * neg != 0: + raise ValueError("the genus must be definite.") + if pos + neg == 1: + return QQ(1)/QQ(2) + if backend == 'sage': + mass = self._standard_mass() + for sym in self._local_symbols: + mass *= sym.mass()/sym._standard_mass() + return QQ(mass.canonicalize_radical()) + elif backend == 'magma': + e = 1 # lattices in magma are positive definite + if neg !=0: + e = -1 + # for some reason LatticeWithGram wants a dense matrix + L = magma(e*self.representative().dense_matrix()) + L = L.LatticeWithGram() + return QQ(L.Mass()) + else: + raise ValueError("unknown backend: %s"%backend) + def _gram_from_jordan_block(p, block, discr_form=False): r""" @@ -2474,14 +3010,14 @@ def _gram_from_jordan_block(p, block, discr_form=False): [1/2 0 0 0] [ 0 0 1/2 0] [ 0 0 0 1/2] - """ + """ from sage.quadratic_forms.genera.normal_form import _min_nonsquare level = block[0] rk = block[1] det = block[2] if p == 2: - o = block[3] - t = block[4] + o = ZZ(block[3]) + t = ZZ(block[4]) U = matrix(QQ,2,[0,1,1,0]) V = matrix(QQ,2,[2,1,1,2]) W = matrix(QQ,1,[1]) @@ -2532,13 +3068,86 @@ def _gram_from_jordan_block(p, block, discr_form=False): q = matrix.identity(QQ, rk) d = 2**(rk % 2) if Integer(d).kronecker(p) != det: - u = _min_nonsquare(p) + u = ZZ(_min_nonsquare(p)) q[0,0] = u q = q * (2 / p**level) if p != 2 and not discr_form: q = matrix.identity(QQ, rk) if det != 1: - u = _min_nonsquare(p) + u = ZZ(_min_nonsquare(p)) q[0,0] = u q = q * p**level return q + +# Helper functions for mass computations + +def M_p(species, p): + r""" + Return the diagonal factor `M_p` as a function of the species. + + EXAMPLES: + + These examples are taken from Table 2 of [CS1988]_:: + + sage: from sage.quadratic_forms.genera.genus import M_p + sage: M_p(0,2) + 1 + sage: M_p(1,2) + 1/2 + sage: M_p(-2,2) + 1/3 + sage: M_p(2,2) + 1 + sage: M_p(3,2) + 2/3 + sage: M_p(-4,2) + 8/15 + sage: M_p(4,2) + 8/9 + sage: M_p(5,2) + 32/45 + + TESTS: + + More values of the table for testing:: + + sage: M_p(0,3) + 1 + sage: M_p(1,3) + 1/2 + sage: M_p(-2,3) + 3/8 + sage: M_p(2,3) + 3/4 + sage: M_p(3,3) + 9/16 + sage: M_p(-4,3) + 81/160 + sage: M_p(4,3) + 81/128 + sage: M_p(5,3) + 729/1280 + + sage: M_p(0,5) + 1 + sage: M_p(1,5) + 1/2 + sage: M_p(-2,5) + 5/12 + sage: M_p(2,5) + 5/8 + sage: M_p(3,5) + 25/48 + sage: M_p(-4,5) + 625/1248 + sage: M_p(4,5) + 625/1152 + """ + if species == 0: + return QQ(1) + n = species.abs() + s = (n+1) // ZZ(2) + mp = ZZ(2) * ZZ.prod(ZZ(1)-p**(-2*k) for k in range(1, s)) + if n % 2 == 0: + mp *= ZZ(1) - species.sign() * p**(-s) + return QQ(1) / mp diff --git a/src/sage/quadratic_forms/quadratic_form.py b/src/sage/quadratic_forms/quadratic_form.py index 58050ee2e1c..bab9760ddb3 100644 --- a/src/sage/quadratic_forms/quadratic_form.py +++ b/src/sage/quadratic_forms/quadratic_form.py @@ -8,16 +8,15 @@ - Simon Brandhorst (2019-10-15): :meth:``quadratic_form_from_invariants`` """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein and Jonathan Hanke # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** -from six.moves import range +# https://www.gnu.org/licenses/ +# **************************************************************************** from warnings import warn from copy import deepcopy @@ -49,7 +48,7 @@ def QuadraticForm__constructor(R, n=None, entries=None): EXAMPLES:: sage: from sage.quadratic_forms.quadratic_form import QuadraticForm__constructor - sage: QuadraticForm__constructor(ZZ, 3) ## Makes a generic quadratic form over the integers + sage: QuadraticForm__constructor(ZZ, 3) # Makes a generic quadratic form over the integers Quadratic form in 3 variables over Integer Ring with coefficients: [ 0 0 0 ] [ * 0 0 ] @@ -61,7 +60,7 @@ def QuadraticForm__constructor(R, n=None, entries=None): def is_QuadraticForm(Q): """ - Determines if the object Q is an element of the QuadraticForm class. + Determine if the object Q is an element of the QuadraticForm class. EXAMPLES:: @@ -71,7 +70,6 @@ def is_QuadraticForm(Q): True sage: is_QuadraticForm(2) ##random False - """ return isinstance(Q, QuadraticForm) @@ -507,27 +505,36 @@ def __init__(self, R, n=None, entries=None, unsafe_initialization=False, number_ Traceback (most recent call last): ... ValueError: the size must be a non-negative integer, not -1 + + sage: x = polygen(ZZ, 'x') + sage: QuadraticForm(x**2) + Traceback (most recent call last): + .... + TypeError: wrong input for QuadraticForm """ - ## Deal with: QuadraticForm(ring, matrix) + # Deal with: QuadraticForm(ring, matrix) matrix_init_flag = False if isinstance(R, Ring): if is_Matrix(n): - ## Test if n is symmetric and has even diagonal + # Test if n is symmetric and has even diagonal if not self._is_even_symmetric_matrix_(n, R): raise TypeError("Oops! The matrix is not a symmetric with even diagonal defined over R.") - ## Rename the matrix and ring + # Rename the matrix and ring M = n M_ring = R matrix_init_flag = True - ## Deal with: QuadraticForm(matrix) - if n is None and is_Matrix(R): - ## Test if R is symmetric and has even diagonal + elif not is_Matrix(R): + # first argument, if not a ring, must be a matrix + raise TypeError('wrong input for QuadraticForm') + else: + # Deal with: QuadraticForm(matrix) + # Test if R is symmetric and has even diagonal if not self._is_even_symmetric_matrix_(R): raise TypeError("Oops! The matrix is not a symmetric with even diagonal.") - ## Rename the matrix and ring + # Rename the matrix and ring M = R M_ring = R.base_ring() matrix_init_flag = True @@ -553,8 +560,6 @@ def __init__(self, R, n=None, entries=None, unsafe_initialization=False, number_ if n < 0: raise ValueError("the size must be a non-negative integer, not {}".format(n)) - # TODO: Verify that R is a ring... - # Store the relevant variables N = n * (n + 1) // 2 self.__n = n @@ -595,7 +600,7 @@ def __init__(self, R, n=None, entries=None, unsafe_initialization=False, number_ def list_external_initializations(self): """ - Returns a list of the fields which were set externally at + Return a list of the fields which were set externally at creation, and not created through the usual QuadraticForm methods. These fields are as good as the external process that made them, and are thus not guaranteed to be correct. @@ -646,11 +651,9 @@ def _pari_init_(self): sage: Q = QuadraticForm(ZZ, 2, [1,0,5]) sage: Q._pari_init_() 'Mat([2,0;0,10])' - """ return self.matrix()._pari_init_() - def _repr_(self): """ Give a text representation for the quadratic form given as an upper-triangular matrix of coefficients. @@ -705,8 +708,6 @@ def _latex_(self): out_str += "\\end{array} \\right]" return out_str - - def __getitem__(self, ij): """ Return the coefficient `a_{ij}` of `x_i * x_j`. @@ -733,7 +734,6 @@ def __getitem__(self, ij): return self.__coeffs[i*self.__n - i*(i-1)//2 + j - i] - def __setitem__(self, ij, coeff): """ Set the coefficient `a_{ij}` in front of `x_i * x_j`. @@ -856,7 +856,7 @@ def __add__(self, right): def sum_by_coefficients_with(self, right): """ - Returns the sum (on coefficients) of two quadratic forms of the same size. + Return the sum (on coefficients) of two quadratic forms of the same size. EXAMPLES:: @@ -895,6 +895,7 @@ def sum_by_coefficients_with(self, right): # Multiply (on the right) the quadratic form Q by an element of the ring that Q is defined over. # # EXAMPLES:: +# # sage: Q = QuadraticForm(ZZ, 2, [1,4,10]) # sage: Q*2 # Quadratic form in 2 variables over Integer Ring with coefficients: @@ -927,10 +928,10 @@ def __call__(self, v): Q' = v^t * Q * v. - EXAMPLES:: + EXAMPLES: + + Evaluate a quadratic form at a vector:: - ## Evaluate a quadratic form at a vector: - ## -------------------------------------- sage: Q = QuadraticForm(QQ, 3, range(6)) sage: Q Quadratic form in 3 variables over Rational Field with coefficients: @@ -944,10 +945,8 @@ def __call__(self, v): sage: Q([1,1,1]) 15 - :: + Evaluate a quadratic form using a column matrix:: - ## Evaluate a quadratic form using a column matrix: - ## ------------------------------------------------ sage: Q = QuadraticForm(QQ, 2, range(1,4)) sage: A = Matrix(ZZ,2,2,[-1,0,0,1]) sage: Q(A) @@ -963,10 +962,8 @@ def __call__(self, v): Quadratic form in 1 variables over Rational Field with coefficients: [ 1 ] - :: + Simple 2x2 change of variables:: - ## Simple 2x2 change of variables: - ## ------------------------------- sage: Q = QuadraticForm(ZZ, 2, [1,0,1]) sage: Q Quadratic form in 2 variables over Integer Ring with coefficients: @@ -981,10 +978,8 @@ def __call__(self, v): [ 1 2 ] [ * 2 ] - :: + Some more tests:: - ## Some more tests: - ## ---------------- sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) sage: Q([1,2,3]) 14 @@ -1103,7 +1098,7 @@ def _is_even_symmetric_matrix_(self, A, R=None): def matrix(self): """ - Returns the Hessian matrix A for which Q(X) = `(1/2) * X^t * A * X`. + Return the Hessian matrix A for which Q(X) = `(1/2) * X^t * A * X`. EXAMPLES:: @@ -1116,10 +1111,9 @@ def matrix(self): """ return self.Hessian_matrix() - def Hessian_matrix(self): """ - Returns the Hessian matrix A for which Q(X) = `(1/2) * X^t * A * X`. + Return the Hessian matrix A for which Q(X) = `(1/2) * X^t * A * X`. EXAMPLES:: @@ -1145,10 +1139,9 @@ def Hessian_matrix(self): return matrix(self.base_ring(), self.dim(), self.dim(), mat_entries) - def Gram_matrix_rational(self): """ - Returns a (symmetric) Gram matrix A for the quadratic form Q, + Return a (symmetric) Gram matrix A for the quadratic form Q, meaning that .. MATH:: @@ -1171,10 +1164,9 @@ def Gram_matrix_rational(self): """ return (ZZ(1) / ZZ(2)) * self.matrix() - def Gram_matrix(self): """ - Returns a (symmetric) Gram matrix A for the quadratic form Q, + Return a (symmetric) Gram matrix A for the quadratic form Q, meaning that .. MATH:: @@ -1211,10 +1203,9 @@ def Gram_matrix(self): else: raise TypeError("Oops! This form does not have an integral Gram matrix. =(") - def has_integral_Gram_matrix(self): """ - Returns whether the quadratic form has an integral Gram matrix (with respect to its base ring). + Return whether the quadratic form has an integral Gram matrix (with respect to its base ring). A warning is issued if the form is defined over a field, since in that case the return is trivially true. @@ -1244,10 +1235,9 @@ def has_integral_Gram_matrix(self): return flag - def gcd(self): """ - Returns the greatest common divisor of the coefficients of the + Return the greatest common divisor of the coefficients of the quadratic form (as a polynomial). EXAMPLES:: @@ -1340,10 +1330,9 @@ def is_primitive(self): """ return (self.gcd() == 1) - def primitive(self): """ - Returns a primitive version of an integer-valued quadratic form, defined over `ZZ`. + Return a primitive version of an integer-valued quadratic form, defined over `ZZ`. EXAMPLES:: @@ -1365,11 +1354,9 @@ def primitive(self): g = self.gcd() return QuadraticForm(self.base_ring(), self.dim(), [ZZ(x/g) for x in self.coefficients()]) - - def adjoint_primitive(self): """ - Returns the primitive adjoint of the quadratic form, which is + Return the primitive adjoint of the quadratic form, which is the smallest discriminant integer-valued quadratic form whose matrix is a scalar multiple of the inverse of the matrix of the given quadratic form. @@ -1637,9 +1624,9 @@ def level_ideal(self): return Ideal(self.base_ring()(self.level())) - def bilinear_map(self,v,w): + def bilinear_map(self, v, w): r""" - Returns the value of the associated bilinear map on two vectors + Return the value of the associated bilinear map on two vectors Given a quadratic form `Q` over some base ring `R` with characteristic not equal to 2, this gives the image of two @@ -1700,11 +1687,12 @@ def bilinear_map(self,v,w): genera = staticmethod(genera) -## ===================================================================================================== +## ============================================================================ + def DiagonalQuadraticForm(R, diag): """ - Returns a quadratic form over `R` which is a sum of squares. + Return a quadratic form over `R` which is a sum of squares. INPUT: @@ -1727,5 +1715,5 @@ def DiagonalQuadraticForm(R, diag): """ Q = QuadraticForm(R, len(diag)) for i in range(len(diag)): - Q[i,i] = diag[i] + Q[i, i] = diag[i] return Q diff --git a/src/sage/quadratic_forms/quadratic_form__equivalence_testing.py b/src/sage/quadratic_forms/quadratic_form__equivalence_testing.py index 0b9214b6cb6..f0689ddc741 100644 --- a/src/sage/quadratic_forms/quadratic_form__equivalence_testing.py +++ b/src/sage/quadratic_forms/quadratic_form__equivalence_testing.py @@ -5,8 +5,7 @@ - Anna Haensch (2014-12-01): added test for rational isometry """ -from __future__ import print_function -from __future__ import absolute_import +from __future__ import print_function, absolute_import from sage.arith.all import hilbert_symbol, prime_divisors, is_prime, valuation, GCD, legendre_symbol from sage.rings.integer_ring import ZZ @@ -20,11 +19,13 @@ ## (For now, we require both forms to be positive definite.) ## ################################################################################ -def is_globally_equivalent_to(self, other, return_matrix=False, check_theta_to_precision=None, check_local_equivalence=None): +def is_globally_equivalent_to(self, other, return_matrix=False): """ - Determines if the current quadratic form is equivalent to the - given form over ZZ. If ``return_matrix`` is True, then we return - the transformation matrix `M` so that ``self(M) == other``. + Determine if the current quadratic form is equivalent to the + given form over ZZ. + + If ``return_matrix`` is True, then we return the transformation + matrix `M` so that ``self(M) == other``. INPUT: @@ -77,16 +78,9 @@ def is_globally_equivalent_to(self, other, return_matrix=False, check_theta_to_p ... ValueError: not a definite form in QuadraticForm.is_globally_equivalent_to() - ALGORITHM: this uses the PARI function ``qfisom()``, implementing + ALGORITHM: this uses the PARI function :pari:`qfisom`, implementing an algorithm by Plesken and Souvignier. """ - if check_theta_to_precision is not None: - from sage.misc.superseded import deprecation - deprecation(19111, "The check_theta_to_precision argument is deprecated and ignored") - if check_local_equivalence is not None: - from sage.misc.superseded import deprecation - deprecation(19111, "The check_local_equivalence argument is deprecated and ignored") - ## Check that other is a QuadraticForm if not is_QuadraticForm(other): raise TypeError("you must compare two quadratic forms, but the argument is not a quadratic form") @@ -107,7 +101,7 @@ def is_globally_equivalent_to(self, other, return_matrix=False, check_theta_to_p def is_locally_equivalent_to(self, other, check_primes_only=False, force_jordan_equivalence_test=False): """ - Determines if the current quadratic form (defined over ZZ) is + Determine if the current quadratic form (defined over ZZ) is locally equivalent to the given form over the real numbers and the `p`-adic integers for every prime p. @@ -166,8 +160,6 @@ def is_locally_equivalent_to(self, other, check_primes_only=False, force_jordan_ return True - - def has_equivalent_Jordan_decomposition_at_prime(self, other, p): """ Determines if the given quadratic form has a Jordan decomposition diff --git a/src/sage/quadratic_forms/quadratic_form__local_density_congruence.py b/src/sage/quadratic_forms/quadratic_form__local_density_congruence.py index a073c460740..35ff11001d0 100644 --- a/src/sage/quadratic_forms/quadratic_form__local_density_congruence.py +++ b/src/sage/quadratic_forms/quadratic_form__local_density_congruence.py @@ -15,7 +15,7 @@ from sage.sets.set import Set from sage.rings.rational_field import QQ from sage.arith.all import valuation -from sage.misc.misc import verbose +from sage.misc.verbose import verbose from sage.quadratic_forms.count_local_2 import count_modp__by_gauss_sum diff --git a/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py b/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py index 977126e99de..03f74d51ed0 100644 --- a/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py +++ b/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py @@ -15,7 +15,6 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range ########################################################################### ## TO DO: Add routines for hasse invariants at all places, anisotropic diff --git a/src/sage/quadratic_forms/quadratic_form__siegel_product.py b/src/sage/quadratic_forms/quadratic_form__siegel_product.py index 2a6154d946c..d380daaeb5b 100644 --- a/src/sage/quadratic_forms/quadratic_form__siegel_product.py +++ b/src/sage/quadratic_forms/quadratic_form__siegel_product.py @@ -16,7 +16,8 @@ from sage.functions.all import sqrt from sage.quadratic_forms.special_values import QuadraticBernoulliNumber -from sage.misc.misc import verbose + +from sage.misc.verbose import verbose diff --git a/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py b/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py index 6d4dff71aa7..9960396049a 100644 --- a/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py +++ b/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py @@ -11,7 +11,6 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from six.moves import range from sage.rings.integer_ring import ZZ from sage.misc.functional import is_odd diff --git a/src/sage/quadratic_forms/quadratic_form__theta.py b/src/sage/quadratic_forms/quadratic_form__theta.py index c5a15329f60..0db237fe638 100644 --- a/src/sage/quadratic_forms/quadratic_form__theta.py +++ b/src/sage/quadratic_forms/quadratic_form__theta.py @@ -21,7 +21,7 @@ -from sage.misc.misc import cputime, verbose +from sage.misc.misc import cputime def theta_series(self, Max=10, var_str='q', safe_flag=True): @@ -367,6 +367,8 @@ def theta_series_degree_2(Q, prec): - Raum, Ryan, Skoruppa, Tornaria, 'On Formal Siegel Modular Forms' (preprint) """ + from sage.misc.verbose import verbose + if Q.base_ring() != ZZ: raise TypeError("The quadratic form must be integral") if not Q.is_positive_definite(): diff --git a/src/sage/quadratic_forms/quadratic_form__variable_substitutions.py b/src/sage/quadratic_forms/quadratic_form__variable_substitutions.py index 4dda6fe451a..8d3b7dac337 100644 --- a/src/sage/quadratic_forms/quadratic_form__variable_substitutions.py +++ b/src/sage/quadratic_forms/quadratic_form__variable_substitutions.py @@ -386,7 +386,7 @@ def add_symmetric(self, c, i, j, in_place = False): sage: Q.add_symmetric(-3/2, 2, 0) ## ERROR: -3/2 isn't in the base ring ZZ Traceback (most recent call last): ... - RuntimeError: Oops! This coefficient can't be coerced to an element of the base ring for the quadratic form. + RuntimeError: Oops! This coefficient can...t be coerced to an element of the base ring for the quadratic form. :: diff --git a/src/sage/quivers/algebra.py b/src/sage/quivers/algebra.py index 506378093a2..dedfb4d7392 100644 --- a/src/sage/quivers/algebra.py +++ b/src/sage/quivers/algebra.py @@ -20,7 +20,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -import six from sage.misc.cachefunc import cached_method from sage.combinat.free_module import CombinatorialFreeModule from .algebra_elements import PathAlgebraElement @@ -324,7 +323,7 @@ def _element_constructor_(self, x): # If it's a tuple or a list, try and create a QuiverPath from it and # then return the associated basis element - if isinstance(x, (tuple, list, six.string_types)): + if isinstance(x, (tuple, list, str)): return self.element_class(self, {self._semigroup(x): self.base_ring().one()}) if isinstance(x, dict): diff --git a/src/sage/quivers/algebra_elements.pyx b/src/sage/quivers/algebra_elements.pyx index 25968539fcf..55d1f0986ab 100644 --- a/src/sage/quivers/algebra_elements.pyx +++ b/src/sage/quivers/algebra_elements.pyx @@ -18,7 +18,7 @@ AUTHORS: include "algebra_elements.pxi" from sage.misc.cachefunc import cached_method -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool @@ -1301,9 +1301,6 @@ cdef class PathAlgebraElement(RingElement): return sample._new_(homog_poly_scale((self).data, x)) raise TypeError("Don't know how to divide {} by {}".format(x, self)) - def __div__(self, x): - return self / x - ## Multiplication in the algebra cpdef _mul_(self, other): diff --git a/src/sage/quivers/path_semigroup.py b/src/sage/quivers/path_semigroup.py index 0f4c0f4b7c4..0ae0d75c22f 100644 --- a/src/sage/quivers/path_semigroup.py +++ b/src/sage/quivers/path_semigroup.py @@ -19,7 +19,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import print_function, absolute_import -from six import integer_types, string_types from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ @@ -173,7 +172,7 @@ def __init__(self, Q): # Check validity of input: vertices have to be labelled 1,2,3,... and # edge labels must be unique for v in Q: - if not isinstance(v, integer_types + (Integer,)): + if not isinstance(v, (Integer, int)): raise ValueError("vertices of the digraph must be labelled by integers") # Determine the category which this (partial) semigroup belongs to @@ -341,7 +340,7 @@ def _element_constructor_(self, data, check=True): elif data == 1: start = end = next(self._quiver.vertex_iterator()) path = [] - elif isinstance(data, string_types): # one edge + elif isinstance(data, str): # one edge i = L.get(data, None) if i is None: raise ValueError("data={!r} is not the label of an edge".format(data)) @@ -349,7 +348,7 @@ def _element_constructor_(self, data, check=True): path = [i] elif not isinstance(data, (tuple, list)): raise TypeError("data={} is not valid. A path must be initialized from either a tuple or a list".format(data)) - elif isinstance(data[0], string_types): # a list of labels + elif isinstance(data[0], str): # a list of labels start = L.get(data[0]) if start is None: raise ValueError("data[0]={!r} is not the label of an edge".format(data[0])) diff --git a/src/sage/repl/attach.py b/src/sage/repl/attach.py index c6cf1b95950..c0d5100d306 100644 --- a/src/sage/repl/attach.py +++ b/src/sage/repl/attach.py @@ -70,7 +70,6 @@ from __future__ import print_function import os -import six import time from IPython import get_ipython @@ -466,7 +465,7 @@ def detach(filename): ... ValueError: file '/dev/null/foobar.sage' is not attached, see attached_files() """ - if isinstance(filename, six.string_types): + if isinstance(filename, str): filelist = [filename] else: filelist = [str(x) for x in filename] @@ -597,12 +596,7 @@ def reload_attached_files_if_modified(): basename = os.path.basename(filename) timestr = time.strftime('%T', mtime) notice = '### reloading attached file {0} modified at {1} ###'.format(basename, timestr) - if ip and ip.pt_cli: - with ip.pt_cli.patch_stdout_context(raw=True): - print(notice) - code = load_wrap(filename, attach=True) - ip.run_cell(code) - elif ip: + if ip: print(notice) code = load_wrap(filename, attach=True) ip.run_cell(code) diff --git a/src/sage/repl/display/fancy_repr.py b/src/sage/repl/display/fancy_repr.py index c72ad3f5a68..9461a136f2a 100644 --- a/src/sage/repl/display/fancy_repr.py +++ b/src/sage/repl/display/fancy_repr.py @@ -13,12 +13,15 @@ #***************************************************************************** import types +from io import StringIO from IPython.lib.pretty import ( - _safe_getattr, _baseclass_reprs, + _safe_getattr, _type_pprinters, ) +_baseclass_reprs = (object.__repr__,) + from sage.repl.display.util import format_list @@ -90,7 +93,6 @@ def format_string(self, obj): 'Error: ObjectReprABC.__call__ is abstract' """ from sage.repl.display.pretty_print import SagePrettyPrinter - from six import StringIO stream = StringIO() p = SagePrettyPrinter(stream, 79, '\n') ok = self(obj, p, False) @@ -199,7 +201,10 @@ def __call__(self, obj, p, cycle): if not p.toplevel(): # Do not print the help for matrices inside containers return False - from sage.matrix.matrix1 import Matrix + try: + from sage.matrix.matrix1 import Matrix + except ModuleNotFoundError: + return False if not isinstance(obj, Matrix): return False from sage.matrix.matrix0 import max_rows, max_cols diff --git a/src/sage/repl/display/formatter.py b/src/sage/repl/display/formatter.py index 5eece441c6a..ba54f3dcb37 100644 --- a/src/sage/repl/display/formatter.py +++ b/src/sage/repl/display/formatter.py @@ -25,11 +25,11 @@ sage: shell = get_test_shell() sage: shell.run_cell('%display ascii_art') sage: shell.run_cell('integral(x^2/pi^x, x)') - -x / 2 2 \ - -pi *\x *log (pi) + 2*x*log(pi) + 2/ + -x / 2 2 \ + -pi *\x *log (pi) + 2*x*log(pi) + 2/ -------------------------------------- - 3 - log (pi) + 3 + log (pi) sage: shell.run_cell("i = var('i')") sage: shell.run_cell('sum(i*x^i, i, 0, 10)') 10 9 8 7 6 5 4 3 2 @@ -60,6 +60,8 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from io import StringIO + from IPython.core.formatters import DisplayFormatter, PlainTextFormatter from IPython.utils.py3compat import unicode_to_str @@ -173,8 +175,8 @@ def format(self, obj, include=None, exclude=None): sage: shell.run_cell('ipython_image') sage: shell.run_cell('get_ipython().display_formatter.format(ipython_image)') - ({u'image/png': ...'\x89PNG...', - u'text/plain': u''}, + ({'image/png': ..., + 'text/plain': ''}, {}) Test that IPython images still work even in latex output mode:: @@ -251,7 +253,7 @@ def __init__(self, *args, **kwds): super(SagePlainTextFormatter, self).__init__(*args, **kwds) def __call__(self, obj): - """ + r""" Compute the pretty representation of the object. Adapted from ``IPython.core.formatters.PlainTextPrettyPrint``. @@ -282,7 +284,6 @@ def __call__(self, obj): if DOCTEST_MODE: # Just to show that this is never executed in any other doctests in the Sage library print('---- calling ipython formatter ----') - from six import StringIO stream = StringIO() printer = SagePrettyPrinter( stream, self.max_width, unicode_to_str(self.newline)) diff --git a/src/sage/repl/display/jsmol_iframe.py b/src/sage/repl/display/jsmol_iframe.py index 82b897fc11c..d8de42f7bb7 100644 --- a/src/sage/repl/display/jsmol_iframe.py +++ b/src/sage/repl/display/jsmol_iframe.py @@ -47,6 +47,7 @@ - - - - - - -""" - -#***************************************************************************** -# Copyright (C) 2015 Volker Braun -# -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# https://www.gnu.org/licenses/ -#***************************************************************************** - -import io -import os -import stat -from sage.misc.cachefunc import cached_method -from sage.misc.html import html -from sage.misc.temporary_file import graphics_filename -from sage.repl.rich_output.backend_base import BackendBase -from sage.repl.rich_output.output_catalog import * -from sage.repl.rich_output.output_video import OutputVideoBase - - -def world_readable(filename): - """ - All SageNB temporary files must be world-readable. - - Discussion of this design choice can be found at :trac:`17743`. - - EXAMPLES:: - - sage: import os, stat - sage: f = tmp_filename() - - At least on a sane system the temporary files are only readable by - the user, but not by others in the group or total strangers:: - - sage: mode = os.stat(f).st_mode - sage: bool(mode & stat.S_IRUSR), bool(mode & stat.S_IRGRP), bool(mode & stat.S_IROTH) # random output - (True, False, False) - - This function disables that protection:: - - sage: from sage.repl.rich_output.backend_sagenb import world_readable - sage: world_readable(f) - sage: mode = os.stat(f).st_mode - sage: bool(mode & stat.S_IRUSR), bool(mode & stat.S_IRGRP), bool(mode & stat.S_IROTH) - (True, True, True) - """ - os.chmod(filename, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) - - -class SageNbOutputSceneJmol(OutputSceneJmol): - """ - Adapt Jmol rich output container for SageNB. - - For legacy reasons, SageNB expects Jmol files saved under strange - names. This class takes care of that. - - EXAMPLES:: - - sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol - sage: SageNbOutputSceneJmol.example() - SageNbOutputSceneJmol container - """ - - @cached_method - def sagenb_launch_script_filename(self): - """ - Return the launch script filename used by SageNB - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol - sage: j = SageNbOutputSceneJmol.example() - sage: j.sagenb_launch_script_filename() - 'sage0-size32.jmol' - """ - import PIL.Image - width, height = PIL.Image.open(io.BytesIO(self.preview_png.get())).size - ext = '-size{0}.jmol'.format(width) - return graphics_filename(ext=ext) - - @cached_method - def _base_filename(self): - """ - Return the common part of the file name - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol - sage: j = SageNbOutputSceneJmol.example() - sage: j._base_filename() - 'sage0-size32' - sage: j.sagenb_launch_script_filename().startswith(j._base_filename()) - True - sage: j.scene_zip_filename().startswith(j._base_filename()) - True - """ - dot_jmol = '.jmol' - filename = self.sagenb_launch_script_filename() - assert filename.endswith(dot_jmol) - return filename[:-len(dot_jmol)] - - @cached_method - def scene_zip_filename(self): - """ - Return the filename for the scene zip archive - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol - sage: j = SageNbOutputSceneJmol.example() - sage: j.scene_zip_filename() - 'sage0-size32-....jmol.zip' - """ - from random import randint - return '{0}-{1}.jmol.zip'.format( - self._base_filename(), - randint(0, 1 << 30) - ) - - @cached_method - def preview_filename(self): - """ - Return the filename for the png preview - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol - sage: j = SageNbOutputSceneJmol.example() - sage: j.preview_filename() - './.jmol_images/sage0-size32.jmol.png' - """ - directory, filename = os.path.split(self._base_filename()) - if not directory: - directory = '.' - return '{0}/.jmol_images/{1}.jmol.png'.format(directory, filename) - - def save_launch_script(self): - """ - Save the Jmol launch script - - See :meth:`sagenb_launch_script_filename`. - - EXAMPLES:: - - sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol - sage: j = SageNbOutputSceneJmol.example() - sage: os.path.exists('sage0-size32.jmol') - False - sage: j.save_launch_script() # py2 # optional -- sagenb - sage: os.path.exists('sage0-size32.jmol') # py2 # optional -- sagenb - True - """ - from sagenb.notebook.interact import SAGE_CELL_ID - path = 'cells/{0}/{1}'.format( - SAGE_CELL_ID, - self.scene_zip_filename()) - with open(self.sagenb_launch_script_filename(), 'w') as f: - f.write('set defaultdirectory "{0}"\n'.format(path)) - f.write('script SCRIPT\n') - world_readable(self.sagenb_launch_script_filename()) - - def save_preview(self): - """ - Save the preview PNG image - - See :meth:`preview_filename`. - - EXAMPLES:: - - sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol - sage: j = SageNbOutputSceneJmol.example() - sage: import shutil - sage: shutil.rmtree('.jmol_images', ignore_errors=True) - sage: j.save_preview() - sage: os.listdir('.jmol_images') # py2 # optional -- sagenb - ['sage1-size32.jmol.png'] - """ - from sage.misc.misc import sage_makedirs - sage_makedirs('.jmol_images') - self.preview_png.save_as(self.preview_filename()) - world_readable(self.preview_filename()) - - def embed(self): - """ - Save all files necessary to embed jmol - - EXAMPLES: - - Switch to a new empty temporary directory:: - - sage: os.chdir(tmp_dir()) - - sage: from sage.repl.rich_output.backend_sagenb import SageNbOutputSceneJmol - sage: j = SageNbOutputSceneJmol.example() - sage: j.embed() # py2 # optional -- sagenb - sage: sorted(os.listdir('.')) # py2 # optional -- sagenb - ['.jmol_images', 'sage0-size32-....jmol.zip', 'sage0-size32.jmol'] - sage: sorted(os.listdir('.jmol_images')) # py2 # optional -- sagenb - ['sage0-size32.jmol.png'] - """ - self.save_preview() - self.save_launch_script() - self.scene_zip.save_as(self.scene_zip_filename()) - world_readable(self.scene_zip_filename()) - - -IFRAME_TEMPLATE = \ -""" - -""" - - -class BackendSageNB(BackendBase): - - def _repr_(self): - """ - Return the string representation - - OUTPUT: - - String - - EXAMPLES:: - - sage: from sage.repl.rich_output.backend_sagenb import BackendSageNB - sage: backend = BackendSageNB() - sage: backend._repr_() - 'SageNB' - """ - return 'SageNB' - - def supported_output(self): - """ - Return the outputs that are supported by the SageNB backend. - - OUTPUT: - - Iterable of output container classes, that is, subclass of - :class:`~sage.repl.rich_output.output_basic.OutputBase`). - The order is ignored. - - EXAMPLES:: - - sage: from sage.repl.rich_output.backend_sagenb import BackendSageNB - sage: backend = BackendSageNB() - sage: supp = backend.supported_output(); supp # random output - set([, - ..., - ]) - sage: from sage.repl.rich_output.output_basic import OutputLatex - sage: OutputLatex in supp - True - """ - return set([ - OutputPlainText, OutputAsciiArt, OutputLatex, - OutputHtml, - OutputImagePng, OutputImageGif, OutputImageJpg, - OutputImagePdf, OutputImageSvg, - SageNbOutputSceneJmol, OutputSceneThreejs, - OutputSceneCanvas3d, - OutputVideoOgg, OutputVideoWebM, OutputVideoMp4, - ]) - - def display_immediately(self, plain_text, rich_output): - r""" - Show output without waiting for the prompt. - - INPUT: - - - ``plain_text`` -- instance of - :class:`~sage.repl.rich_output.output_basic.OutputPlainText`. The - plain text version of the output. - - - ``rich_output`` -- instance of an output container class - (subclass of - :class:`~sage.repl.rich_output.output_basic.OutputBase`). Guaranteed - to be one of the output containers returned from - :meth:`supported_output`, possibly the same as - ``plain_text``. - - OUTPUT: - - This method does not return anything. - - EXAMPLES:: - - sage: import sage.repl.rich_output.output_catalog as catalog - sage: plain_text = catalog.OutputPlainText.example() - sage: from sage.repl.rich_output.backend_sagenb import BackendSageNB - sage: backend = BackendSageNB() - sage: backend.display_immediately(plain_text, plain_text) - Example plain text output - sage: latex = catalog.OutputLatex.example() - sage: backend.display_immediately(plain_text, latex) - - """ - if isinstance(rich_output, (OutputPlainText, OutputAsciiArt)): - rich_output.print_to_stdout() - elif isinstance(rich_output, OutputLatex): - print(rich_output.mathjax()) - elif isinstance(rich_output, OutputHtml): - print(rich_output.with_html_tag()) - elif isinstance(rich_output, OutputImagePng): - self.embed_image(rich_output.png, '.png') - elif isinstance(rich_output, OutputImageGif): - self.embed_image(rich_output.gif, '.gif') - elif isinstance(rich_output, OutputImageJpg): - self.embed_image(rich_output.jpg, '.jpg') - elif isinstance(rich_output, OutputImagePdf): - self.embed_image(rich_output.pdf, '.pdf') - elif isinstance(rich_output, OutputImageSvg): - self.embed_image(rich_output.svg, '.svg') - elif isinstance(rich_output, OutputSceneJmol): - rich_output.embed() - elif isinstance(rich_output, OutputSceneThreejs): - filename = graphics_filename(ext='.html') - rich_output.html.save_as(filename) - world_readable(filename) - iframe = IFRAME_TEMPLATE.format( - html='cell://' + filename, - width=700, - height=400, - ) - from pretty_print import pretty_print - pretty_print(html(iframe)) - elif isinstance(rich_output, OutputSceneCanvas3d): - self.embed_image(rich_output.canvas3d, '.canvas3d') - elif isinstance(rich_output, OutputVideoBase): - self.embed_video(rich_output) - else: - raise TypeError('rich_output type not supported, got {}'.format(rich_output)) - - def embed_image(self, output_buffer, file_ext): - """ - Embed Image in the SageNB worksheet - - SageNB scans per-cell directories for image files, so all we - have to do here is to save the image at the right place. - - INPUT: - - - ``output_buffer`` -- - :class:`~sage.repl.rich_output.buffer.Buffer`. A buffer - holding the image data. - - - ``file_ext`` -- string. The file extension to use for saving - the image. - - OUTPUT: - - Nothing is returned. The image file is saved in the - appropriate place for SageNB. - - EXAMPLES:: - - sage: from sage.repl.rich_output import get_display_manager - sage: dm = get_display_manager() - sage: rich_output = dm.types.OutputImagePng.example() - sage: os.path.exists('sage0.png') - False - sage: dm._backend.embed_image(rich_output.png, '.png') - sage: os.path.exists('sage0.png') - True - """ - filename = graphics_filename(ext=file_ext) - output_buffer.save_as(filename) - world_readable(filename) - - def embed_video(self, video_output): - filename = graphics_filename(ext=video_output.ext) - video_output.video.save_as(filename) - world_readable(filename) - html(video_output.html_fragment( - url='cell://' + filename, - link_attrs='class="file_link"', - )) - - def threejs_offline_scripts(self): - """ - Three.js scripts for the Sage notebook - - OUTPUT: - - String containing script tags - - EXAMPLES:: - - sage: from sage.repl.rich_output.backend_sagenb import BackendSageNB - sage: backend = BackendSageNB() - sage: backend.threejs_offline_scripts() - '...